原创 | 使用 JPA 实现 DDD 持久化 - 通过 Spring Data JPA 访问数据

通过Spring Data JPA访问数据
上节内容介绍了如何通过JPA原生API访问数据库。从范例代码中可以看到,这个过程还是比较繁琐的,例如:
需要创建和调用
EntityManager;需要使用
JPQL编写查询语句;需要自己管理事务。
实际上大多数时候我们的查询都只是根据属性值查询实体对象而已。我们不希望为这样简单的目的编写一堆样板代码。
Spring Data JPA的出现大大简化了JPA查询的实现。它使得我们只需要通过创建一个接口,并且在其中根据我们的意图构造一个方法名称,Spring在运行时会自动使用JPA实现这个接口,查询数据库并返回正确的数据。
本项目的tmall-persistence-spring-data-jpa模块,就是使用spring-data-jpa实现tmall-domain模块中定义的仓储接口。
引入依赖
为了使用spring-data-jpa,我们只需要在maven项目的pom.xml中引入spring-data-jpa依赖。当然还需要一个JPA的实现框架,以及相关数据库的JDBC驱动:
Spring配置
需要类或xml形式定义Spring配置,创建JPA和spring-data-jpa相关的基础设施。此处采用配置类形式的Spring配置:
定义JpaRepository接口的子类,同时实现实体仓储接口
在tmall-domain模块中,我们定义了一个定价仓储接口:
定价实体Pricing记录产品每次调价的信息。其定义如下:
我们定义一个接口PricingRepository(注意是接口而不是实现类),同时扩展了Pricings和JpaRepository:
说明如下:
这个接口扩展了
Pricings和JpaRepository。后者提供了很多预定义的CRUD方法。spring-data-jpa提供的JpaRepository已经提供了Pricings接口中定义的save()方法。因此PricingRepository不再需要提供这个方法。由于
Pricings接口的方法getPricingAt()不符合spring-data-jpa的方法命名规范,不能由spring-data-jpa在运行时自动提供实现,因此我做了一个转换。我在PricingRepository中定义了一个符合spring-data-jpa规范的方法名findFirstByProduct AndEffectiveTimeIsLessThanEqualOrderByEffectiveTimeDesc(),同时利用Java 8引进的默认方法特性定义了Pricings接口的同名默认方法getPricingAt(),它直接调用findFirstByProductAndEffectiveTimeIsLessThanEqualOrderByEffectiveTime Desc()方法。findFirstByProductAndEffectiveTimeIsLessThanEqualOrderByEffectiveTimeDesc()方法由spring-data-jpa在运行时自动生成实现类。它查询数据库,返回关联指定产品的、生效时间不晚于指定时间的、按生效时间降序排列的定价实体的列表中的第一个定价实体(如果有的话)。通过给
PricingRepository类标注@Named注解,将它定义为一个由Spring容器管理的bean。可以把它注入到它的客户类中,供后者使用。@Named注解由依赖注入规范JSR-330定义。为什么不使用Spring中等价的@Bean注解?首先是因为JSR-330是依赖注入的规范,规范优先于实现;其次是因为@Bean只能注解类而不能注解接口。
集成测试
我们用集成测试作为仓储接口的客户类,来测试spring-data-jpa是否正确实现:
这个集成测试类使用了spring-test,因此可以实现依赖注入(通过JSR-330注解@Inject)和事务管理(通过@Transactional注解)。
无法自动实现的方法
并非所有的查询方法都能通过spring-data-jpa来自动生成实现类。这个时候也有解决方法。下面是一个例子:
订单仓储接口Orders中有一个findByOrgBuyers()方法,用于查找买家类型为机构买家的所有订单:
Orders接口中的findByBuyer()方法符合spring-data-jpa规范,可由spring-data-jpa自动生成实现;而findByOrgBuyers()方法无法自动生成实现。
这时可以定义一个接口,此处命名为OrderOperations,它定义了同样的findByOrgBuyers()方法:
同时提供它的实现类,命名为接口名加Impl后缀:
它可以依赖注入Spring管理的任何bean。此处通过构造函数注入一个EntityManager,由EntityManager实现findByOrgBuyers()方法。
我们的spring-data-jpa实现的仓储OrderRepository,除了扩展Orders和JpaRepository之外,还要扩展OrderOperations:
在运行时,spring-data-jpa会自动为标准方法findByBuyer()生成实现,并且将对OrderRepository的findByOrgBuyers()方法调用转发给OrderOperationsImpl的findByOrgBuyers()方法去实现。
总之,使用spring-data-jpa可以:
对于基于属性的简单查询和排序,可以在运行时自动生成实现;
对于复杂查询,可以自定义实现方法,由
spring-data-jpa调用。
总结
本章的实例覆盖了通过JPA实现ORM的主要内容。
通过业务分析和领域建模,创建领域模型,包含各种各样实体和值对象,以及它们之间的关系。
编写映射元数据,告知
JPA如何将领域模型映射到数据库的表和列。
总体而言,映射元数据既可以按照类级/属性级映射划分,又可以按物理/逻辑映射划分;
对于属性级映射,既可以按值/关联映射划分,又可以按单值/多值映射划分。
编写
persistence.xml文件,告知JPA如何连接到具体的数据库实例。通过
JPA原生API或spring-data-jpa访问数据。
后面的章节将对映射、连接、访问三大部分分门别类进行详细论述。其他种种如缓存、事务、事件机制等等细节也都会一一说明。
详细内容请戳这里↓↓↓
原创 | 使用JPA实现DDD持久化-通过Spring Data JPA访问数据
这一节就讲到这里,下一节我们讲全新章节"映射"。
如果觉得有收获,点个【赞】鼓励一下呗!

版权声明: 本文为 InfoQ 作者【编程道与术】的原创文章。
原文链接:【http://xie.infoq.cn/article/9ef7d023d83f0c18a17c65d4d】。文章转载请联系作者。











评论