首页 > 代码库 > 初入spring boot(五 )Spring Data JPA

初入spring boot(五 )Spring Data JPA

  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. 让数据库操作方法中可以使用entityManager
3. 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,继承JpaRepositoryFactoryBean
2.
重写createRepositoryFactory方法,用当前的CustomRepositoryFactory创建实例
3. 创建CustomRepositoryFactory,并继承JpaRepositoryFactory
4. 重写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);
        
    } 
}

 

初入spring boot(五 )Spring Data JPA