开始使用 Spring Data JPA(1)(2)
查询方法
当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注解:
- @Transactional(readOnly = true)
- public interface AccountRepository extends JpaRepository<Account, Long> {
- @Query("<JPQ statement here>")
- List<Account> findByCustomer(Customer customer);
- }
现在我们对CustomerServiceImpl用我们已经看到的特性做一个前后对比:
- @Repository
- @Transactional(readOnly = true)
- public class CustomerServiceImpl implements CustomerService {
- @PersistenceContext
- private EntityManager em;
- @Override
- public Customer findById(Long id) {
- return em.find(Customer.class, id);
- }
- @Override
- public List<Customer> findAll() {
- return em.createQuery("select c from Customer c", Customer.class).getResultList();
- }
- @Override
- public List<Customer> findAll(int page, int pageSize) {
- TypedQuery query = em.createQuery("select c from Customer c", Customer.class);
- query.setFirstResult(page * pageSize);
- query.setMaxResults(pageSize);
- return query.getResultList();
- }
- @Override
- @Transactional
- public Customer save(Customer customer) {
- // Is new?
- if (customer.getId() == null) {
- em.persist(customer);
- return customer;
- } else {
- return em.merge(customer);
- }
- }
- @Override
- public List<Customer> findByLastname(String lastname, int page, int pageSize) {
- TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class);
- query.setParameter(1, lastname);
- query.setFirstResult(page * pageSize);
- query.setMaxResults(pageSize);
- return query.getResultList();
- }
- }
好吧,首先创建CustomerRepository并消除CRUD方法:
- @Transactional(readOnly = true)
- public interface CustomerRepository extends JpaRepository<Customer, Long> { … }
- @Repository
- @Transactional(readOnly = true)
- public class CustomerServiceImpl implements CustomerService {
- @PersistenceContext
- private EntityManager em;
- @Autowired
- private CustomerRepository repository;
- @Override
- public Customer findById(Long id) {
- return repository.findById(id);
- }
- @Override
- public List<Customer> findAll() {
- return repository.findAll();
- }
- @Override
- public List<Customer> findAll(int page, int pageSize) {
- TypedQuery query = em.createQuery("select c from Customer c", Customer.class);
- query.setFirstResult(page * pageSize);
- query.setMaxResults(pageSize);
- return query.getResultList();
- }
- @Override
- @Transactional
- public Customer save(Customer customer) {
- return repository.save(customer);
- }
- @Override
- public List<Customer> findByLastname(String lastname, int page, int pageSize) {
- TypedQuery query = em.createQuery("select c from Customer c where c.lastname = ?1", Customer.class);
- query.setParameter(1, lastname);
- query.setFirstResult(page * pageSize);
- query.setMaxResults(pageSize);
- return query.getResultList();
- }
- }
到目前为止,一切都很好。以下两种方法都可以处理常见的场景:你希望给定查询能够分页而不是返回全部实体比如一页10条记录)。眼下我们通过两个整数适当限制查询来实现,但这样做有两个问题,两个整数实际上只代表了一个方面,这里并没有明确这一点。除此之外,结果我们只返回了一个简单的list,所以我们丢掉了实际页面的元数据信息:这是第一页吗?这是最后一页吗?总共有多少页?Spring Data提供了一个抽象包括两个接口:Pageable(捕捉分页请求)和Page捕获结果以及元信息)。让我们按照如下方式尝试添加 findByLastname(…)到存储库接口中并重写findAll(…)和findByLastname(…)方法:
- @Transactional(readOnly = true)
- public interface CustomerRepository extends JpaRepository<Customer, Long> {
- Page<Customer> findByLastname(String lastname, Pageable pageable);
- }
- @Override
- public Page<Customer> findAll(Pageable pageable) {
- return repository.findAll(pageable);
- }
- @Override
- public Page<Customer> findByLastname(String lastname, Pageable pageable) {
- return repository.findByLastname(lastname, pageable);
- }
请确保按照名字的变化对测试用例进行了适配,这样它应该能正常运行。归结下来为两件事:我们有CRUD方法支持分页并且查询执行机制知道分页的参数。在这个阶段,我们的包装类实际上已经过时了,因为客户端也可以直接调用我们的存储库接口,这样我们摆脱了整个的实现代码。
总结
在本文的课程中,我们把为存储库编写的代码减少到2个接口,包含3个方法一行XML:
- @Transactional(readOnly = true)
- public interface CustomerRepository extends JpaRepository<Customer, Long> {
- Page<Customer> findByLastname(String lastname, Pageable pageable);
- }
- @Transactional(readOnly = true)
- public interface AccountRepository extends JpaRepository<Account, Long> {
- List<Account> findByCustomer(Customer customer);
- }
- <jpa:repositories base-package="com.acme.repositories" />
我们拥有类型安全的CRUD方法、查询执行方法和内置的分页功能。这不仅适用于基于JPA的存储库,而且也适用于非关系型数据库,这很酷。第一个支持这些功能的非关系型数据库是MongoDB ,它是几天后发版的Spring Data Document中的一部分。你将会获得Mongo DB版的所有这些特性,而且我们还将支持其它一些数据库。另外,还有其它一些特性等待我们去探秘(比如,实体审计,自定义数据访问代码集成) ,我们会在后续的博文中涉及。
英文原文:Getting started with Spring Data JPA
译文链接:http://www.oschina.net/translate/getting-started-with-spring-data-jpa
用户点评