第一节 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等)和具体数据库(例如H2MySQL等)的依赖。

三、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>

这个模块使用了HibernateH2数据库的JDBC驱动程序作为运行时依赖,但是代码是纯粹基于JPA规范的,没有使用任何Hibernate或数据库特定的类或接口。这意味着:如果我们更改了所用的数据库例如MySQL或者JPA实现框架例如Apache OpenJPA,我们只需要修改pom.xmlpersistence.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遵循这个规范。

results matching ""

    No results matching ""