Spring Data JPA通过提供基于JPA的Repository极大地减少JPA作为数据访问方案的代码量。
1.定义数据访问层
使用Spring Data JPA建立数据访问层十分简单,只需定义一个继承JpaRepository的接口即可,接口如下:
1 @RepositoryRestResource(path = "people") 2 public interface PersonRepository extends JpaRepository<Person, Long> { 3 4 @RestResource(path = "nameStartsWith", rel = "nameStartsWith") 5 Person findByNameStartsWith(@Param("name")String name); 6 7 }
继承JpaRepository接口意味着我们默认已经有了下面的数据访问操作方法:
1 @NoRepositoryBean 2 public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> { 3 List<T> findAll(); 4 5 List<T> findAll(Sort var1); 6 7 List<T> findAll(Iterable<ID> var1); 8 9 <S extends T> List<S> save(Iterable<S> var1); 10 11 void flush(); 12 13 <S extends T> S saveAndFlush(S var1); 14 15 void deleteInBatch(Iterable<T> var1); 16 17 void deleteAllInBatch(); 18 19 T getOne(ID var1); 20 21 <S extends T> List<S> findAll(Example<S> var1); 22 23 <S extends T> List<S> findAll(Example<S> var1, Sort var2); 24 }
2.配置使用Spring Data JPA
在Spring环境中,使用Spring Data JPA可通过@EnableJpaRepositories注解来开启Spring Data JPA的支持,@EnableJpaRepositories接收的value参数用来扫描数据访问层所在包下的数据访问的接口定义。
1 @Configuration 2 @EnableJpaRepositories("com.test.dao") 3 public class JpaConfiguration { 4 @Bean 5 public EntityManagerFactory entityManagerFactory(){ 6 //... 7 return null; 8 } 9 10 //还需配置DataSource、PlatformTransactionManager等相关必须bean 11 }
3.定义查询方法
(1)根据属性名查询
1)常规查询。根据属性名来定义查询方法
1 public interface PersonRepository extends CustomRepository<Person, Long> { 2 3 /** 4 * 通过名字相等查询,参数name 5 * 相当于JPQL:select p from Person p where p.name=? 6 */ 7 List<Person> findByName(String name); 8 9 /** 10 * 通过名字like查询,参数为name 11 * 相当于JPQL:select p from Person p where p.name like ? 12 */ 13 List<Person> findByNameLike(String name); 14 15 /** 16 * 通过名字和地址查询,参数为name和address 17 * 相当于JPQL:select p from Person p where p.name = ? and p.address = ? 18 */ 19 List<Pserson> findByNameAndAddress(String name,String address); 20 }
从代码可以看出,这里使用了findBy、like、And这样的关键字。其中findBy可以用find、read、readBy、query、queryBy、get、getBy来代替。
2)限制结果数量。结果数量是用top和first关键字来实现的:\
1 public interface PersonRepository extends CustomRepository<Person, Long> { 2 3 /** 4 * 获取查询条件的前10条数据 5 * 通过名字相等查询,参数name 6 * 相当于JPQL:select p from Person p where p.name=? 7 */ 8 List<Person> findFirst10ByName(String name); 9 10 /** 11 * 获取查询条件的前10条数据 12 * 通过名字like查询,参数为name 13 * 相当于JPQL:select p from Person p where p.name like ? 14 */ 15 List<Person> findTop10ByNameLike(String name); 16 17 }
(2)使用JPA的NamedQuery查询
Spring Data JPA支持用JPA的NameQuery来定义查询方法,即一个名称映射一个查询语句。
1 @Entity 2 @NamedQuery(name = "Person.withNameAndAddressNamedQuery", 3 query = "select p from Person p where p.name=? and address=?") 4 public class Person { 5 @Id 6 @GeneratedValue 7 private Long id; 8 9 private String name; 10 11 private Integer age; 12 13 private String address; 14 15 16 17 public Person() { 18 super(); 19 } 20 21 public Long getId() { 22 return id; 23 } 24 public void setId(Long id) { 25 this.id = id; 26 } 27 public String getName() { 28 return name; 29 } 30 public void setName(String name) { 31 this.name = name; 32 } 33 public Integer getAge() { 34 return age; 35 } 36 public void setAge(Integer age) { 37 this.age = age; 38 } 39 public String getAddress() { 40 return address; 41 } 42 public void setAddress(String address) { 43 this.address = address; 44 } 45 46 47 }
这时在接口里使用 Person withNameAndAddressNamedQuery(String name,String address); 时是使用的在上面定义的sql语句,而不是根据方法名称查询
(3)使用@Query查询
1)使用命名参数,使用名称来匹配查询参数。Spring Data JPA还支持@Query注解在接口的方法上实现查询
@Query("select p from Person p where p.name= :name and p.address= :address") Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
2)使用参数索引
@Query("select p from Person p where p.name= ? and p.address= ?") Person withNameAndAddressNamedQuery(String name,String address);
3)更新查询。Spring Data JPA支持@Modifying和@Query注解组合事件来更新查询
4)JPA提供了基于准则查询方式,即Criteria查询。而Spring Data JPA提供了一个Specification(规范)接口让我们可以更方便的构造准则查询,Specification接口定义了一个toPredicate方法用来构造查询条件。
4.自定义Repository的实现
spring data提供了CrudRepository和PagingAndSortingRepository,spring data JPA也提供了JpaRepository。如果我们想把自己常用的数据库操作封装起来,像JpaRepository一样提供给我们领域类的Repository接口使用,应该怎么做?
1)定义自定义Repository接口
@NoRepositoryBean public interface CustomRepository<T, ID extends Serializable>extends JpaRepository<T, ID> ,JpaSpecificationExecutor<T>{ Page<T> findByAuto(T example,Pageable pageable); }1. @NoRepositoryBean指明当前这个接口不是我们领域类的接口(例如PersonRepository)2. 我们自定义的Repository实现JpaRepository接口(这里也可以实现PagingAndSortingRepository接口,看具体需求),具备JpaRepository的能力3. 要定义的数据操作方法在接口中的定义
2)定义接口实现
public class CustomRepositoryImpl <T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T,ID> { private final EntityManager entityManager; public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) { super(domainClass, entityManager); this.entityManager = entityManager; } @Override public Page<T> findByAuto(T example, Pageable pageable) { return findAll(byAuto(entityManager, example),pageable); //在此处定义数据访问操作
} } 1. 首先要实现CustomRepository接口,继承SimpleJpaRepository类让我们可以使用其提供的方法(例如:findAll)2. 让数据库操作方法中可以使用entityManager3. CustomRepositoryImpl的构造函数,需当前处理的领域类类型和entityManager作为构造函数
3)自定义RepositoryFactoryBean。自定义JpaRepositoryFactoryBean替代默认RepositoryFactoryBean,我们会获得一个RepositoryFactory,RepositoryFactory将会注册我们自定义的Repository的实现
public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T, S, ID> {// 1 @Override protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {// 2 return new CustomRepositoryFactory(entityManager); } private static class CustomRepositoryFactory extends JpaRepositoryFactory {// 3 public CustomRepositoryFactory(EntityManager entityManager) { super(entityManager); } @Override @SuppressWarnings({"unchecked"}) protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository( RepositoryInformation information, EntityManager entityManager) {// 4 return new CustomRepositoryImpl<T, ID>((Class<T>) information.getDomainType(), entityManager); } @Override protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {// 5 return CustomRepositoryImpl.class; } } } 1. 自定义RepositoryFactoryBean,继承JpaRepositoryFactoryBean2. 重写createRepositoryFactory方法,用当前的CustomRepositoryFactory创建实例3. 创建CustomRepositoryFactory,并继承JpaRepositoryFactory4. 重写getTargetRepository方法,获得当前自定义的Repository实现5. 重写getRepositoryBaseClass,获得当前自定义的Repository实现的类型
4)开启自定义支持使用@EnableJpaRepositories的repositoryFactoryBeanClass来指定FactoryBean即可
@SpringBootApplication @EnableJpaRepositories(repositoryFactoryBeanClass = CustomRepositoryFactoryBean.class) public class TestApplication { @Autowired PersonRepository personRepository; public static void main(String[] args) { SpringApplication.run(Ch82Application.class, args); } }