第七节 只要 O,忘记 R
面向对象和面向关系是两个不同的范式。要同时面向两种不同的范式并且在两者之间频繁切换,对开发者来说是一种既筋疲力尽又容易出错的体验。通过JPA
这样的ORM
规范,我们期待达到的效果是:对于一般开发者来说:
只要 O,不要 R。
就是说:对于大多数开发者,在大多数时候,只需要掌握和处理对象模型,不需要处理数据库和进行SQL
访问。
我们仍然可以保留“仓储(Repository)”的概念,但是我们不再是根据列值来查询数据行,而是根据属性值来查询领域对象。
第一节 Maven 配置
遵循良好的工程实践,为了实现业务代码和技术代码的分离,我将范例项目按多模块Maven
项目的方式来构建:
tmall-domain
模块:纯粹的业务层代码,与技术实现(Hibernate、Spring、数据库等)无关。
tmall-persistence-jpa
模块:使用原生JPA API
实现tmall-domain
模块中定义的仓储接口。
tmall-persistence-spring-data-jpa
模块:使用Spring Data JPA
实现tmall-domain
模块中定义的仓储接口。
本项目的代码可以在github
网站https://github.com/dayatang/jpa-sample-tmall下载。
一、外层伞项目
最外层的“伞”项目包含以上三个子模块,并定义通用的Maven
配置(主要是统一配置依赖项及其版本),供三个子模块继承。各个子模块通过继承这个伞项目的Maven
配置,一方面简化了自身的配置项,另一方面也为子模块定义了依赖项的统一版本号。
伞项目的pom.xml
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>yang.yu.tmall</groupId>
<artifactId>jpa-sample-tmall</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>jpa-sample-tmall</name>
<url>http://tmall.yyang.io</url>
<modules>
<module>tmall-domain</module>
<module>tmall-persistence-jpa</module>
<module>tmall-persistence-spring-data-jpa</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.23.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.200</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.5.15</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.17.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.2</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.9.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.1.1</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
复制代码
在pom.xml
中,通过dependencyManagement
节点,统一指定了各个子项目共同依赖项的版本号;通过build
节点下的pluginManagement
节点,统一指定了构建中需要用到的Maven
插件的版本号。继承伞项目的子模块使用到这些依赖项和插件的时候,不再需要指定它们的版本号。
二、tmall-domain 模块
本模块实现纯粹的业务逻辑,是软件中的领域层(Domain Layer)。主要包含需要持久化的实体和值对象,以及各个实体所对应的仓储接口(值对象的生命周期从属于实体,因此不需要自己的仓储接口)。
仓储(Repository
)接口旧称数据访问对象(DAO
)接口,用于从数据库中存取和查询实体。根据Evans
的领域驱动设计(DDD
)思想,仓储和实体、值对象、领域服务一样,属于领域对象的一种,它的存在意义主要是:通过隔离掉领域层对数据库和数据访问技术的概念依赖,实现了依赖倒置:领域层不依赖于数据访问层(例如后面两个模块),相反,数据访问层依赖于领域层,因为数据访问层需要实现领域层中的仓储接口。
因为仓储和实体的都是领域对象,两者地位等同,甚至可能相互引用(实体可能使用仓储来完成自己的某些功能),因此我将仓储接口和实体、值对象放到同一个包中,而不是将所有的仓储接口放到一个专门的包中,否则可能导致两个包之间的双向依赖或循环依赖。这是“基于功能而不是基于类型划分包”原则的应用。
基于“仓储可视为实体的集合”这个理念,我们将用实体类名(例如Order
)的复数形式作为该实体对应的仓储接口的名字(例如Orders
)。
tmall-domain
模块的pom.xml
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>yang.yu.tmall</groupId>
<artifactId>jpa-sample-tmall</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>tmall-domain</artifactId>
<dependencies>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
</dependency>
</dependencies>
</project>
复制代码
该模块仅仅依赖于JPA
规范,不包含对任何技术框架(例如Hibernate
等)和具体数据库(例如H2
、MySQL
等)的依赖。
三、tmall-persistence-jpa 模块
本模块使用原生JPA API
实现tmall-domain
领域层模块中定义的仓储接口。在系统中属于数据访问层(Data Access Layer)或更加泛化意义上的基础设施层(Infrastructure Layer)。广义上的基础设施层不但包含数据库访问代码,还包含对缓存、消息中间件、第三方系统等等的访问代码。
tmall-persistence-jpa
模块的pom.xml
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>yang.yu.tmall</groupId>
<artifactId>jpa-sample-tmall</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>tmall-persistence-jpa</artifactId>
<dependencies>
<dependency>
<groupId>yang.yu.tmall</groupId>
<artifactId>tmall-domain</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
</project>
复制代码
这个模块使用了Hibernate
和H2
数据库的JDBC
驱动程序作为运行时依赖,但是代码是纯粹基于JPA
规范的,没有使用任何Hibernate
或数据库特定的类或接口。这意味着:如果我们更改了所用的数据库例如MySQL
或者JPA
实现框架例如Apache OpenJPA
,我们只需要修改pom.xml
和persistence.xml
等配置文件,而不需要对产品代码做丝毫的改动。
四、tmall-persistence-spring-data-jpa 模块
本模块使用Spring Data JPA
框架实现tmall-domain
领域层模块中定义的仓储接口,大大简化了实现。在系统中属于数据访问层/基础设施层。
tmall-persistence-spring-data-jpa
模块的pom.xml
内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>yang.yu.tmall</groupId>
<artifactId>jpa-sample-tmall</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>tmall-persistence-spring-data-jpa</artifactId>
<dependencies>
<dependency>
<groupId>yang.yu.tmall</groupId>
<artifactId>tmall-domain</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<dependency>
<groupId>javax.inject</groupId>
<artifactId>javax.inject</artifactId>
<version>1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.1</version>
</dependency>
</dependencies>
</project>
复制代码
本模块需要对Spring Data JPA
框架的编译时依赖,对数据库驱动程序和JPA
实现框架Hibernate
的运行时依赖。由于项目中采用C3P0
实现数据库连接池,所以还需要对C3P0
的编译时依赖(如果采用XML
形式配置Spring
,则只需要运行时依赖)。javax.inject
是依赖注入规范,Spring
遵循这个规范。
详细内容请戳这里↓↓↓
原创 | 使用JPA实现DDD持久化-只要O,忘记R & Maven配置
这一节就讲到这里,下一节我们讲"数据库连接配置:persistence.xml"。
如果觉得有收获,点个【赞】鼓励一下呗!
评论