JpaRepository如何实现增删改查并进行单元测试
作者:yeungsinsin
JpaRepository增删改查进行单元测试
项目结构
创建UserInfoDaoI.java文件,继承JpaRepository。(不需要实现类)
根据条件查询/删除
更新
单元测试
SpringDataJPA的Repository理解
repository抽取扩展理解
在SpringDataJPA中使用repository来进行数据层操作(作用相当于dao层),直接使用repository对象调用已经实现好的数据层操作方法进行CRUD、分页、排序等操作,还可以在自定义repository接口中依据一定规则扩展功能。在一个项目中,我们平常需要为每一个实体类都创建一个repository接口,我们自定义的repository接口都需要去继承JpaRepository接口,以具有所有的数据层操作功能,但也仅仅限于简单查询等操作。通常我们为了能实现复杂查询操作,会继续继承JpaSpecificationExecutor接口,简单说就是建立一个规则来进行查询。
这样虽然在很大程度上简化了开发代码难度,但在为每一实体类创建对应repository接口时,发现存在大量的重复代码,这时很容易的我们会想到抽取父类来简化一些公共代码并顺带可以对子类进行一些规范。虽然SpringDataJPA提供的功能已经足够强大了,但是依然还是不能满足实际开发中的所有需求,所以我们想在抽取父类的同时实现对其功能的扩展。在进行这个操作之前我们需要了解一下整个repository的结构。
应该免不了有疑问,我们仅仅是定义了一个接口去实现了JpaRepository接口,怎么就能创建对象了,而且还能给我们实现一系列的数据层操作?这就要从为什么自定义repository接口要去继承JpaRepository接口说起了:
通过它的结构图可以看出,JpaRepository的父类分别是PagingAndSortingRepository、CrudRepository和Repository,前两个接口中前者定义了分页和排序功能,后者看名字就可以知道里面全是CRUD操作,值得注意的是,它这保存和修改方法都是save。Repository接口呢没有定义任何功能,它的作用就是标识。这么说吧我们自定义的接口继承JpaRepository接口就相当于继承了Repository接口,那么SpringDataJPA在扫描时只要扫描到我们某个接口实现了Repository接口,就为自动的为其创建代理实现子类(通过AOP实现)。但是为什么就只为我们自定义的repository接口创建了实现子类而没有为PagingAndSortingRepository、CrudRepository、JpaRepository创建呢?这个同样能从结构图中看出答案:它们都是打上了NoRepositoryBean注解的,凡是打上了这个注解的接口SPringDataJPA都不会为其创建实现子类。
这样一来,我们自定义的接口继承了以上三个接口后就相当于“集百家之长”了,拥有了它们所有功能,但是问题又来了,它们再牛终归还是接口啊,又不能创建对象,是怎么操作的呢?
原来,SpringDataJPA自动的为自动创建的repository实现子类继承了SimpleJpaRepository类,而SimpleJpaRepository又是SpringDataJPA的Repository默认实现两大方式之一,自然而然的自动创建的repository代理子类也就具有了所有的功能。至此,SpringDataJPA中repository实现数据层操作的原理也大概讲清楚了,接下来就说说扩展了:虽然SPringDataJPA提供的功能已经足够强大了,但是依然还是不能满足实际开发中的所有需求。所以说了这么久终于要说到重点了:抽取父类、扩展功能。
通过上图,大概也能看出结构了,大概思想:在原本应该继承JpaRepository的实体类repository接口与JpaRepository之间增加了一层而已,让BaseRepository继承JpaSpecificationExecutor、JpaRepository,实体类repository接口继承BaseRepository接口;这样便能实现在拥有原有SpringDataJPA的Repository功能的情况下在BaseRepository扩展其他功能,但在这需要注意:在SpringDataJPA中默认会使自动创建的repository代理实现子类(例:图中的EmployeeRepositoryImpl)去继承SimpleJpaRepository,很明显我们并不能使用默认的,因为这样会使得我们的BaseRepository接口定义的一无是处,所以我们需要再创建一个自定义的BaseRepository实现BaseRepositoryImpl继承SimpleJpaRepository,再修改配置使得SpringDataJPA自动创建出的所有实体类repository的实现代理子类都去继承我们的BaseRepositoryImpl,这样我们便能彻底实现扩展了。
前面说过SpringDataJPA会默认的将自动创建出的实现代理子类继承SimpleJpaRepository类,所以我们需要修改SimpleJpaRepository为我们自定义的BaseRepositoryImpl类,SpringDataJPA是通过JpaRepositoryFactoryBean来实现创建并继承过程的。我们只用自定义一个BaseRepositoryFactoryBean来继承JpaRepositoryFactoryBean,接下来只用修改最终返回功能对象,和确定功能对象的类型即可。最后记得要在spring的配置文件中在SpringDataJPA的配置中添加修改创建对象的factoryBean为我们自定义的BaseRepositoryFactoryBean。
最后,记得修改各实体repository接口继承我们定义的BaseRepository接口
接下来贴一贴代码
接下来贴一贴代码
BaseRepository
import com.xer.aisell.query.BaseQuery; import org.springframework.data.domain.Page; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.repository.NoRepositoryBean; import java.io.Serializable; import java.util.List; /** * 虽然jpadata提供的功能已经足够强大了,但是依然还是不能满足实际开发中的所有需求 * 所以希望在具有它功能的前提下再拓展一些功能 * 之前是通过每个实体类的repository接口来直接继承JpaRepository接口,现在我们可以在中间添加一个父类接口BaseRepository * BaseRepository继承JpaRepository,再由实体类repository来继承BaseRepository * 这样就能够实现既实现了功能,又能随时扩展功能 * @param <T> * @param <ID> */ @NoRepositoryBean public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>{ //根据Query拿到分页对象(分页) Page findPageByQuery(BaseQuery baseQuery); //根据Query拿到对应的所有数据(不分页) List<T> findByQuery(BaseQuery baseQuery); //根据jpql与对应的参数拿到数据 List findByJpql(String jpql,Object... values); }
BaseRepositoryImpl
import com.xer.aisell.query.BaseQuery; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import javax.persistence.EntityManager; import javax.persistence.Query; import java.io.Serializable; import java.util.List; public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements BaseRepository<T,ID> { private final EntityManager entityManager; public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) { super(domainClass, em); this.entityManager = em; } @Override /** * 分页 */ public Page findPageByQuery(BaseQuery baseQuery) { //拿到条件 Specification spec = baseQuery.createSpec(); //创建排序 Sort sort = baseQuery.getSort(); //分页 Pageable page = new PageRequest(baseQuery.getJPACurrentPage(), baseQuery.getPageSize(), sort); return super.findAll(spec,page); } /** * 不分页查询 * @param baseQuery * @return */ @Override public List findByQuery(BaseQuery baseQuery) { //获取到条件 Specification spec = baseQuery.createSpec(); //排序 Sort sort = baseQuery.getSort(); return super.findAll(spec,sort); } /** * * 根据传入JPQL查询 * @param jpql * @param values * @return */ @Override public List findByJpql(String jpql, Object... values) { Query query = entityManager.createQuery(jpql); //为传入JPQL填充条件值 if (values != null) { for (int i = 0;i < values.length;i++) { query.setParameter(i+1,values[i]); } } return query.getResultList(); } }
EmployeeRepository接口
import com.xer.aisell.domain.Employee; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import java.util.List; /*** *用于数据库操作 */ public interface EmployeeRepository extends BaseRepository<Employee,Long> { /** * 可以自己扩展 */ List<Employee> findByUsernameLike(String username); /** * 使用@Query * 实现自己写JPQL查询 */ @Query("select o from Employee o where username like ?1") List<Employee> findByUsername(String username); /** * 当然也可以实现自己写SQL */ @Query(nativeQuery = true,value = "SELECT COUNT(*) FROM employee") Long getCount(); }
BaseRepositoryFactoryBean类(难)
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import javax.persistence.EntityManager; import java.io.Serializable; public class BaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T,S,ID> { @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { return new MyRepositoryFactory<T,ID>(entityManager); //注:这里创建是我们的自定义类 } //继承JpaRepositoryFactory后,把返回的对象修改成我们自己的实现 private static class MyRepositoryFactory<T,ID extends Serializable> extends JpaRepositoryFactory { private final EntityManager entityManager; /** * Creates a new {@link JpaRepositoryFactory}. * * @param entityManager must not be {@literal null} */ public MyRepositoryFactory(EntityManager entityManager) { super(entityManager); this.entityManager = entityManager; } //这里返回最后的功能对象 @Override protected Object getTargetRepository(RepositoryInformation information) { return new BaseRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager); } //确定功能对象的类型 @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { return BaseRepositoryImpl.class; } } }
spring配置文件中关于SpringDataJPA的配置
<jpa:repositories base-package="com.xer.aisell.dao" entity-manager-factory-ref="entityManagerFactory" transaction-manager-ref="transactionManager" factory-class="com.xer.aisell.dao.BaseRepositoryFactoryBean"/>
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。