web-dev-qa-db-ja.com

純粋なJPAセットアップでデータベース接続を取得する

JPAアプリケーション(休止状態を使用)があり、JDBCデータベース接続をパラメーターとして必要とするレガシーレポートツールに呼び出しを渡す必要があります。 hibernateがセットアップしたJDBC接続にアクセスする簡単な方法はありますか?

58
Jacob

その接続を取得する場所は不明です。 1つの可能性は、Sessionによって使用される基礎となるHibernate EntityManagerから取得することです。 JPA 1.0では、次のようなことをする必要があります。

_Session session = (Session)em.getDelegate();
Connection conn = session.connection();
_

getDelegate() は移植性がないことに注意してください。このメソッドの結果は実装固有です:上記のコードはJBossで動作します。GlassFishの場合、適応させる必要がありますat EntityManager.getDelegate()の使用中は注意してください

JPA 2.0では、状況は少し改善されており、次のことができます。

_Connection conn = em.unwrap(Session.class).connection();
_

コンテナー内で実行している場合は、構成済みのDataSourceでルックアップを実行することもできます。

51
Pascal Thivent

hibernate docs here に従って

接続connection()

非推奨 (4.xでの削除が予定されています)。交換は必要性に依存します。直接的なJDBC処理を行うには、doWork(org.hibernate.jdbc.Work)を使用します...

代わりにHibernate Work APIを使用します。

Session session = entityManager.unwrap(Session.class);
session.doWork(new Work() {

    @Override
    public void execute(Connection connection) throws SQLException {
        // do whatever you need to do with the connection
    }
});
53
Dominik

Java EE 5.0を使用している場合、これを行う最良の方法は、@ Resourceアノテーションを使用して、データソースを保持するためにクラス(たとえばEJB)の属性にデータソースを挿入することです。レガシーレポートツールのリソース(たとえば、Oracleデータソース)、次の方法:

@Resource(mappedName="jdbc:/OracleDefaultDS") DataSource datasource;

後で接続を取得し、この方法でレガシーレポートツールに渡すことができます。

Connection conn = dataSource.getConnection();
29
Alberto Vazquez

eclipseLinkを使用する場合:Connectionにアクセスするには、JPAトランザクションを開始する必要があります

entityManager.getTransaction().begin();
Java.sql.Connection connection = entityManager.unwrap(Java.sql.Connection.class);
...
entityManager.getTransaction().commit();
22
armando

@Pascalによって提案されたコードは@Jacobによって言及されているように非推奨であるため、私は これとは別の方法 が機能することがわかりました。

import org.hibernate.classic.Session;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.engine.SessionFactoryImplementor;

Session session = (Session) em.getDelegate();
SessionFactoryImplementor sfi = (SessionFactoryImplementor) session.getSessionFactory();
ConnectionProvider cp = sfi.getConnectionProvider();
Connection connection = cp.getConnection();
14
Luistar15

Hibernate 4/5の場合:

Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> doSomeStuffWith(connection));
8
Sasha Shpota

WordpureはWordhibernateと一致しません。

コードを共有しています。

Connectionから派生したEntityManagerを使用するメソッドを定義できるとしましょう。

_static <R> applyConnection(final EntityManager manager,
                           final Function<Connection, R> function) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (function == null) {
        throw new NullPointerException("function is null");
    }

    // we gonna fill here up

    throw new RuntimeException("failed to work with a connection");
}
_

EclipseLink

上記のリンクで説明されているように、それはやや簡単です。

  • EntityManagerTransactionに結合する必要があります。そうしないと、unwrapメソッドはnullを返します。 (まったく良い動きではありません。)
  • 接続を閉じる責任がわかりません。
_// --------------------------------------------------------- EclipseLink
try {
    final Connection connection = manager.unwrap(Connection.class);
    if (connection != null) { // manage is not in any transaction
        return function.apply(connection);
    }
} catch (final PersistenceException pe) {
    logger.log(FINE, pe, () -> "failed to unwrap as a connection");
}
_

Hibernate

基本的には、次のコードで行う必要があります。

_// using vendor specific APIs
final Session session = (Session) manager.unwrap(Session.class);
//return session.doReturningWork<R>(function::apply);
return session.doReturningWork(new ReturningWork<R>() {
    @Override public R execute(final Connection connection) {
        return function.apply(connection);
    }
});
_

さて、私たち(少なくとも私)は、ベンダー固有の依存関係を望まないかもしれません。 プロキシ が助けになります。

_try {
    // See? You shouldn't fire me, ass hole!!!
    final Class<?> sessionClass
            = Class.forName("org.hibernate.Session");
    final Object session = manager.unwrap(sessionClass);
    final Class<?> returningWorkClass
            = Class.forName("org.hibernate.jdbc.ReturningWork");
    final Method executeMethod
            = returningWorkClass.getMethod("execute", Connection.class);
    final Object workProxy = Proxy.newProxyInstance(
            lookup().lookupClass().getClassLoader(),
            new Class[]{returningWorkClass},
            (proxy, method, args) -> {
                if (method.equals(executeMethod)) {
                    final Connection connection = (Connection) args[0];
                    return function.apply(connection);
                }
                return null;
            });
    final Method doReturningWorkMethod = sessionClass.getMethod(
            "doReturningWork", returningWorkClass);
    return (R) doReturningWorkMethod.invoke(session, workProxy);
} catch (final ReflectiveOperationException roe) {
    logger.log(Level.FINE, roe, () -> "failed to work with hibernate");
}
_

