每次在Hibernate中动态创建EntityManager /连接到自定义主机/数据库

问题描述 投票:1回答:2

我有一个在Websphere Liberty上运行的应用程序,该应用程序应该比较2个数据库/方案中的表。

用户应该能够输入连接数据,例如主机和凭据。

我正在使用Hibernate访问应用程序数据库。

我尝试使用多个持久性单元,一个用于应用程序数据库,一个用于所有其他数据库。

但是我有两个问题:

  1. 我得到“非法尝试征募多个1PC XAResources”有时会出现错误
  2. 可以查询两个数据库用户提交的凭据,但除非连接,否则我没有任何结果到server.xml文件中列出的与DataSource相同的数据库

这是服务器上server.xml上的数据源(dbs是oracle dbs)

<dataSource id="MyAppDS" jndiName="jdbc/MyDS" type="javax.sql.ConnectionPoolDataSource">
    <jdbcDriver javax.sql.ConnectionPoolDataSource="oracle.jdbc.pool.OracleConnectionPoolDataSource" libraryRef="OracleSQLLib"/>
    <connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/>
    <properties password="..." url="jdbc:oracle:thin:@...:1521:..." user="..."/>
</dataSource>
<dataSource id="OtherOracle" jndiName="jdbc/OtherOracle" type="javax.sql.ConnectionPoolDataSource">
    <jdbcDriver javax.sql.ConnectionPoolDataSource="oracle.jdbc.pool.OracleConnectionPoolDataSource" libraryRef="OracleSQLLib"/>
    <connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/>
    <properties password="..." url="jdbc:oracle:thin:@127.0.0.1:1521:XE" user="..."/>
</dataSource>

这是EJB模块上的persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="main-persistence">
    <jta-data-source>jdbc/MyDS</jta-data-source>

    <class>classes...</class>

    <properties>
        <property name="hibernate.transaction.jta.platform"
            value="org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform" />
        <property name="hibernate.dialect"
            value="org.hibernate.dialect.Oracle9iDialect" />
        <property name="hibernate.temp.use_jdbc_metadata_defaults"
            value="false" />
    </properties>
</persistence-unit>
<persistence-unit name="other-persistence" transaction-type="RESOURCE_LOCAL">
    <non-jta-data-source>jdbc/OtherOracle</non-jta-data-source>
    <class>classes...</class>

    <properties>
        <property name="hibernate.transaction.jta.platform"
            value="org.hibernate.service.jta.platform.internal.WebSphereExtendedJtaPlatform" />
        <property name="hibernate.dialect"
            value="org.hibernate.dialect.Oracle9iDialect" />
        <property name="hibernate.temp.use_jdbc_metadata_defaults"
            value="false" />
    </properties>
</persistence-unit>

在Java Bean上,我使用EntityManagerFactory

    @PersistenceUnit(unitName = "other-persistence")
    private EntityManagerFactory emf;

并且我使用这样的自定义凭据创建实体管理器

    Map<String, String> properties = new HashMap<String, String>();
    properties.put("hibernate.connection.driver_class", "oracle.jdbc.OracleDriver");
    properties.put("hibernate.connection.url", myCustomCreatedConnectionUrl);
    properties.put("hibernate.connection.username", customUser);
    properties.put("hibernate.connection.password", customPassword);
    properties.put("hibernate.dialect", "org.hibernate.dialect.Oracle9iDialect");
    properties.put("hibernate.show-sql", "true");
    EntityManager entityManager = emf.createEntityManager(properties);

如果我使用getProperties检查EntityManager属性,一切似乎都是正确的。但是,仅当凭据/主机为数据源=时查询才有效。否则我没有结果(但没有错误)

问题可能是什么?有没有一种方法可以只使用一个持久性单元,但可以对不同的查询使用自定义的主机/凭据?

java hibernate entitymanager websphere-liberty
2个回答
0
投票

