关于java:SE环境下从persistence.xml中解耦一个JPA实体jar

Decouple a JPA entities jar from persistence.xml in SE environment

有没有办法在多个 SE 应用程序中重用带有 JPA 注释实体的 jar(作为依赖项)? SE环境不支持persistence.xml中的,还有其他方法吗?


正式(根据规范),您必须使用 class 元素指定所有类。引用 JSR-220 的第 6.2.1.6 章 mapping-file, jar-file, class, exclude-unlisted-classes:

A list of named managed persistence classes may also be specified instead of, or in addition to, the JAR files and mapping files. Any mapping metadata annotations found on these classes will be processed, or they will be mapped using the mapping annotation defaults. The class element is used to list a managed persistence class. A list of all named managed persistence classes must be specified in Java SE environments to insure portability. Portable Java SE applications should not rely on the other mechanisms described here to specify the managed persistence classes of a persistence unit. Persistence providers may also require that the set of entity classes and classes that are to be managed must be fully enumerated in each of the persistence.xml files in Java SE environments.

现在,如果您不介意不可移植,Hibernate 支持在 Java SE 中使用 jar-file 元素(在这种情况下,需要绝对 url,而不方便)。即使在 JSE 中,Hibernate 实际上也支持自动检测。好多了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<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_1_0.xsd"
  version="1.0">
  <persistence-unit name="foo">

    <!-- This is required to be spec compliant, Hibernate however supports auto-detection even in JSE. -->
    <class>foo.Bar<class>

    <properties>
      <!-- Scan for annotated classes and Hibernate mapping XML files -->
      <property name="hibernate.archive.autodetection" value="class, hbm"/>
      ...
    </properties>
  </persistence-unit>

</persistence>

根据我的经验 - 现在可以了。

我们正在使用:
Hibernate3.jar 3.6.0.Final
hibernate-jpa-2.0-api-1.0.0.Final.jar

< jar-file >file:... 知道如何查找相对路径 - 它适用于 jar 文件或目录。

这个技能我用了两次:

  • 有一个装有我的实体的罐子——它在多个应用程序中使用。每个应用程序都有自己的 persistence.xml - 主要是为了提供不同的 ehcache 设置。
  • 当我想要所有测试时使用 Junits,在所有其他依赖项目中都有一个 persistence.xml 文件,该文件将指向实体项目中的所有实体。然后我们将persistence.xml保存在test/resources/META-INF下的entities项目中,指向该项目的Bin目录:
    file:../entities/bin

据我所知,没有办法让类扫描注释以在该配置中工作。但是,您可以明确地将您的 persistence.xml 文件指向每个实体类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<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_1_0.xsd"
                     version="1.0">

  <persistence-unit name="punit">

    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <jta-data-source>java:/myDS</jta-data-source>

    <!-- Must be explicit as classes are in separate jar -->
    <class>com.foo.Bar</class>
    <class>com.foo.Baz</class>

    <properties/>      
  </persistence-unit>

</persistence>

这是我遇到的一个问题。因为我需要独立运行几个 jar 以及作为 war 部署的一部分,所以稍微更前一点。

那里有一些 hack 似乎围绕着多个 persistence.xml 文件和/或一些看起来很奇怪的尝试使用 spring 资源加载器(这对我不起作用)来引用 jar 文件。

我的个人技巧是使用 spring 资源加载器来解析所有实体 jar 中的资源,解析出 URL jar 引用并使用 Spring 持久性单元管理器将这些注入到虚拟持久性中的 jar-file 标记中.xml

这是一种迂回的做法,但避免了多个persistence.xml——这在技术上是无效的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class SpringPersistenceUnitManager extends DefaultPersistenceUnitManager implements ApplicationContextAware {

private final Logger log = LoggerFactory.getLogger(getClass());

private ApplicationContext ctx = null;


private String jarLocationPattern;

@Override
protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
    super.postProcessPersistenceUnitInfo(pui);
    try {
        Resource[] resources = ctx.getResources("classpath*:applicationContext.xml");
        for (Resource res : resources) {
            String resJar = resolveJar(res.getURL());
            if (!resJar.equals(pui.getPersistenceUnitRootUrl().toString())) {
                log.info("Adding" + resJar +" to persistence context");
                pui.addJarFileUrl(new URL(resJar));
            }
        }
    }
    catch (IOException e) {
        log.error("error", e);
    }
}

private String resolveJar(URL fileInJar) {
    String path = fileInJar.getPath();
    return path.substring(0, path.indexOf('!'));
}

和弹簧上下文的东西:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<util:properties id="hibernate.properties" location="classpath:hibernate.properties" />

<bean id="persistenceUnitManager" class="com.rokksoft.blackice.util.SpringPersistenceUnitManager"
    p:defaultDataSource-ref="jdbcDataSourcePool"
/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" lazy-init="true"
    p:persistenceUnitManager-ref="persistenceUnitManager"
    p:persistenceUnitName="blackicePU"
    p:dataSource-ref="jdbcDataSourcePool"
    p:jpaProperties-ref="hibernate.properties">
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"/>
    </property>
</bean>

您希望理想地过滤 jar 名称 - 第 3 方 jar 可以包含任何内容。