现在的位置: 首页 > 综合 > 正文

开始使用 Spring Data JPA(2)

2018年05月03日 ⁄ 综合 ⁄ 共 4865字 ⁄ 字号 评论关闭

当Spring Data JPA为创建AccountRepository接口创建Spring实例的时候,它会检查接口里面定义的所有查询方法并且 为它们每个都派生一个查询。默认情况下,Spring Data JPA 将自动解析方法名并以此创建一个查询,查询用标准JPA的API实现。在本例中findByCustomer(...)方法在逻辑上等同于JPQL 查询“select a from Account a where a.customer = ?1”。解析方法名称的解析器支持大量的关键字比如And,Or,GreaterThan,LessThan,Like,IsNull,Notand等等,如果您喜欢,您还可以添加ORDER
BY子句。有关详情请参阅文档。这种机制给我们提供了一个查询方法编程模型就像你在Grails
或 Spring Roo中用到的一样。

现在,假设你想要显式的使用指定的查询。要做到这点你可以按照如下命名规约(本例中为Account.findByCustomer)在实体上通过注解或在orm.xml中声明一个JPA命名的查询来实现,另外一个选择就是你可以在存储库方法中使用@Query注解:

  1. @Transactional(readOnly = true)
     
  2. public interface AccountRepository extends JpaRepository<Account, Long> {
     
  3.  
  4.   @Query("<JPQ statement here>")
     
  5.   List<Account> findByCustomer(Customer customer);   

现在我们对CustomerServiceImpl用我们已经看到的特性做一个前后对比:

  1. @Repository 
  2. @Transactional(readOnly = true)
     
  3. public class CustomerServiceImpl implements CustomerService {
     
  4.  
  5.   @PersistenceContext 
  6.   private EntityManager em;  
  7.  
  8.   @Override 
  9.   public Customer findById(Long id) {
     
  10.     return em.find(Customer.class, id);
     
  11.   }  
  12.  
  13.   @Override 
  14.   public List<Customer> findAll() {
     
  15.     return em.createQuery("select c from Customer c", Customer.class).getResultList();
     
  16.   }  
  17.  
  18.   @Override 
  19.   public List<Customer> findAll(int page, int pageSize) {
     
  20.  
  21.     TypedQuery query = em.createQuery("select c from Customer c", Customer.class);
     
  22.  
  23.     query.setFirstResult(page * pageSize);  
  24.     query.setMaxResults(pageSize);  
  25.  
  26.     return query.getResultList();
     
  27.   }  
  28.  
  29.   @Override 
  30.   @Transactional 
  31.   public Customer save(Customer customer) {
     
  32.  
  33.     // Is new?
     
  34.     if (customer.getId() == null) {
     
  35.       em.persist(customer);  
  36.       return customer;  
  37.     } else {  
  38.       return em.merge(customer);
     
  39.     }  
  40.   }  
  41.  
  42.   @Override 
  43.   public List<Customer> findByLastname(String lastname, int page, int pageSize) {
     
  44.  
  45.     TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class);
     
  46.  
  47.     query.setParameter(1, lastname);  
  48.     query.setFirstResult(page * pageSize);  
  49.     query.setMaxResults(pageSize);  
  50.  
  51.     return query.getResultList();
     
  52.   }  

好吧,首先创建CustomerRepository并消除CRUD方法:

  1. @Transactional(readOnly = true)
     
  2. public interface CustomerRepository extends JpaRepository<Customer, Long> { … } 
  1. @Repository 
  2. @Transactional(readOnly = true)
     
  3. public class CustomerServiceImpl implements CustomerService {
     
  4.  
  5.   @PersistenceContext 
  6.   private EntityManager em;  
  7.  
  8.   @Autowired 
  9.   private CustomerRepository repository;
     
  10.  
  11.   @Override 
  12.   public Customer findById(Long id) {
     
  13.     return repository.findById(id);
     
  14.   }  
  15.  
  16.   @Override 
  17.   public List<Customer> findAll() {
     
  18.     return repository.findAll();
     
  19.   }  
  20.  
  21.   @Override 
  22.   public List<Customer> findAll(int page, int pageSize) {
     
  23.  
  24.     TypedQuery query = em.createQuery("select c from Customer c", Customer.class);
     
  25.  
  26.     query.setFirstResult(page * pageSize);  
  27.     query.setMaxResults(pageSize);  
  28.  
  29.     return query.getResultList();
     
  30.   }  
  31.  
  32.   @Override 
  33.   @Transactional 
  34.   public Customer save(Customer customer) {
     
  35.     return repository.save(customer);
     
  36.   }  
  37.  
  38.   @Override 
  39.   public List<Customer> findByLastname(String lastname, int page, int pageSize) {
     
  40.  
  41.     TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class);
     
  42.  
  43.     query.setParameter(1, lastname);  
  44.     query.setFirstResult(page * pageSize);  
  45.     query.setMaxResults(pageSize);  
  46.  
  47.     return query.getResultList();
     
  48.   }  

到目前为止,一切都很好。以下两种方法都可以处理常见的场景:你希望给定查询能够分页而不是返回全部实体(比如一页10条记录)。眼下我们通过两个整数适当限制查询来实现,但这样做有两个问题,两个整数实际上只代表了一个方面,这里并没有明确这一点。除此之外,结果我们只返回了一个简单的list,所以我们丢掉了实际页面的元数据信息:这是第一页吗?这是最后一页吗?总共有多少页?Spring Data提供了一个抽象包括两个接口:Pageable(捕捉分页请求)和Page(捕获结果以及元信息)。让我们按照如下方式尝试添加 findByLastname(…)到存储库接口中并重写findAll(…)和findByLastname(…)方法:

  1. @Transactional(readOnly = true
     
  2. public interface CustomerRepository extends JpaRepository<Customer, Long> {
     
  3.  
  4.   Page<Customer> findByLastname(String lastname, Pageable pageable);   
  1. @Override   
  2. public Page<Customer> findAll(Pageable pageable) {  
  3.   return repository.findAll(pageable);
     
  4. }  
  5.  
  6. @Override 
  7. public Page<Customer> findByLastname(String lastname, Pageable pageable) {  
  8.   return repository.findByLastname(lastname, pageable); 
     

请确保按照名字的变化对测试用例进行了适配,这样它应该能正常运行。归结下来为两件事:我们有CRUD方法支持分页并且查询执行机制知道分页的参数。在这个阶段,我们的包装类实际上已经过时了,因为客户端也可以直接调用我们的存储库接口,这样我们摆脱了整个的实现代码。

总结

在本文的课程中,我们把为存储库编写的代码减少到2个接口,包含3个方法一行XML:

  1. @Transactional(readOnly = true
     
  2. public interface CustomerRepository extends JpaRepository<Customer, Long> {
     
  3.  
  4.     Page<Customer> findByLastname(String lastname, Pageable pageable);   
  1. @Transactional(readOnly = true)
     
  2. public interface AccountRepository extends JpaRepository<Account, Long> {
     
  3.  
  4.     List<Account> findByCustomer(Customer customer);   
  1. <jpa:repositories base-package="com.acme.repositories" /> 

我们拥有类型安全的CRUD方法、查询执行方法和内置的分页功能。这不仅适用于基于JPA的存储库,而且也适用于非关系型数据库,这很酷。第一个支持这些功能的非关系型数据库是MongoDB ,它是几天后发版的Spring Data Document中的一部分。你将会获得Mongo DB版的所有这些特性,而且我们还将支持其它一些数据库。另外,还有其它一些特性等待我们去探秘(比如,实体审计,自定义数据访问代码集成) ,我们会在后续的博文中涉及。

抱歉!评论已关闭.