OpenJPA

OpenJPAが既にunwrap(Connection.class)を使用する方法を提供しているとは思いませんが、上記のリンクのいずれかで説明されている方法で実行できます。

接続を閉じる責任は明確ではありません。文書(上記のリンクの1つ)ははっきり言っているようですが、私は英語が苦手です。

_try {
    final Class<?> k = Class.forName(
            "org.Apache.openjpa.persistence.OpenJPAEntityManager");
    if (k.isInstance(manager)) {
        final Method m = k.getMethod("getConnection");
        try {
            try (Connection c = (Connection) m.invoke(manager)) {
                return function.apply(c);
            }
        } catch (final SQLException sqle) {
            logger.log(FINE, sqle, () -> "failed to work with openjpa");
        }
    }
} catch (final ReflectiveOperationException roe) {
    logger.log(Level.FINE, roe, () -> "failed to work with openjpa");
}
_

付録

_static <U, R> R applyConnection(
        final EntityManager manager,
        final BiFunction<Connection, U, R> function, final U u) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (function == null) {
        throw new NullPointerException("function is null");
    }
    return applyConnection(manager, t -> function.apply(t, u));
}

static void acceptConnection(
        final EntityManager manager, final Consumer<Connection> consumer) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (consumer == null) {
        throw new NullPointerException("consumer is null");
    }
    applyConnection(
            manager,
            t -> {
                consumer.accept(t);
                return null;
            }
    );
}

static <U> void acceptConnection(
        final EntityManager manager,
        final BiConsumer<Connection, U> consumer, final U u) {
    if (manager == null) {
        throw new NullPointerException("manager is null");
    }
    if (consumer == null) {
        throw new NullPointerException("consumer is null");
    }
    acceptConnection(manager, t -> consumer.accept(t, u));
}
_
5
Jin Kwon

HibernateはConnectionProviderを内部的に使用して接続を取得します。休止状態のjavadocから:

ConnectionProviderインターフェースは、アプリケーションに公開されることを意図していません。代わりに、接続を取得するためにHibernateによって内部的に使用されます。

これを解決するよりエレガントな方法は、データベース接続プールを自分で作成し、そこから休止状態とレガシーツールへの接続を渡すことです。

2
Kees de Kooter

以下は私のために働いたコードです。 Apache openjpa実装であるjpa 1.0を使用します。

import Java.sql.Connection;
import org.Apache.openjpa.persistence.OpenJPAEntityManager;
import org.Apache.openjpa.persistence.OpenJPAPersistence;

public final class MsSqlDaoFactory {


       public static final Connection getConnection(final EntityManager entityManager) {
              OpenJPAEntityManager openJPAEntityManager = OpenJPAPersistence.cast(entityManager);
              Connection connection = (Connection) openJPAEntityManager.getConnection();
              return connection;

        }

}
1
aborskiy

古いバージョンのHibernate(3.3.0)と最新バージョンのOpenEJB(4.6.0)を使用しています。私の解決策は:

_EntityManagerImpl entityManager = (EntityManagerImpl)em.getDelegate();
Session session = entityManager.getSession();
Connection connection = session.connection();
Statement statement = null;
try {
    statement = connection.createStatement();
    statement.execute(sql);
    connection.commit();
} catch (SQLException e) {
    throw new RuntimeException(e);
}
_

その後エラーが発生しました:

_Commit can not be set while enrolled in a transaction
_

上記のコードはEJBコントローラー内にあったためです(トランザクション内でcommitを使用することはできません)。メソッドに@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)と注釈を付けたところ、問題はなくなりました。

1
Dherik

今日、この問題に遭遇しましたが、これが私がやったトリックでした。

   EntityManagerFactory emf = Persistence.createEntityManagerFactory("DAOMANAGER");
   EntityManagerem = emf.createEntityManager();

   org.hibernate.Session session = ((EntityManagerImpl) em).getSession();
   Java.sql.Connection connectionObj = session.connection();

最良の方法ではありませんが、仕事をします。

1

Dominikの答えに基づいてHibernate 4で動作するコードスニペットがあります

Connection getConnection() {
    Session session = entityManager.unwrap(Session.class);
    MyWork myWork = new MyWork();
    session.doWork(myWork);
    return myWork.getConnection();
}

private static class MyWork implements Work {

    Connection conn;

    @Override
    public void execute(Connection arg0) throws SQLException {
        this.conn = arg0;
    }

    Connection getConnection() {
        return conn;
    }

}
0
uvperez