关于第一个错误,““非法尝试征用多个1PC XAResources”,这是因为您正在同一事务中使用这两个资源。在配置中可以看到<non-jta-data-source>jdbc/OtherOracle</non-jta-data-source>,这表明您可能打算用于jdbc / OtherOracle是非登记资源。要使其正常工作,需要将数据源本身配置为非登记资源。您可以使用transactional="false"属性执行以下操作:

<dataSource id="OtherOracle" jndiName="jdbc/OtherOracle" type="javax.sql.ConnectionPoolDataSource" transactional="false">
...

另一方面,如果您实际上希望两种资源都参与事务,那么您需要使用XADataSource而不是ConnectionPoolDataSource。这是如何执行此操作的示例(请注意,都必须为此同时更新type下的dataSourcejdbcDriver下的属性和类)。<dataSource id="MyAppDS" jndiName="jdbc/MyDS" type="javax.sql.XADataSource"> <jdbcDriver javax.sql.XADataSource="oracle.jdbc.xa.client.OracleXADataSource" libraryRef="OracleSQLLib"/> <connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/> <properties password="..." url="jdbc:oracle:thin:@...:1521:..." user="..."/> </dataSource> <dataSource id="OtherOracle" jndiName="jdbc/OtherOracle" type="javax.sql.XADataSource"> <jdbcDriver javax.sql.XADataSource="oracle.jdbc.xa.client.OracleXADataSource" libraryRef="OracleSQLLib"/> <connectionManager agedTimeout="30m" connectionTimeout="10s" maxPoolSize="20" minPoolSize="5"/> <properties password="..." url="jdbc:oracle:thin:@127.0.0.1:1521:XE" user="..."/> </dataSource>

[在第二个问题中,我认为您是在说不同的用户看不到数据。难道是因为不同的数据库用户使用的是不同的架构,并且无法访问彼此的数据吗?如果可以使所有用户使用通用模式,则将@Table(schema="YOUR_SCHEMA_NAME")添加到JPA @Entity可能会有所帮助。表注释的JavaDoc可以找到here

我最近有一个相同的请求,用户可以连接到应用程序并输入其数据库详细信息。从这一点开始,将在此新连接上执行每个JPA查询。

另外一个问题是有100多个库可供选择,在开始时创建所有数据源不是一个好主意。

因此,我们创建了一个用于管理JDBC连接的单个RoutingDatasource。 Spring的实现

org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource

。给我们很大的启发。主要思想是:

    您扩展了AbstractDataSource和
  • getConnection()方法,在每个JPA查询中都会调用该方法。您基本上是在JPA层下修补JDBC层。然后您将根据用户输入的内容创建一个新的数据源
  • 将您的路由数据源设置为您的实体管理器的主要数据源
  • [可选地,添加一些缓存以避免每次都重新创建数据源(或实现一些池)
  • 这里是演示班:
  • public class RoutingDataSource extends AbstractDataSource { ... ... @Override public Connection getConnection() throws SQLException { return determineTargetDataSource().getConnection(); } DataSource determineTargetDataSource() { EmployeeDatabase lookupKey = determineCurrentLookupKey(); DataSource dataSource = this.datasources.get(lookupKey); if (dataSource == null) { logger.debug("Datasource not found. Creating new one"); SQLServerDataSource newDatasource = new SQLServerDataSource(); newDatasource.setURL("jdbc:sqlserver://" + lookupKey.getDatabaseHost()); newDatasource.setPassword(dbPass); datasources.put(lookupKey, newDatasource); dataSource = newDatasource; } else { logger.debug("Found existing database for key " + lookupKey); } logger.debug("Connecting to " + dataSource); return dataSource; } }


0
投票
我最近有一个相同的请求,用户可以连接到应用程序并输入其数据库详细信息。从这一点开始,将在此新连接上执行每个JPA查询。
© www.soinside.com 2019 - 2024. All rights reserved.