web-dev-qa-db-fra.com

Obtenir une connexion de base de données dans une configuration JPA pure

Nous avons une application JPA (utilisant hibernate) et nous devons passer un appel à un outil de reporting hérité qui nécessite une connexion à une base de données JDBC en tant que paramètre. Existe-t-il un moyen simple d’avoir accès à la connexion JDBC configurée par hibernate?

53
Jacob

Il n’est pas clair où vous voulez établir ce lien. Une possibilité serait de l’obtenir à partir de la variable Hibernate Session sous-jacente utilisée par la variable EntityManager. Avec JPA 1.0, vous devrez faire quelque chose comme ceci:

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

Notez que getDelegate() n'est pas portable, le résultat de cette méthode est spécifique à l'implémentation: le code ci-dessus fonctionne dans JBoss. Pour GlassFish, vous devrez l'adapter - regardez Faites attention en utilisant EntityManager. getDelegate () .

Dans JPA 2.0, les choses vont un peu mieux et vous pouvez effectuer les opérations suivantes:

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

Si vous utilisez un conteneur, vous pouvez également effectuer une recherche sur la variable DataSource configurée.

49
Pascal Thivent

Selon la hibernate docs here

Connexion connexion () 

Obsolète. (prévu pour le retrait dans 4.x). Le remplacement dépend du besoin. pour utiliser directement des éléments JDBC doWork (org.hibernate.jdbc.Work) ...

Utilisez plutôt l'API Hibernate Work:

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

Si vous utilisez Java EE 5.0, la meilleure solution consiste à utiliser l'annotation @Resource pour injecter la source de données dans un attribut d'une classe (par exemple, un EJB) afin de contenir la ressource de source de données (par exemple, une source de données Oracle). l'outil de reporting hérité, de cette façon:

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

Vous pouvez ensuite obtenir la connexion et la transmettre à l'outil de génération de rapports hérité de la manière suivante:

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

si vous utilisez EclipseLink: Vous devez être dans une transaction JPA pour accéder à la connexion.

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

Puisque le code suggéré par @Pascal est obsolète, comme mentionné par @Jacob, j'ai trouvé ceci d'une autre façon cela fonctionne pour moi.

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

Avec Hibernate 4/5

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

Le mot pure ne correspond pas au mot hibernate.

Je partage mes codes.

Supposons que nous puissions définir une méthode d'utilisation de Connection dérivée d'une 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

C'est un peu simple comme décrit dans le lien ci-dessus.

  • Notez que la EntityManager doit être jointe à une Transaction ou que la méthode unwrap renverra null. (Pas un bon coup du tout.)
  • Je ne suis pas sûr de la responsabilité de fermer la connexion.
// --------------------------------------------------------- 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

Cela devrait être fait essentiellement avec les codes suivants.

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

Eh bien, nous (au moins moi) pourrions ne pas souhaiter de dépendances spécifiques à un fournisseur. Proxy vient en sauvetage.

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

Je ne suis pas sûr que OpenJPA serve déjà un moyen en utilisant unwrap(Connection.class) mais peut être fait avec le moyen décrit dans l'un des liens ci-dessus.

Ce n'est pas clair la responsabilité de fermer la connexion. Le document (un des liens ci-dessus) semble dire clairement mais je ne suis pas bon en anglais.

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

ANNEXE

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

Hibernate utilise un ConnectionProvider en interne pour obtenir des connexions. Du hibernate javadoc:

L'interface ConnectionProvider n'est pas destinée à être exposée à l'application. Au lieu de cela, il est utilisé en interne par Hibernate pour obtenir des connexions.

La façon la plus élégante de résoudre ce problème serait de créer vous-même un pool de connexions de base de données et des connexions manuelles pour l'hibernation et votre outil hérité à partir de là.

2
Kees de Kooter

J'utilise une ancienne version d'Hibernate (3.3.0) avec une version plus récente d'OpenEJB (4.6.0). Ma solution était:

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

J'ai eu une erreur après ça: 

Commit can not be set while enrolled in a transaction

Parce que ce code ci-dessus était à l'intérieur d'un contrôleur EJB (vous ne pouvez pas commit dans une transaction). J'ai annoté la méthode avec @TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED) et le problème avait disparu. 

1
Dherik

J'ai rencontré ce problème aujourd'hui et c'est le truc que j'ai fait et qui a fonctionné pour moi:

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

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

Bien que ce ne soit pas la meilleure façon mais fait le travail.

1
Aneesh Vijendran

Voici le code qui a fonctionné pour moi. Nous utilisons jpa 1.0, la mise en œuvre 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
aborskiy

Voici un extrait de code qui fonctionne avec Hibernate 4 en fonction de la réponse de Dominik

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