<?xml version="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:jpa="http://www.springframework.org/schema/data/jpa"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsdhttp://www.springframework.org/schema/data/jpahttps://www.springframework.org/schema/data/jpa/spring-jpa.xsd"><!--配置spring包扫描--><context:component-scanbase-package="com"/><!--对Spring和SpringDataJPA进⾏配置--><!--创建数据库连接池druid--><!--引⼊外部资源⽂件--><context:property-placeholderlocation="classpath:jdbc.properties"/><!--第三⽅jar中的bean定义在xml中--><beanid="dataSource"class="com.alibaba.druid.pool.DruidDataSource"><propertyname="driverClassName"value="${jdbc.driver}"/><propertyname="url"value="${jdbc.url}"/><propertyname="username"value="${jdbc.username}"/><propertyname="password"value="${jdbc.password}"/></bean><!--配置⼀个JPA中⾮常重要的对象,entityManagerFactory,entityManager类似于mybatis中的SqlSessionentityManagerFactory类似于Mybatis中的SqlSessionFactory当然,这里的配置有点像对应的mybatis与spring整合的处理,即:<bean id="sessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">--><beanid="entityManagerFactory"class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"><!--配置⼀些细节--><!--配置数据源--><propertyname="dataSource"ref="dataSource"/><!--配置包扫描(pojo实体类所在的包,名称处理)--><propertyname="packagesToScan"value="com.pojo"/><!--指定jpa的具体实现,也就是hibernate,如果你不显式地指定persistenceProvider,Spring会尝试根据类路径上的依赖自动检测一个合适的JPA提供者(如果你已经在项目中引入了Hibernate的相关依赖,Spring会自动选择Hibernate作为JPA提供者),如果没有,那么可能会出现异常
这里是需要一个实现的,前面说明了,可以搜索"需要一个具体的实现来执行这些操作"注意了:hibernate自身就有jpa规范,所以可以单独的使用hibernate,而不用进行整合,只不过Spring Data JPA是对JPA的一层抽象和增强,提供了一些方便的功能和工具,所以这里整合一下
换言之,这里的配置相当于替换了hibernate自身的jpa规范,也或者是对应的spring data jpa使用hibernate的实现--><propertyname="persistenceProvider"><beanclass="org.hibernate.jpa.HibernatePersistenceProvider"></bean></property><!--jpa方言配置,不同的jpa实现对于类似于beginTransaction等细节实现起来是不⼀样的,所以传⼊JpaDialect具体的实现类,这里指定的是 HibernateJpaDialect,表示使用Hibernate的方言实现
问题:既然上面给了提供者,为什么还需要方言,难道不会自动给出吗,答:通常会自动的处理,只是我们建议手动的指定,防止不会出现错误(如不小心设置了其他方言)
--><propertyname="jpaDialect"><beanclass="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean></property><!--配置具体provider,hibearnte框架的执⾏细节--><propertyname="jpaVendorAdapter"><beanclass="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"><!--定义hibernate框架的⼀些细节--><!--配置数据表是否⾃动创建,因为我们会建⽴pojo和数据表之间的映射关系程序启动时,如果数据表还没有创建,是否要程序给创建⼀下--><propertyname="generateDdl"value="false"/><!--指定数据库的类型hibernate本身是个dao层框架,可以⽀持多种数据库类型的,这⾥就指定本次使⽤的什么数据库--><propertyname="database"value="MYSQL"/><!--配置数据库的方言hibernate可以帮助我们拼装sql语句,但是不同的数据库sql语法是不同的,所以需要我们注⼊具体的数据库⽅⾔,这个可以不写,因为上面指定了数据库类型,会自动考虑对应数据库的方言当然,这里可以进行覆盖,还有其实,他们两个可以独立的写的
但是我们还是建议手动的指定,防止不会出现错误(如不小心设置了其他方言)--><propertyname="databasePlatform"value="org.hibernate.dialect.MySQLDialect"/><!--是否显示sql操作数据库时,是否打印sql
删除了,那么对应操作代码时,可能没有sql语句看了哦--><propertyname="showSql"value="true"/></bean></property></bean><!--引⽤上⾯创建的entityManagerFactory<jpa:repositories> 配置jpa的dao层细节base-package:指定dao层接⼝所在包entity-manager-factory-ref指定一个,如果只存在一个可以忽略,存在多个,自动选择一个(一般是先配置的)transaction-manager-ref指定一个,如果存在多个,通常必须指定,否则可能出现异常--><jpa:repositoriesbase-package="com.dao"entity-manager-factory-ref="entityManagerFactory"transaction-manager-ref="transactionManager"/><!--事务管理器配置jdbcTemplate/mybatis 使⽤的是DataSourceTransactionManagerjpa规范:JpaTransactionManager--><beanid="transactionManager"class="org.springframework.orm.jpa.JpaTransactionManager"><propertyname="entityManagerFactory"ref="entityManagerFactory"/></bean><!--声明式事务配置,通常需要操作@Transactional注解时需要,相当于扫描对应的注解使得起作用--><tx:annotation-driven/></beans><!--方言可以是语法(mysql),也可以是内部操作方式(jpa方言配置)-->
至此,对应的spring扫描,以及数据源配置,以及jpa工厂配置(整合了hibernate)
我们在测试资源文件夹下创建com.test包,然后创建Test1类:
packagecom.test;importcom.dao.ResumeDao;importcom.pojo.Resume;importorg.junit.Test;importorg.junit.runner.RunWith;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.test.context.ContextConfiguration;importorg.springframework.test.context.junit4.SpringJUnit4ClassRunner;importjava.util.Optional;@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations ={"classpath:spring.xml"})publicclassTest1{// 要测试IOC哪个对象注⼊即可@AutowiredprivateResumeDao resumeDao;/*** dao层接⼝调⽤,分成两块:* 1、基础的增删改查* 2、专⻔针对查询的详细分析使⽤*/@TestpublicvoidtestFindById(){// 早期的版本 dao.findOne(id); 可能现在没有了Optional<Resume> optional = resumeDao.findById(1l);Resume resume = optional.get();System.out.println(resume);/*打印的结果(也包括sql语句):Hibernate: select resume0_.id as id1_0_0_, resume0_.address as address2_0_0_, resume0_.name as name3_0_0_, resume0_.phone as phone4_0_0_ from tb_resume resume0_ where resume0_.id=?Resume{id=1, name='张三', address='北京', phone='131000000'}很明显:对应的接口由于有对应的继承的泛型,那么会找到对应的类,然后通过类的信息进行处理sql语句是:类名称0_.类变量 as 类变量递增数_0_0类别名是:类名称0_还有:@Table(name = "tb_resume")和@Column(name = "id")中id是作为sql中变量存在的,如果是iD,那么就是resume0_.iD,而id和iD在mysql中是没有影响的(表字段忽略大小写)但是呢,他们是直接的指定,在mp中,是存在不指定,使用类来操作的,我们可以测试,发现他没有像mp一样的驼峰大小写,也就是说nAme就是nAme,不指定也算的(当然了,这些小细节忽略即可,没有必要的)*/}}
直接进行访问,若出现了数据,代表操作成功,至此我们搭建好了环境的处理
我们继续学习:
继续在该测试类中补充如下:
@TestpublicvoidtestFindOne(){Resume resume =newResume();resume.setId(1l);Example<Resume> of =Example.of(resume);Optional<Resume> one = resumeDao.findOne(of);//操作条件System.out.println(one.get());//get得到对应的Resume//当然了,对应get里面如果没有数据,会出现异常:/*public T get() {if (value == null) {throw new NoSuchElementException("No value present");}return value;}*///Optional在31章博客有点说明}
执行看看结果,然后我们继续补充:
@TestpublicvoidtestSave(){Resume resume =newResume();resume.setId(1l);resume.setName("让人人");//其他没有写的,按照默认则是null//新增和更新都是使用Save方法的,通过是否有主键来区分(类设置了对应的注解的)Resume save = resumeDao.save(resume);//更新resume.setName("他人");System.out.println(save);//返回更新的数据,所以对上面的resume修改不会影响这个返回resume.setId(null);Resume save1 = resumeDao.save(resume);//新增resume.setName("和");System.out.println(save1);//返回要新增的数据,也就是上面的resume,所以上面的resume修改会影响这个返回//注意,在更新之前,或者更新方法执行后,会进行一次查询操作,来保证更新的数据存在//还有,如果查询的数据,与要更新的数据一样,是不会进行更新的,否则进行更新//上面的返回中,更新是创建对象得到对应的参数数据,然后返回,而新增则不是//因为考虑更新中有个查询(他只根据id查的),用这个新实例得到参数数据(如果一样的,那么不更新,如果不一样,就得到参数数据),然后进行更新//这里是可以返回对应的实例的,在mp中,通常只会考虑是否成功}
@TestpublicvoidtestFindAll(){List<Resume> all = resumeDao.findAll();//查询所有for(Resume resume : all){System.out.println(resume);}}@TestpublicvoidtestSort(){//设置id进行降序Sort sort =newSort(Sort.Direction.DESC,"id");//id是需要对应数据库的字段,他是在后面补充的,也可以说成是对应与注解,因为最终都是对应与数据库List<Resume> list = resumeDao.findAll(sort);for(int i =0; i < list.size(); i++){Resume resume = list.get(i);System.out.println(resume);}}
上面基本都是使用继承的接口的方法,我们也可以自定义一个:
回到前面的接口ResumeDao,在里面加上如下:
//可以引⼊jpql(jpa查询语⾔)语句进⾏查询,jpql 语句类似于sql,只不过sql操作的是数据表和字段,jpql操作的是对象和属性//⽐如 from Resume where id=xx(在hibernate中称为hql语句)@Query("from Resume where id =?1")//指定?,代表对应的给入的值,后面的1代表接口参数中的第一个//即?1代表使用Long idpublicList<Resume>findByJpql(Long id);
我们回到测试类,然后加上如下:
@TestpublicvoidtestJpql(){List<Resume> byJpql = resumeDao.findByJpql(6l);for(Resume s : byJpql){System.out.println(s);}/*Hibernate:selectresume0_.id as id1_0_,resume0_.address as address2_0_,resume0_.name as name3_0_,resume0_.phone as phone4_0_from tb_resume resume0_where resume0_.id=?
Resume{id=6, name='让人', address='null', phone='null'}对比一下这个:@Query("from Resume where id =?1")
很明显,这个注解是进行解析的,from Resume代表从这个类操作,这个类通常代表所在接口类对应继承的泛型来确定
后面的则是这个类对应的id的条件,和位置还有,对应的接口可以不加返回值,只不过你不加的话,在扫描到这个注解时(对于dao层来说,对应的即有扫描,也有对应的直接读取,可以参考mybatis中对其dao的扫描操作(可以看看是否有注解),也可以参考106章博客)
进行执行时,自然也不会有返回值(反射),一般来说,如果考虑自动匹配一些返回值列表,通常需要代理(当然,代理的底层肯定是C完成),然后自行设置返回值至此我们解释完毕*/}
我们还可以继续补充参数:
@Query("from Resume where id =?1 and name = ?2")publicList<Resume>findByJpql(Long id,String name);//对应的Long id的id名称是可以随便写的
修改一下:
@TestpublicvoidtestJpql(){List<Resume> byJpql = resumeDao.findByJpql(6l,"让人");for(Resume s : byJpql){System.out.println(s);}}
进行测试吧,我们还可以操作原生的sql,我们在ResumeDao接口里面继续添加:
//默认情况下nativeQuery = true为false,代表操作jpql,设置为true,代表操作原生的//如果操作原生的,自然需要完整的sql,否则自然报错(sql报错,导致我们报错)@Query(value ="select * from tb_resume where id =?1 and name = ?2",nativeQuery =true)publicList<Resume>findBySql(Long id,String name);
继续测试:
@TestpublicvoidtestSql(){List<Resume> sql = resumeDao.findBySql(6l,"让人");for(Resume s : sql){System.out.println(s);}}
/*方法命名规则查询*/publicList<Resume>findByNameLike(String name);/*
select
resume0_.id as id1_0_,
resume0_.address as address2_0_,
resume0_.name as name3_0_,
resume0_.phone as phone4_0_
from tb_resume resume0_
where resume0_.name like ? escape ?
单独参数且默认情况下(具体为什么添加,可能还有其他原因,这里了解即可),如果escape什么都不操作,可以认为是空值,如''(上面只是打印sql,具体后面的拼接在底层代码里面,所以并不是全部需要看我们的参数的,有些可能有自动的处理的)*/publicList<Resume>findByName(String name);/*select resume0_.id as id1_0_, resume0_.address as address2_0_, resume0_.name as name3_0_, resume0_.phone as phone4_0_ from tb_resume resume0_ where resume0_.name=?*///我们可以知道,对应的需要findBy开头,然后Name写上(首字母通常需要大写,否则报错,我测试了name,好像并不需要,但是还是建议大写,可能看版本),来确定操作谁,如果后面没有写具体的操作,如Like(首字母通常需要大写,否则报错),那么默认是等于的
@TestpublicvoidtestSpecification(){//动态条件封装Specification<Resume> objectSpecification =newSpecification(){//root:需要查询的对象属性//criteriaBuilder:构建查询条件@OverridepublicPredicatetoPredicate(Root root,CriteriaQuery criteriaQuery,CriteriaBuilder criteriaBuilder){//获取到name属性(会根据泛型找到对应类的)Path name = root.get("name");//构建条件Predicate predicate = criteriaBuilder.equal(name,"他");return predicate;}};/*select resume0_.id as id1_0_, resume0_.address as address2_0_, resume0_.name as name3_0_, resume0_.phone as phone4_0_ from tb_resume resume0_ where resume0_.name=?*///findOne只能让你查询一个,如果有多条数据,那么会报错Optional<Resume> one = resumeDao.findOne(objectSpecification);Resume resume = one.get();//没有找到会报错的System.out.println(resume);}
继续测试一下:
@TestpublicvoidtestSpecificationTest(){//动态条件封装Specification<Resume> objectSpecification =newSpecification(){//root:需要查询的对象属性//criteriaBuilder:构建查询条件@OverridepublicPredicatetoPredicate(Root root,CriteriaQuery criteriaQuery,CriteriaBuilder criteriaBuilder){//获取到name属性(会根据泛型找到对应类的)Path name = root.get("name");Path address = root.get("address");//构建条件Predicate predicate = criteriaBuilder.equal(name,"让人");Predicate predicate1 = criteriaBuilder.like(address.as(String.class),"分%");//告诉他,拼接的时候,是字符串//简单来说,如果address对应的类型不是String,那么转化成String,否则相当于不变(或者不写)//组合Predicate and = criteriaBuilder.and(predicate, predicate1);//有顺序的,predicate的在前面return and;}};/*
select
resume0_.id as id1_0_,
resume0_.address as address2_0_,
resume0_.name as name3_0_,
resume0_.phone as phone4_0_
from tb_resume resume0_
where resume0_.name=? and (resume0_.address like ?)*/Optional<Resume> one = resumeDao.findOne(objectSpecification);Resume resume = one.get();//没有找到会报错的System.out.println(resume);}
@TestpublicvoidtestPage(){/*** 第⼀个参数:当前查询的⻚数,可以从0开始,而不是1,从1开始代表是第二页* 由于是页,所以从0开始,默认为limit 0,2(也可以是limit 2,默认起始为0),那么第一页就是limit 2,2* 这里是页,而不是起始位置* 第⼆个参数:每⻚查询的数量*/Pageable pageable =PageRequest.of(1,2);//最终查询两个数量Page<Resume> all = resumeDao.findAll(pageable);System.out.println(all);//Page 2 of 5 containing com.pojo.Resume instancesList<Resume> collect = all.get().collect(Collectors.toList());for(Resume resume : collect){System.out.println(resume);}/*执行了两次,一个是分页的,一个是所有的数据selectresume0_.id as id1_0_,resume0_.address as address2_0_,resume0_.name as name3_0_,resume0_.phone as phone4_0_from tb_resume resume0_ limit ?, ?select count(resume0_.id) as col_0_0_ from tb_resume resume0_*///上面Page<Resume> all = resumeDao.findAll(pageable);操作后,就会有上面两个sql的打印System.out.println(all.get().count());//当前页的条数System.out.println(all.getTotalElements());//总记录,select count(resume0_.id) as col_0_0_ from tb_resume resume0_}
Spring Data JPA 执行过程源码分析:
Spring Data Jpa 源码很少有人去分析,因为Spring Data Jpa 地位没有之前学习的框架高,习惯把它当成⼀个工具来用,并且接口的实现对象肯定是通过动态代理来完成的(也就是增强),且代理对象的产生过程追源码很难追,所以少有人分析
//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//packageorg.springframework.beans.factory;importorg.springframework.lang.Nullable;publicinterfaceFactoryBean<T>{@NullableTgetObject()throwsException;@NullableClass<?>getObjectType();defaultbooleanisSingleton(){returntrue;}}
publicBeanDefinitiongetBeanDefinition(String beanName)throwsNoSuchBeanDefinitionException{//也就是这个地方:beanDefinitionMap是存放bean信息的工厂,从里面拿取对应的bean信息(比如名称和全限定名称)BeanDefinition bd =(BeanDefinition)this.beanDefinitionMap.get(beanName);if(bd ==null){if(this.logger.isTraceEnabled()){this.logger.trace("No bean named '"+ beanName +"' found in "+this);}thrownewNoSuchBeanDefinitionException(beanName);}else{return bd;}}
我们找他put方法:
//就在当前类里面可以找到publicvoidregisterBeanDefinition(String beanName,BeanDefinition beanDefinition)throwsBeanDefinitionStoreException{//..Assert.hasText(beanName,"Bean name must not be empty");Assert.notNull(beanDefinition,"BeanDefinition must not be null");if(beanDefinition instanceofAbstractBeanDefinition){try{((AbstractBeanDefinition)beanDefinition).validate();}catch(BeanDefinitionValidationException var8){thrownewBeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,"Validation of bean definition failed", var8);}}//.. }
在上面的 Assert.notNull(beanDefinition, “BeanDefinition must not be null”);打上断点并加上条件(有beanName),看调用栈:
publicObjectgetProxy(@NullableClassLoader classLoader){returnthis.createAopProxy().getProxy(classLoader);}protectedfinalsynchronizedAopProxycreateAopProxy(){if(!this.active){this.activate();}//进入returnthis.getAopProxyFactory().createAopProxy(this);}publicAopProxycreateAopProxy(AdvisedSupport config)throwsAopConfigException{if(!config.isOptimize()&&!config.isProxyTargetClass()&&!this.hasNoUserSuppliedProxyInterfaces(config)){returnnewJdkDynamicAopProxy(config);}else{Class<?> targetClass = config.getTargetClass();if(targetClass ==null){thrownewAopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");}else{//看这里,可以发现对应是操作代理的return(AopProxy)(!targetClass.isInterface()&&!Proxy.isProxyClass(targetClass)?newObjenesisCglibAopProxy(config):newJdkDynamicAopProxy(config));}}}
return(Repository)this.repository.get();publicTget(){T value =this.getNullable();if(value ==null){thrownewIllegalStateException("Expected lazy evaluation to yield a non-null value but got null!");}else{return value;}}@NullableprivateTgetNullable(){T value =this.value;if(this.resolved){return value;}else{value =this.supplier.get();this.value = value;this.resolved =true;return value;}}
这是篇NIPS2023的 world model 论文文章提出,WM的误差会在训练过程中积累从而影响policy的训练,向WM中加噪声可以改善这一点。其他的流程和IRIS差不多,差别在以下几点: image encoder,IRIS用的VQVAE, 本文用的是VAE&am…