【问题标题】:How to configure JPA for testing in Maven如何配置 JPA 以在 Maven 中进行测试
【发布时间】:2010-09-27 23:37:01
【问题描述】:

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

我尝试将 persistence.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 配置的部署版本,最好是在项目签出后直接进行,无需进行任何本地调整。

【问题讨论】:

    标签: java testing maven-2 jpa integration-testing


    【解决方案1】:

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

    您可以在测试期间使用 maven-antrun-plugin 将 persistence.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>
    

    【讨论】:

    • 确实,这是一个巧妙的解决方案。但是,您可能还想将 overwite="true" 属性添加到最后一个 Ant 任务,以确保复制回正确的 XML 文件。在我的环境中,由于目的地和目标的时间戳相同,它似乎失败了。
    【解决方案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.xml(复制到 target/test-classes)将优先于 /src/main/resources/META-INF/persistence.xml(复制到 target/classes)。

    不幸的是,persistence.xml 文件的位置也决定了所谓的“persistence unit's root”,然后它决定了扫描哪些类以查找@Entity 注释。因此,使用/src/test/resources/META-INF/persistence.xml 将扫描target/test-classes 中的类,而不是target/classes 中的类(需要测试的类将存在于其中)。

    因此,为了进行测试,需要将&lt;class&gt; 条目显式添加到persistence.xml,以避免java.lang.IllegalArgumentException: Not an entity: class ...。可以通过使用不同的文件名(如persistence-TEST.xml)来避免需要&lt;class&gt; 条目,并将该文件放在与常规persistence.xml 文件完全相同的文件夹中。然后,您的测试文件夹中的 Spring 上下文可以仅引用 &lt;property name="persistenceXmlLocation" value="META-INF/persistence-TEST.xml" /&gt;,Spring 会在 src/main 中为您找到它。

    作为替代方案,可以将persistence.xml 与实际应用程序和测试保持一致,并且只在src/main 中定义一个。大多数配置(例如驱动程序、方言和可选凭据)都可以在 Spring 上下文中完成。 hibernate.hbm2ddl.auto等设置也可以是passed in the context:

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

    【讨论】:

    • 原问题中没有提到spring。
    • 没错,@Dmitry,但这值得你投反对票吗?那么 Spring 的同样问题应该是另一个问题吗?在我看来,Stack Overflow 不是这样工作的,显然有 18 位支持者找到了他们的问题的答案,标题为“如何配置 JPA 以在 Maven 中进行测试”。更重要的是,我回答的第一段不是关于 Spring 的,然后是 "When using JPA in Spring [...]".
    • “那么 Spring 的同一个问题应该是另一个问题吗?” ——是的,还有这样的问题。而且很难找到非弹簧配置问题的答案。 “18 upvoters”——这就是为什么我必须投反对票。因为这是一个很好的答案。但是对于另一个问题。
    • 好的,那么我们(非常)不同意,@Dmitry。显然,当我需要回答 6 年前的“如何配置 JPA 以在 Maven 中进行测试” 时,我没有找到任何特定于 Spring 的问题,并在找到解决方案后发布了我的解决方案。 (顺便说一句,即使以 Spring-config 开头的部分也不仅仅与 Spring 有关;如果您自己也遇到此问题,请务必阅读 "persistence unit's root" 上的链接说明。)
    【解决方案3】:

    多个persistence.xml文件似乎是JPA的一个普遍问题,只能通过类加载技巧来解决。

    一种对我有用的解决方法是在一个 persistence.xml 文件中定义多个持久性单元,然后确保您的部署和测试代码使用不同的绑定(在 Spring 中,您可以在实体管理器上设置“persistenceUnitName”属性工厂)。它会使用测试配置污染您的部署文件,但如果您不介意它可以正常工作。

    【讨论】:

      【解决方案4】:

      为测试添加一个persistance.xml:/src/test/resources/META-INF/persistence.xml 正如@Arjan 所说,这将改变persistance unit's root 并且实体类将在目标/测试类中进行扫描。为了解决这个问题,将 jar-file 元素添加到这个persistance.xml:

      /src/test/resources/META-INF/persistence.xml

      <persistence xmlns="http://java.sun.com/xml/ns/persistence"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
                   version="2.0">
          <persistence-unit name="com.some.project">
              <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
              <jar-file>${project.basedir}/target/classes</jar-file>
              <properties>
                  <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/test_database" />
                  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
                  <property name="javax.persistence.jdbc.user" value="user" />
                  <property name="javax.persistence.jdbc.password" value="..." />
              </properties>
          </persistence-unit>
      </persistence>
      

      然后,将测试资源的过滤添加到您的 pom.xml:

      <project>
          ...
          <build>
              ...
              <testResources>
                  <testResource>
                      <directory>src/test/resources</directory>
                      <filtering>true</filtering>
                  </testResource>
              </testResources>
              ...
          </build>
      ...
      </project>
      

      这会起作用,因为 jar-file 可以定位到目录,而不仅仅是 jar 文件。

      【讨论】:

      • 我在哪里添加这个?在我的 中只有一个 元素。 testResources 是插件的一部分还是我应该把它放在下面?
      • @HubertGrzeskowiak,它应该放在plugins xml 元素下面。这是一个带有example of pom.xml 的页面,其中存在testResources 元素。
      【解决方案5】:

      我更喜欢使用不同的 persistence.xml 作为 Rich Seller post 进行测试和生产的解决方案@(谢谢!!)。

      但需要改变:

      <copy file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml"/>
      

      为:

      <move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
      

      为了persistence.xml.proper没有嵌入到.jar文件中

      【讨论】:

      • 除了富卖家的答案之外,这个答案对我有用。
      【解决方案6】:

      我尝试了 ClassLoaderProxy 方法,但问题是 JPA 注释的类没有被休眠作为持久类处理。

      因此决定不使用persistence.xml 进行尝试。优点是 maven 构建和 Eclipse JUnit 测试无需修改即可工作。

      我有一个用于 JUnit 测试的持久支持类。

      public class PersistenceTestSupport {
      
          protected EntityManager em;
          protected EntityTransaction et;
      
          /**
           * Setup the the {@code EntityManager} and {@code EntityTransaction} for
           * local junit testing.
           */
          public void setup() {
      
              Properties props = new Properties();
              props.put("hibernate.hbm2ddl.auto", "create-drop");
              props.put("hibernate.dialect", "org.hibernate.dialect.MySQLDialect");
              props.put("hibernate.connection.url", "jdbc:mysql://localhost/db_name");
              props.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
              props.put("hibernate.connection.username", "user");
              props.put("hibernate.connection.password", "****");
      
              Ejb3Configuration cfg = new Ejb3Configuration();
              em = cfg.addProperties(props)
                  .addAnnotatedClass(Class1.class)
                  .addAnnotatedClass(Class2.class)
                  ...
                          .addAnnotatedClass(Classn.class)
                  .buildEntityManagerFactory()
                  .createEntityManager();
      
              et = em.getTransaction();
          }
      }
      

      我的测试类只是扩展 PersistenceTestSupport 并在 TestCase.setup() 中调用 setup()。

      唯一的缺点是使持久类保持最新,但对于 JUnit 测试,这对我来说是可以接受的。

      【讨论】:

        【解决方案7】:

        这个答案可能听起来很傻,但我正在寻找一种方法,让我通过 Run As -> JUnit Test 从 eclipse 运行这些测试。我就是这样做的:

        @BeforeClass
        public static void setUp() throws IOException {
            Files.copy(new File("target/test-classes/META-INF/persistence.xml"), new File("target/classes/META-INF/persistence.xml"));
            // ...
        }
        

        我只是将 test/persistence.xml 复制到 classes/persistence.xml。这行得通。

        【讨论】:

        • 不错。只是为了后代:在 EE6/CDI/JPA 项目中使用它时(根本不需要任何技巧),JPA 仍然更喜欢 target/test-classes/META-INF/persistence.xml 而不是 target/classes/META-INF/persistence.xml,因此 JPA 只会扫描 target/test-classes 中的类,而不是target/classes 中的那些(在依赖 @Entity 注释时可能会很麻烦)。
        • 但是你覆盖了 src/main persistence.xml 对吧?
        【解决方案8】:

        保留两个 persistence.xml 文件的副本。一个用于测试,另一个用于正常构建。

        默认生命周期将build persistence.xml复制到src/test/resources/META-INF

        创建一个单独的配置文件,运行时会将测试持久性.xml 复制到 src/test/resources/META-INF

        【讨论】:

          【解决方案9】:

          Persistence.xml 用作搜索实体类的起点,除非您明确列出所有类并添加 . 所以如果你想用另一个文件覆盖这个文件,比如从 src/test/resources,你必须在第二个 persistence.xml 中指定每个实体类,否则找不到实体类。

          另一种解决方案是使用 maven-resources-plugin('copy-resources' 目标)覆盖文件。但是你必须覆盖它两次,一次用于测试(例如阶段 process-test-classes),一次用于真正的打包(阶段 'prepare-package')。

          【讨论】:

            【解决方案10】:

            这是 Rich Seller 答案的扩展,通过正确处理 Hibernate 在类路径上查找多个 persistence.xml 文件和预测试状态恢复。

            设置:

            创建一个用于部署/打包的持久性文件和一个用于测试:

            • src/main/resources/persistence.xml

            • src/test/resources/persistence-testing.xml

            在您的 pom.xml 中将其添加到插件部分:

                    <plugin>
                        <artifactId>maven-antrun-plugin</artifactId>
                        <version>1.3</version>
                        <executions>
                            <execution>
                                <id>copy-test-persistence</id>
                                <phase>process-test-resources</phase>
                                <configuration>
                                    <tasks>
                                        <echo>renaming deployment persistence.xml</echo>
                                        <move file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.proper"/>
                                        <echo>replacing deployment persistence.xml with test version</echo>
                                        <copy file="${project.build.testOutputDirectory}/META-INF/persistence-testing.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                                    </tasks>
                                </configuration>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                            </execution>
                            <execution>
                                <id>restore-persistence</id>
                                <phase>prepare-package</phase>
                                <configuration>
                                    <tasks>
                                        <echo>restoring the deployment persistence.xml</echo>
                                        <move file="${project.build.outputDirectory}/META-INF/persistence.xml.proper" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                                    </tasks>
                                </configuration>
                                <goals>
                                    <goal>run</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
            

            优于其他解决方案的优势

            • 无需额外的 Java 代码
            • 类路径中只有一个 persistence.xml
            • 构建和测试都按预期工作
            • 描述控制台上的输出 (echo)
            • 对于包装,状态已 100% 恢复。没有剩余文件

            【讨论】:

              【解决方案11】:

              我正在尝试做同样的事情。我有一个适合我的解决方案 - 你的可能会有所不同(你可能不喜欢这个解决方案......它有点低级)。

              我在网上看到一篇文章,他们正在使用自定义类加载器来做类似的事情,这给了我灵感。如果有人可以看到如何改进,那么欢迎提出建议。对于部署,我依赖于 EntityManager 的容器注入,但对于测试,我使用以下代码自己创建它:

              final Thread currentThread = Thread.currentThread();
              final ClassLoader saveClassLoader = currentThread.getContextClassLoader();
              currentThread.setContextClassLoader(new ClassLoaderProxy(saveClassLoader));
              
              EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("test");
              em = emFactory.createEntityManager();
              

              然后,ClassLoaderProxy 将尽可能小,只需将对 META-INF/persistence.xml 的请求重定向到 META-INF/test-persist.xml:

              public class ClassLoaderProxy extends ClassLoader {
              
                  public ClassLoaderProxy(final ClassLoader parent) {
                      super();
                  }
              
                  @Override
                  public Enumeration<URL> getResources(final String name) throws IOException {
                      if (!"META-INF/persistence.xml".equals(name)) {
                          return super.getResources(name);
                      } else {
                          System.out.println("Redirecting persistence.xml to test-persist.xml");
                          return super.getResources("META-INF/test-persist.xml");
                      }
                  }
              }
              

              再解释一下:

              1. 有两个 persistence.xml 文件(一个名为 persistence.xml,用于测试外部,一个名为 test-persist.xml,用于测试)。
              2. 自定义类加载器对单元测试有效(部署时一切正常)
              3. 自定义类加载器将对“META-INF/persistence.xml”的请求重定向到测试版本(“META-INF/test-persist.xml”)。

              我最初遇到了一些问题,因为 Hibernate 会(以某种方式)恢复到用于加载 Hibernate 的类加载器(至少我认为这是正在发生的事情)。我发现将 ClassLoader 切换代码(第一个块)作为静态块放在您的测试用例中,它将在 Hibernate 之前加载,但是根据您的单元测试结构,您可能还需要将相同的代码放在其他地方(恶心)。

              【讨论】:

              • 有趣...它会起作用,但您会从其他开发人员那里得到一些有趣的表情。此外,您忘记恢复线程的上下文类加载器。
              • 很公平。关于“忘记”评论……这是几年前的事了,所以谁知道我是否忘记了,但我看不出意图如何更清楚。添加代码(使用 try/finally 等)似乎会分散目标的注意力,但我想每个都是自己的。
              • 同意代码清晰性。我只是在想象有人复制粘贴该代码并遇到麻烦。虽然,如果你想避免麻烦,最好不要复制粘贴随机代码。 :)
              【解决方案12】:

              另一种方法是使用单独的 persistence.xml 进行测试(test/../META-INF/persistence.xml 但覆盖扫描器如下:-

              测试persistence.xml需要包含

              &lt;property name="hibernate.ejb.resource_scanner" value = "...TestScanner" /&gt;

              新类TestScanner的代码如下。

              import java.lang.annotation.Annotation;
              import java.net.MalformedURLException;
              import java.net.URL;
              import java.util.Set;
              import org.hibernate.ejb.packaging.NamedInputStream;
              import org.hibernate.ejb.packaging.NativeScanner;
              
              
              public class TestScanner extends NativeScanner
              {
              
              @Override
              public Set <Class <?> > 
              getClassesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
              {  return super.getClassesInJar (getUpdatedURL (jar), annotations); }
              
              @Override
              public Set <NamedInputStream> 
              getFilesInJar (URL jar, Set <String> patterns)
              {  return super.getFilesInJar (getUpdatedURL (jar), patterns); }
              
              @Override
              public Set <Package> 
              getPackagesInJar (URL jar, Set <Class <? extends Annotation> > annotations)
              {  return super.getPackagesInJar (getUpdatedURL (jar), annotations); }
              
              private URL getUpdatedURL (URL url)
              {
                String oldURL = url.toExternalForm ();
                String newURL = oldURL.replaceAll ("test-classes", "classes");
                URL result;
                try {
                  result = newURL.equals (oldURL) ? url : new URL (newURL);
                } catch (MalformedURLException e)
                {  // Whatever  }
                return result;
              }
              
              }
              

              【讨论】:

              • 这似乎不再适用于 Hibernate 5。
              【解决方案13】:

              使用 OpenEJB 时,persistence.xml 可以用替代描述符覆盖:http://tomee.apache.org/alternate-descriptors.html

              【讨论】:

                【解决方案14】:

                此用例的另一种选择是添加多个持久性单元,一个用于生产,另一个用于测试并相应地注入 EntityManagerFactory。

                将两个持久性单元放入实际项目的 persistence.xml 中,并让您的测试用例注入正确的 EntityManager。下面的示例说明了如何使用 guice 来做到这一点。请注意,为了完整起见,我留下了一些 mockito 模拟,mockito 特定代码已相应标记,不需要注入。

                public class HibernateTestDatabaseProvider extends AbstractModule {
                    private static final ThreadLocal<EntityManager> ENTITYMANAGER_CACHE = new ThreadLocal<>();
                
                    @Override
                    public void configure() {
                    }
                
                    @Provides
                    @Singleton
                    public EntityManagerFactory provideEntityManagerFactory() {
                        return Persistence.createEntityManagerFactory("my.test.persistence.unit");
                    }
                
                    @Provides
                    public CriteriaBuilder provideCriteriaBuilder(EntityManagerFactory entityManagerFactory) {
                        return entityManagerFactory.getCriteriaBuilder();
                    }
                
                    @Provides
                    public EntityManager provideEntityManager(EntityManagerFactory entityManagerFactory) {
                        EntityManager entityManager = ENTITYMANAGER_CACHE.get();
                        if (entityManager == null) {
                            // prevent commits on the database, requires mockito. Not relevant for this answer
                            entityManager = spy(entityManagerFactory.createEntityManager());
                
                
                            EntityTransaction et = spy(entityManager.getTransaction());
                            when(entityManager.getTransaction()).thenReturn(et);
                            doNothing().when(et).commit();
                
                            ENTITYMANAGER_CACHE.set(entityManager);
                        }
                        return entityManager;
                    }
                }
                

                【讨论】:

                  【解决方案15】:

                  使用persistence.xml将测试放在自己的maven项目中

                  【讨论】:

                  • 这是一种解决方案,但如果您的 persistence.xml 是域模型 (JPA) 模块的一部分,则它不起作用。
                  【解决方案16】:

                  我建议使用不同的 maven 配置文件,您可以在其中过滤 database.proprerties 文件并为每个配置文件设置一个 database.properties。

                  这样您就不必保留除 .properties 之外的任何其他配置文件的副本。

                  <properties>
                      <!-- Used to locate the profile specific configuration file. -->
                      <build.profile.id>default</build.profile.id>
                      <!-- Only unit tests are run by default. -->
                      <skip.integration.tests>true</skip.integration.tests>
                      <skip.unit.tests>false</skip.unit.tests>
                      <integration.test.files>**/*IT.java</integration.test.files>
                  </properties>
                  <profiles>
                      <profile>
                          <id>default</id>
                          <activation>
                              <activeByDefault>true</activeByDefault>
                          </activation>
                          <properties>
                              <!--
                                  Specifies the build profile id, which is used to find out the correct properties file.
                                  This is not actually necessary for this example, but it can be used for other purposes.
                              -->
                              <build.profile.id>default</build.profile.id>
                              <skip.integration.tests>true</skip.integration.tests>
                              <skip.unit.tests>false</skip.unit.tests>
                          </properties>
                          <build>
                              <filters>
                                  <!--
                                      Specifies path to the properties file, which contains profile specific
                                      configuration. In this case, the configuration file should be the default spring/database.properties file
                                  -->
                                  <filter>src/main/resources/META-INF/spring/database.properties</filter>
                              </filters>
                              <resources>
                                  <!--
                                      Placeholders found from files located in the configured resource directories are replaced
                                      with values found from the profile specific configuration files.
                                  -->
                                  <resource>
                                      <filtering>true</filtering>
                                      <directory>src/main/resources</directory>
                                      <!--
                                          You can also include only specific files found from the configured directory or
                                          exclude files. This can be done by uncommenting following sections and adding
                                          the configuration under includes and excludes tags.
                                      -->
                                      <!--
                                      <includes>
                                          <include></include>
                                      </includes>
                                      <excludes>
                                          <exclude></exclude>
                                      </excludes>
                                      -->
                                  </resource>
                              </resources>
                          </build>
                      </profile>
                      <profile>
                          <id>integration</id>
                          <properties>
                              <!--
                                  Specifies the build profile id, which is used to find out the correct properties file.
                                  This is not actually necessary for this example, but it can be used for other purposes.
                              -->
                              <build.profile.id>integration</build.profile.id>
                              <skip.integration.tests>false</skip.integration.tests>
                              <skip.unit.tests>true</skip.unit.tests>
                          </properties>
                          <build>
                              <filters>
                                  <!--
                                      Specifies path to the properties file, which contains profile specific
                                      configuration. In this case, the configuration file is searched
                                      from spring/profiles/it/ directory.
                                  -->
                                  <filter>src/main/resources/META-INF/spring/profiles/${build.profile.id}/database.properties</filter>
                              </filters>
                              <resources>
                                  <!--
                                      Placeholders found from files located in the configured resource directories are replaced
                                      with values found from the profile specific configuration files.
                                  -->
                                  <resource>
                                      <filtering>true</filtering>
                                      <directory>src/main/resources</directory>
                                      <!--
                                          You can also include only specific files found from the configured directory or
                                          exclude files. This can be done by uncommenting following sections and adding
                                          the configuration under includes and excludes tags.
                                      -->
                                      <!--
                                      <includes>
                                          <include></include>
                                      </includes>
                                      <excludes>
                                          <exclude></exclude>
                                      </excludes>
                                      -->
                                  </resource>
                              </resources>
                          </build>
                      </profile>
                  </profiles>
                  

                  借助用于单元测试的surefire和用于集成测试的failsfe,您就完成了。

                      <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-surefire-plugin</artifactId>
                      <version>2.12</version>
                      <configuration>
                          <junitArtifactName>org.junit:com.springsource.org.junit</junitArtifactName>
                          <!--see: https://issuetracker.springsource.com/browse/EBR-220-->
                          <printSummary>false</printSummary>
                          <redirectTestOutputToFile>true</redirectTestOutputToFile>
                          <!-- Skips unit tests if the value of skip.unit.tests property is true -->
                          <skipTests>${skip.unit.tests}</skipTests>
                          <!-- Excludes integration tests when unit tests are run. -->
                          <excludes>
                              <exclude>${integration.test.files}</exclude>
                          </excludes>
                      </configuration>
                  </plugin>
                  
                  
                  <plugin>
                      <groupId>org.apache.maven.plugins</groupId>
                      <artifactId>maven-failsafe-plugin</artifactId>
                      <version>2.12</version>
                      <configuration>
                          <!-- Skips integration tests if the value of skip.integration.tests property is true -->
                          <skipTests>${skip.integration.tests}</skipTests>
                          <includes>
                              <include>${integration.test.files}</include>
                          </includes>
                          <forkMode>once</forkMode>
                          <!--
                                              <reuseForks>false</reuseForks>
                                              <forkCount>1</forkCount>
                          -->
                      </configuration>
                      <executions>
                          <execution>
                              <id>integration-test</id>
                              <goals>
                                  <goal>integration-test</goal>
                              </goals>
                          </execution>
                          <execution>
                              <id>verify</id>
                              <goals>
                                  <goal>verify</goal>
                              </goals>
                          </execution>
                      </executions>
                  </plugin>
                  

                  现在您只需要 mvn test 进行单元测试和 mvn verify -Pintegration 进行集成测试。 显然,您应该在指定(在配置文件上)路径(或其他地方并更改路径)中创建 database.properties 文件

                  基于参考:http://www.petrikainulainen.net/programming/tips-and-tricks/creating-profile-specific-configuration-files-with-maven/

                  【讨论】:

                    【解决方案17】:

                    我发现了 2 种可能性,而无需更改类加载器/使用其他 Maven 插件/配置文件/复制覆盖文件。

                    TL;DR:检查提供商名称

                    起初我开始以编程方式构建 entityManagerFactory,如下所示:create entity manager programmatically without persistence file

                    所以我做了非常相似的事情:

                        @BeforeClass
                        public static void prepare() {
                            Map<String, Object> configOverrides = new HashMap<>();
                            configOverrides.put("hibernate.connection.driver_class", "org.h2.Driver");
                            configOverrides.put("hibernate.connection.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1");
                            configOverrides.put("hibernate.connection.username", "sa");
                            configOverrides.put("hibernate.connection.password", "sa");
                            configOverrides.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
                            configOverrides.put("hibernate.show_sql", "true");
                            configOverrides.put("hibernate.hbm2ddl.auto", "validate");
                            factory = new HibernatePersistence().createContainerEntityManagerFactory(
                                    new CustomPersistenceUnitInfo(), configOverrides
                            );
                            //factory = Persistence.createEntityManagerFactory("test");
                            assertNotNull(factory);
                        }
                    ...
                        private static class CustomPersistenceUnitInfo implements PersistenceUnitInfo {
                    
                            @Override
                            public String getPersistenceUnitName() {
                                return "test";
                            }
                    
                            @Override
                            public String getPersistenceProviderClassName() {
                                return "org.hibernate.jpa.HibernatePersistenceProvider";
                     // <------------note here: this is wrong!
                            }
                    
                            @Override
                            public PersistenceUnitTransactionType getTransactionType() {
                                return PersistenceUnitTransactionType.RESOURCE_LOCAL;
                            }
                    
                            @Override
                            public DataSource getJtaDataSource() {
                                return null;
                            }
                    
                            @Override
                            public DataSource getNonJtaDataSource() {
                                return null;
                            }
                    
                            @Override
                            public List<String> getMappingFileNames() {
                                return Collections.emptyList();
                            }
                    
                            @Override
                            public List<URL> getJarFileUrls() {
                                try {
                                    return Collections.list(this.getClass()
                                            .getClassLoader()
                                            .getResources(""));
                                } catch (IOException e) {
                                    throw new UncheckedIOException(e);
                                }
                            }
                    
                            @Override
                            public URL getPersistenceUnitRootUrl() {
                                return null;
                            }
                    
                            @Override
                            public List<String> getManagedClassNames() {
                                return Arrays.asList(
                                        "com.app.Entity1",
                                        "com.app.Entity2"
                                );
                            }
                    
                            @Override
                            public boolean excludeUnlistedClasses() {
                                return true;
                            }
                    
                            @Override
                            public SharedCacheMode getSharedCacheMode() {
                                return null;
                            }
                    
                            @Override
                            public ValidationMode getValidationMode() {
                                return null;
                            }
                    
                            @Override
                            public Properties getProperties() {
                                return null;
                            }
                    
                            @Override
                            public String getPersistenceXMLSchemaVersion() {
                                return null;
                            }
                    
                            @Override
                            public ClassLoader getClassLoader() {
                                return null;
                            }
                    
                            @Override
                            public void addTransformer(final ClassTransformer classTransformer) {
                    
                            }
                    
                            @Override
                            public ClassLoader getNewTempClassLoader() {
                                return null;
                            }
                        }
                    
                    
                    

                    然后,我发现它仍然返回null。为什么?

                    然后我发现当我使用com.hibernate.ejb.HibernatePersistence类的时候,provider应该不是com.hibernate.jpa.HibernatePersistenceProvider,而是com.hibernate.ejb.HibernatePersistenceHibernatePersistenceProvider 类甚至在 IDEA“开放类”中都找不到,即使它在主 persistence.xml 中也是如此。

                    Ejb3Configuration.class我发现:

                            integration = integration != null ? Collections.unmodifiableMap(integration) : CollectionHelper.EMPTY_MAP;
                            String provider = (String)integration.get("javax.persistence.provider");
                            if (provider == null) {
                                provider = info.getPersistenceProviderClassName();
                            }
                    
                            if (provider != null && !provider.trim().startsWith(IMPLEMENTATION_NAME)) { // private static final String IMPLEMENTATION_NAME = HibernatePersistence.class.getName(); which, is, "com.hibernate.ejb.HibernatePersistence"
                                LOG.requiredDifferentProvider(provider);
                                return null;
                            } else {
                    

                    所以我回到persistence.xml 的第一个解决方案,并更改提供者名称,现在它可以工作了。似乎即使 main 中的提供者也是jpa.xxx,在测试中它不是。

                    因此,总而言之,需要检查 3 件事:

                    • 在 Maven 中打开 -X 以检查 maven-resources-plugin 是否真的将您的 src/test/resources/META-INF/persistence.xml 复制到 target/test-classes(我认为这永远不会失败)
                    • 检查hibernate-entitymanager是否在你的类路径中(你可以用mvn dependency:tree -Dincludes=org.hibernate:hibernate-entitymanager检查。
                    • 检查提供商的名称,最重要的一项。应该是org.hibernate.ejb.HibernatePersistence
                    <persistence version="2.0"
                                 xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                                 xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
                        <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">
                            <provider>org.hibernate.ejb.HibernatePersistence</provider>
                            <class>com.app.model.Company</class>
                            ...
                    
                    

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 2020-04-10
                      • 1970-01-01
                      • 2014-02-24
                      • 2012-12-12
                      • 1970-01-01
                      • 2012-01-16
                      • 2021-10-30
                      相关资源
                      最近更新 更多