如何在 Maven 中配置 JPA 以进行测试

有没有办法在 Maven 项目中设置第二个持久性.xml文件,以便将其用于测试,而不是用于部署的普通文件?

我尝试将持久性.xml放入src/test/resources/META-INF中,该函数被复制到target/test-classes/META-INF中,但似乎target/classes/META-INF(来自src/main/resources的副本)变得更受欢迎,尽管以正确的顺序列出了类路径条目:mvn -X test

[DEBUG] Test Classpath :
[DEBUG]   /home/uqpbecke/dev/NetBeansProjects/UserManager/target/test-classes
[DEBUG]   /home/uqpbecke/dev/NetBeansProjects/UserManager/target/classes
[DEBUG]   /home/uqpbecke/.m2/repository/junit/junit/4.5/junit-4.5.jar
...

我希望能够针对简单的 hsqldb 配置运行测试,而不必更改 JPA 配置的部署版本,最好是在项目签出后立即运行,而无需进行任何本地调整。


答案 1

以下内容适用于 Maven 2.1+(在此之前,在测试和包之间没有可以绑定执行的阶段)。

您可以使用 maven-antrun-plugin 在测试期间将持久性.xml替换为测试版本,然后在打包项目之前恢复正确的版本。

此示例假设生产版本是 src/main/resources/META-INF/persistence.xml测试版本是 src/test/resources/META-INF/persistence.xml,因此它们将分别复制到 target/classes/META-INF 和 target/test-classes/META-INF。

将其封装到mojo中会更优雅,但是由于您只是复制文件,因此似乎有些过分。

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <version>1.3</version>
  <executions>
    <execution>
      <id>copy-test-persistence</id>
      <phase>process-test-resources</phase>
      <configuration>
        <tasks>
          <!--backup the "proper" persistence.xml-->
          <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
          <!--replace the "proper" persistence.xml with the "test" version-->
          <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
    <execution>
      <id>restore-persistence</id>
      <phase>prepare-package</phase>
      <configuration>
        <tasks>
          <!--restore the "proper" persistence.xml-->
          <copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
</plugin>

答案 2

在EE6 / CDI / JPA项目中,无需任何进一步配置即可很好地获取测试。src/test/resources/META-INF/persistence.xml

在Spring中使用JPA时,以下内容适用于用于测试的应用程序上下文:

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <!--
        JPA requires META-INF/persistence.xml, but somehow prefers the one
        in classes/META-INF over the one in test-classes/META-INF. Spring
        to the rescue, as it allows for setting things differently, like by
        referring to "classpath:persistence-TEST.xml". Or, simply referring
        to "META-INF/persistence.xml" makes JPA use the test version too: 
    -->
    <property name="persistenceXmlLocation" value="META-INF/persistence.xml" />

    <!-- As defined in /src/test/resources/META-INF/persistence.xml -->
    <property name="persistenceUnitName" value="myTestPersistenceUnit" />
    <property name="jpaVendorAdapter">
        <bean
            class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
        </bean>
    </property>
</bean>

在这里,(复制到 )将优先于(复制到 )。/src/test/resources/META-INF/persistence.xmltarget/test-classes/src/main/resources/META-INF/persistence.xmltarget/classes

不幸的是,文件的位置也决定了所谓的“持久性单元的根”,然后确定扫描哪些类以进行注释。因此,使用 将扫描 中的类,而不是 中的类(需要测试的类将位于该类的位置)。persistence.xml@Entity/src/test/resources/META-INF/persistence.xmltarget/test-classestarget/classes

因此,对于测试,需要显式地将条目添加到 中以避免 。通过使用不同的文件名(如 ),可以将该文件放在与常规文件完全相同的文件夹中,从而避免对条目的需求。然后,测试文件夹中的Spring上下文可以只引用 ,Spring将在中找到它。<class>persistence.xmljava.lang.IllegalArgumentException: Not an entity: class ...<class>persistence-TEST.xmlpersistence.xml<property name="persistenceXmlLocation" value="META-INF/persistence-TEST.xml" />src/main

作为替代方法,对于实际应用程序和测试,可能能够保持相同,并且只能在 中定义一个。大多数配置(如驱动程序,方言和可选凭据)都可以在Spring上下文中完成。还可以在上下文中传递等设置:persistence.xmlsrc/mainhibernate.hbm2ddl.auto

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <!-- For example: com.mysql.jdbc.Driver or org.h2.Driver -->
    <property name="driverClassName" value="#{myConfig['db.driver']}" />
    <!-- For example: jdbc:mysql://localhost:3306/myDbName or 
        jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 -->
    <property name="url" value="#{myConfig['db.url']}" />
    <!-- Ignored for H2 -->
    <property name="username" value="#{myConfig['db.username']}" />
    <property name="password" value="#{myConfig['db.password']}" />
</bean>

<bean id="jpaAdaptor"
    class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
    <!-- For example: org.hibernate.dialect.MySQL5Dialect or 
        org.hibernate.dialect.H2Dialect -->
    <property name="databasePlatform" value="#{myConfig['db.dialect']}" />
</bean>

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="jpaVendorAdapter" ref="jpaAdapter" />
    <property name="jpaProperties">
        <props>
            <!-- For example: validate, update, create or create-drop -->
            <prop key="hibernate.hbm2ddl.auto">#{myConfig['db.ddl']}</prop>
            <prop key="hibernate.show_sql">#{myConfig['db.showSql']}</prop>
            <prop key="hibernate.format_sql">true</prop>
        </props>
    </property>
</bean>