wake-up-neo.com

Datenbankverbindung im reinen JPA-Setup herstellen

Wir haben eine JPA-Anwendung (mit Ruhezustand), und wir müssen einen Aufruf an ein älteres Reporting-Tool übergeben, das eine JDBC-Datenbankverbindung als Parameter benötigt. Gibt es eine einfache Möglichkeit, auf die JDBC-Verbindung zuzugreifen, die der Ruhezustand eingerichtet hat?

53
Jacob

Wo Sie diese Verbindung herstellen möchten, ist unklar. Eine Möglichkeit wäre, es von der zugrunde liegenden Hibernate Session abzurufen, die von der EntityManager verwendet wird. Mit JPA 1.0 müssen Sie Folgendes tun:

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

Beachten Sie, dass getDelegate() nicht portierbar ist, das Ergebnis dieser Methode ist implementierungsspezifisch: Der obige Code funktioniert in JBoss, für GlassFish müssten Sie ihn anpassen - schauen Sie sich Vorsicht beim EntityManager an. getDelegate () .

In JPA 2.0 sind die Dinge etwas besser und Sie können Folgendes tun:

Connection conn = em.unwrap(Session.class).connection();

Wenn Sie sich in einem Container befinden, können Sie auch die konfigurierte DataSource durchsuchen.

49
Pascal Thivent

Wie in der hibernate docs hier

Verbindung Verbindung () 

Veraltet. (für den Ausbau in 4.x vorgesehen). Ersatz hängt vom Bedarf ab; für direkte JDBC-Funktionen verwenden doWork (org.hibernate.jdbc.Work) ...

Verwenden Sie stattdessen die API für den Ruhezustand:

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
    }
});
51
Dominik

Wenn Sie Java EE 5.0 verwenden, können Sie dies am besten mit der @Resource-Annotation tun, um die Datenquelle in ein Attribut einer Klasse (z. B. eine EJB) einzubringen, in der die Datenquellenressource (z. B. eine Oracle-Datenquelle) enthalten ist das Legacy-Reporting-Tool auf folgende Weise:

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

Später können Sie die Verbindung abrufen und auf folgende Weise an das ältere Tool zur Berichterstellung übergeben:

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

wenn Sie EclipseLink verwenden: Sie sollten sich in einer JPA-Transaktion befinden, um auf die Verbindung zuzugreifen

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

Da der von @Pascal vorgeschlagene Code, wie von @Jacob erwähnt, nicht mehr empfohlen wird, habe ich dies auf eine andere Weise gefunden, das für mich funktioniert.

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

Mit Hibernate 4/5

Session session = entityManager.unwrap(Session.class);
session.doWork(connection -> doSomeStuffWith(connection));
7

Das Wort pure stimmt nicht mit dem Wort hibernate überein.

Ich teile meine Codes.

Nehmen wir an, wir können eine Methode zur Verwendung von Connection definieren, die von einer EntityManager abgeleitet ist.

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

Es ist etwas unkompliziert, wie im obigen Link beschrieben.

  • Beachten Sie, dass die Variable EntityManager mit einer Variable Transaction verbunden sein muss, oder die Methode unwrap gibt null zurück. (Überhaupt kein guter Zug.)
  • Ich bin mir nicht sicher, ob ich die Verbindung schließen muss.
// --------------------------------------------------------- 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

Grundsätzlich sollten folgende Codes verwendet werden.

// 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);
    }
});

Nun, wir (zumindest ich) möchten vielleicht keine anbieterspezifischen Abhängigkeiten. Proxy rettet.

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

Ich bin nicht sicher, ob OpenJPA bereits eine Möglichkeit bietet, unwrap(Connection.class) zu verwenden, kann aber auf die in einem der obigen Links beschriebene Weise ausgeführt werden.

Die Verantwortung für das Schließen der Verbindung ist nicht klar. Das Dokument (einer der obigen Links) scheint klar zu sein, aber ich kann Englisch nicht gut.

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");
}

ANNEKTIEREN

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));
}
4
Jin Kwon

Der Ruhezustand verwendet intern einen ConnectionProvider, um Verbindungen zu erhalten. Aus dem Winterschlaf Javadoc:

Die ConnectionProvider-Schnittstelle ist für die Anwendung nicht vorgesehen. Stattdessen wird es intern von Hibernate verwendet, um Verbindungen zu erhalten.

Der elegantere Weg, dies zu lösen, wäre, einen Datenbankverbindungspool selbst einzurichten und Handverbindungen zum Ruhezustand und zu Ihrem alten Tool von dort aus zu erstellen.

2
Kees de Kooter

Ich verwende eine alte Version von Hibernate (3.3.0) mit einer neuesten Version von OpenEJB (4.6.0). Meine Lösung war:

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);
}

Ich hatte danach einen Fehler: 

Commit can not be set while enrolled in a transaction

Da sich dieser Code oben in einem EJB-Controller befand (Sie können keine commit in einer Transaktion verwenden). Ich habe die Methode mit @TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) kommentiert und das Problem war weg. 

1
Dherik

Ich bin heute auf dieses Problem gestoßen und dies war der Trick, den ich gemacht habe, was für mich funktioniert hat:

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

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

Obwohl nicht der beste Weg, aber die Arbeit erledigt.

1

Unten ist der Code, der für mich funktioniert hat. Wir verwenden jpa 1.0, Apache openjpa-Implementierung.

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;

        }

}
0
aborskiy

Hier ist ein Code-Snippet, das auf der Antwort von Dominik mit Hibernate 4 arbeitet

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