在共享 JAR 的 persistence.xml 中使用 EAR 级别定义的数据源作为 JTA 数据源

Using EAR-level defined data source as JTA data source in persistence.xml of a shared JAR

在 Jakarta EE 8 环境中:是否可以在 application.xml 中的 EAR 级别 [1] 定义(“便携式 JNDI”)数据源并将此数据源用作 JTA 数据source in the persistence.xml inside a library/JAR module?

目的:创建一个通用的 JAR 模块,它定义了 JPA 实体以及相应的“存储库”,以便这个 JAR 模块可以被多个 WAR 模块使用(例如 RESTful API 和 UI 模块)并将这些模块打包为可部署到 多个应用程序服务器 .

的 EAR

使用以下 attempt/method(作为完整示例,我创建了一个简单的 git 存储库 [2]),此类 EAR 的部署失败(至少对于 Payara 和野蝇)。


Attempt/Method

假设有一个应用程序由 2 个 WAR 模块组成,并且两个 WAR 模块都使用一个共享的 JAR 模块,因此应用程序结构如下所示:

ear/
├── shared-lib-jar
|   ├── ...
|   └── META-INF
|       ├── ...
|       └── persistence.xml
├── api-war/
|   └── ...
├── ui-war/
|   └── ...
├── application.xml
├── ...

在 EAR 的 application.xml 中,数据源定义如下:

<application>
  <!-- ... -->
  <data-source>
    <name>java:app/appDS</name>
    <!-- ... -->
  </data-source>
</application>

persistence.xml 中,application.xml 中定义的 JNDI 名称用作 JTA 数据源:

<persistence>
  <persistence-unit name="..." transaction-type="JTA">
    <jta-data-source>java:app/appDS</jta-data-source>
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <!-- ... -->
  </persistence-unit>
</persistence>

unexpected/faulty behavior/situation 具有不同的 (2) 个应用程序服务器

设置:org.h2.jdbcx.JdbcDataSource作为class-name和“基于文件”的数据库

帕亚拉 (5.2020.2)

不会创建数据库文件,服务器日志显示:

[2020-07-07T22:56:32.731+0200] [Payara 5.2020] [SEVERE] [AS-DEPLOYMENT-00026] [javax.enterprise.system.tools.deployment.dol] [tid: _ThreadID=168 _ThreadName=admin-thread-pool::admin-listener(11)] [timeMillis: 1594155392731] [levelValue: 1000] [[
  JNDI lookup failed for the resource: Name: foo-core, Lookup: java:app/appDS, Type: javax.sql.DataSource.]]

[2020-07-07T22:56:32.731+0200] [Payara 5.2020] [SEVERE] [] [javax.enterprise.system.core] [tid: _ThreadID=168 _ThreadName=admin-thread-pool::admin-listener(11)] [timeMillis: 1594155392731] [levelValue: 1000] [[
  JNDI lookup failed for the resource: Name: [foo-core], Lookup: [java:app/appDS], Type: [javax.sql.DataSource]]]

野蝇 (1.4.11.Final)

数据库文件已创建,但服务器日志显示:

{"WFLYCTL0062: Composite operation failed and was rolled back. Steps that failed:" => {"Operation step-2" => {"WFLYCTL0080: Failed services" => {"jboss.deployment.unit.\"foo-ear-0.0.1-SNAPSHOT.ear\".WeldStartService" => "Failed to start service
    Caused by: java.lang.IllegalArgumentException: WFLYWELD0037: Error injecting persistence unit into CDI managed bean. Can't find a persistence unit named 'foo-core' in deployment foo-ear-0.0.1-SNAPSHOT.ear for injection point protected javax.persistence.EntityManager com.acme.BookRepository.entityManager"}}}}

1: https://jakarta.ee/specifications/platform/8/platform-spec-8.html#a1688

2: https://gitlab.com/hjoeren/application-level-ds-example

是的,您几乎完成了,至少在 Payara 或 Glasshish 的情况下是这样。您不需要在 application.xml.

公开您的数据源

首先像您所做的那样在 persistence.xml 声明您的数据源:

<?xml version="1.0" encoding="UTF-8"?>
<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="my.PU" transaction-type="JTA">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    **<jta-data-source>jdbc/mydatasource</jta-data-source>**
    <exclude-unlisted-classes>false</exclude-unlisted-classes>
    <properties>
      <property name="eclipselink.logging.level" value="INFO"/>
      <property name="eclipselink.logging.parameters" value="true"/>
      <property name="eclipselink.persistence-context.flush-mode" value="COMMIT"/>
    </properties>
  </persistence-unit>
</persistence>

然后只需使用管理控制台或 asadmin 命令在您的 Payara(或 GF)应用程序服务器上配置适当的连接池和 JDBC 资源。文件 glassfish-resources.xml

示例
$cat glassfish-resources.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">    
    <resources>
        <jdbc-connection-pool connection-creation-retry-interval-in-seconds="30" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" wrap-jdbc-objects="false" res-type="javax.sql.DataSource" `name="mysql_myrootPool"` is-connection-validation-required="true" connection-creation-retry-attempts="10" validate-atmost-once-period-in-seconds="60">
            <property name="User" value="root"/>
            <property name="Password" value="secret"/>
            <property name="URL" value="jdbc:mysql://localhost:3306/mydb?zeroDateTimeBehavior=convertToNull"/>
            <property name="driverClass" value="com.mysql.jdbc.Driver"/>
            <property name="zeroDateTimeBehavior" value="convertToNull"/>
            <property name="characterEncoding" value="utf-8"/>
            <property name="useSSL" value="false"/>
        </jdbc-connection-pool>
        <jdbc-resource enabled="true" `jndi-name="jdbc/mydatasource"` object-type="user" `pool-name="mysql_myrootPool"`/>
    </resources>
$asadmin add-resources glassfish-resources.xml

请注意池、持久性单元和资源的正确名称。

现在您可以像这样在您的 EAR 中包含的任何 EJB 或 WEB 模块中允许的任何地方(EJB、Servlet、拦截器等)注入您的连接

@PersistenceContext(unitName = "my.PU")
private EntityManager em;

终于找到错误了:我把application.xml放错了文件夹。不要将 application.xml 放在根文件夹中,而是必须将它放在 META-INF 文件夹的更深一层,这样项目结构如下所示:

ear/
├── shared-lib-jar
|   ├── ...
|   └── META-INF
|       ├── ...
|       └── persistence.xml
├── api-war/
|   └── ...
├── ui-war/
|   └── ...
├── META-INF/
|   ├── ...
|   └──application.xml
├── ...

为了查看完整示例,我修复了此问题 repo 中的 错误配置