69

有没有办法在 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 配置的部署版本,最好是在项目签出后直接进行,无需进行任何本地调整。

4

17 回答 17

26

以下将适用于 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>
于 2009-07-24T20:01:13.787 回答
21

在 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文件的位置也决定了所谓的“持久化单元的根”,它决定了扫描哪些类来查找@Entity注释。因此, using/src/test/resources/META-INF/persistence.xml将扫描 中的类target/test-classes,而不是中的类target/classes(需要测试的类将存在的地方)。

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

作为一种替代方案,人们可​​能能够persistence.xml为实际应用程序和测试保持相同,并且只在src/main. 大多数配置(例如驱动程序、方言和可选凭据)都可以在 Spring 上下文中完成。hibernate.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>
于 2010-06-01T11:55:40.120 回答
13

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

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

于 2008-12-22T05:34:30.427 回答
11

为测试添加一个persistance.xml:/src/test/resources/META-INF/persistence.xml 正如@Arjan 所说,这将改变持久性单元的根,实体类将在目标/测试类中进行扫描。要处理这个问题,请将jar-file元素添加到这个 persistence.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 文件。

于 2015-12-06T08:52:44.007 回答
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 测试,这对我来说是可以接受的。

于 2011-03-31T15:38:41.453 回答
6

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

但需要改变:

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

于 2009-12-11T18:50:28.533 回答
4

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

@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。这行得通。

于 2014-11-04T11:11:51.467 回答
3

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

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

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

于 2008-12-22T04:18:39.750 回答
3

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

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

于 2009-06-22T10:41:56.680 回答
2

这是 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
  • 构建和测试都按预期工作
  • 描述控制台上的输出(回声)
  • 对于包装,状态是 100% 恢复的。没有剩余文件
于 2016-02-23T14:13:29.027 回答
1

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

于 2013-06-28T13:13:43.987 回答
1

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

我在网上看到一篇文章,他们使用自定义类加载器来做类似的事情,这给了我灵感。如果有人可以看到如何改进,那么欢迎提出建议。对于部署,我依赖于 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 之前加载,但是根据您的单元测试结构,您可能还需要将相同的代码放在其他地方(呸)。

于 2009-07-21T12:02:20.120 回答
1

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

测试 persistence.xml 需要包含

<property name="hibernate.ejb.resource_scanner" value = "...TestScanner" />

新类 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;
}

}
于 2012-05-08T15:01:52.853 回答
1

此用例的另一种选择是添加多个持久性单元,一个用于生产,另一个用于测试并相应地注入 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;
    }
}
于 2016-04-19T08:38:11.067 回答
0

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

于 2009-09-21T06:35:40.733 回答
0

我建议使用不同的 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/

于 2014-10-01T14:11:39.967 回答
0

我发现了 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.HibernatePersistenceclass的时候,provider应该不是com.hibernate.jpa.HibernatePersistenceProvider,而是com.hibernate.ejb.HibernatePersistenceHibernatePersistenceProvider即使在主persistence.xml中,IDEA“Open Class”也找不到该类。

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.xmltarget/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>
        ...

于 2021-09-13T10:55:59.787 回答