Uzyskiwanie połączenia z bazą danych w czystej konfiguracji JPA

Mamy aplikację JPA (używającą hibernate) i musimy przekazać wywołanie do starszego narzędzia do raportowania, które wymaga połączenia z bazą danych JDBC jako parametru. Czy istnieje prosty sposób, aby uzyskać dostęp do połączenia JDBC Hibernate ma skonfigurowany?

Author: Pascal Thivent, 2010-08-16

12 answers

Gdzie chcesz uzyskać to połączenie jest niejasne. Jedną z możliwości jest pobranie go z bazowego Hibernate Session używanego przez EntityManager. Z JPA 1.0 będziesz musiał zrobić coś takiego:

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

Zauważ, że getDelegate() nie jest przenośny, wynik tej metody jest specyficzny dla implementacji: powyższy kod działa w JBoss, dla GlassFish musiałbyś go dostosować-spójrz na bądź ostrożny podczas używania Entitymanagera.getDelegate () .

W JPA 2.0, rzeczy są nieco lepsze i można zrobić następujące:

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

Jeśli uruchamiasz się wewnątrz kontenera, możesz również przeprowadzić wyszukiwanie na skonfigurowanym DataSource.

 48
Author: Pascal Thivent,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 20:50:42

Zgodnie z hibernate docs TUTAJ ,

Connection connection ()

Deprecated . (planowane do usunięcia w 4.x). Wymiana zależy od potrzeby; do robienia bezpośrednich rzeczy JDBC używać doWork(org.hibernacja.jdbc.Pracy)...

Zamiast tego użyj interfejsu 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
    }
});
 50
Author: Dominik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-04-13 10:57:50

Jeśli używasz JAVA EE 5.0, najlepszym sposobem na to jest użycie adnotacji @ Resource, aby wstrzyknąć datasource do atrybutu klasy (na przykład EJB) do przechowywania zasobu datasource (na przykład Oracle datasource) dla starszego narzędzia do raportowania, w ten sposób:

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

Później możesz uzyskać połączenie i przekazać je do starszego narzędzia raportującego w ten sposób:

Connection conn = dataSource.getConnection();
 23
Author: Alberto Vazquez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-28 18:55:31

Jeśli używasz EclipseLink: Powinieneś być w transakcji JPA, aby uzyskać dostęp do połączenia

entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();
 21
Author: armando,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-10 04:12:24

Ponieważ kod sugerowany przez @ Pascal jest przestarzały, jak wspomniał @ Jacob, znalazłem inny sposób , który działa dla mnie.

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();
 13
Author: Luistar15,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-06 09:54:37

Słowo pure nie pasuje do słowa hibernate .

Dzielę się swoimi kodami.

Powiedzmy, że możemy zdefiniować metodę użycia Connection wywodzącą się z 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

Jest to dość proste, jak opisano powyżej link.

  • zauważ, że EntityManager musi być połączone z Transaction lub metoda unwrap zwróci null. (Niezbyt dobry ruch.)
  • Nie jestem pewien, czy powinienem zamknąć połączenie.
// --------------------------------------------------------- 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

Powinno być w zasadzie zrobione za pomocą następujących kodów.

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

Cóż, my (przynajmniej ja) możemy nie chcieć żadnych zależności specyficznych dla dostawcy. Proxy przychodzi na ratunek.

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

Nie jestem pewien, czy OpenJPA już obsługuje sposób używając unwrap(Connection.class), ale można to zrobić w sposób opisany w jednym z powyższych linków.

Nie jest jasne, jaka jest odpowiedzialność za zamknięcie połączenia. Dokument (jeden z powyższych linków) wydaje się wyraźnie mówić, ale nie jestem dobry w języku angielskim.

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

Załącznik

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));
}
 3
Author: Jin Kwon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-27 08:15:24

Z Hibernate 4 / 5:

Asynchroniczny:

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

Syncronous:

Session session = entityManager.unwrap(Session.class);
Connection cnn = session.doReturningWork(c -> c);

Lub pojedyncze:

Connection cnn = em.unwrap(Session.class).doReturningWork(c -> c);
 3
Author: Oleksandr Shpota,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-31 22:06:34

Hibernate używa wewnętrznie ConnectionProvider do uzyskania połączeń. Z Hibernate javadoc:

Interfejs ConnectionProvider nie jest przeznaczony do kontaktu z aplikacją. Zamiast tego jest używany wewnętrznie przez Hibernate do uzyskiwania połączeń.

Bardziej eleganckim sposobem rozwiązania tego problemu byłoby samodzielne utworzenie puli połączeń bazodanowych i ręczne Połączenie z hibernate i z tego narzędzia.

 2
Author: Kees de Kooter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:55:50

Wpadłem dziś na ten problem i to była moja sztuczka, która zadziałała na mnie:

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

   org.hibernate.Session session = ((EntityManagerImpl) em).getSession();
   java.sql.Connection connectionObj = session.connection();
Chociaż nie jest to najlepszy sposób, ale wykonuje swoją pracę.
 1
Author: Aneesh Vijendran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-17 14:43:21

Używam starej wersji Hibernate (3.3.0) z najnowszą wersją OpenEJB (4.6.0). Moje rozwiązanie to:

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

Potem miałem błąd:

Commit can not be set while enrolled in a transaction

Ponieważ powyższy kod znajdował się wewnątrz kontrolera EJB (nie możesz commit wewnątrz transakcji). Opisałem metodę @TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) i problem zniknął.

 1
Author: Dherik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-02 18:17:04

Oto fragment kodu, który działa z Hibernate 4 na podstawie odpowiedzi Dominika

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
Author: uvperez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-27 20:06:09

Poniżej znajduje się kod, który działał dla mnie. Używamy jpa 1.0, implementacji Apache openjpa.

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
Author: aborskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-23 21:02:37