在纯JPA设置中获取数据库连接

时间:2010-08-16 13:20:27

标签: java hibernate orm jpa jdbc

我们有一个JPA应用程序(使用hibernate),我们需要将调用传递给需要JDBC数据库连接作为参数的旧报告工具。是否有一种简单的方法可以访问hibernate设置的JDBC连接?

12 个答案:

答案 0 :(得分:52)

根据hibernate文档here

  

连接连接()

     

<强>已过时即可。 (计划在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
    }
});

答案 1 :(得分:51)

您想要获得该连接的位置尚不清楚。一种可能性是从Session使用的底层Hibernate EntityManager中获取它。使用JPA 1.0,您必须执行以下操作:

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

请注意,getDelegate()不可移植,此方法的结果是特定于实现的:上面的代码适用于JBoss,对于GlassFish,您必须对其进行调整 - 查看Be careful while using EntityManager.getDelegate()

在JPA 2.0中,事情要好一些,您可以执行以下操作:

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

如果您在容器内运行,您还可以对配置的DataSource执行查找。

答案 2 :(得分:27)

如果您使用的是JAVA EE 5.0,最好的方法是使用@Resource注释将数据源注入类的属性(例如EJB)以保存数据源资源(例如Oracle)数据源)旧版报告工具,这样:

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

稍后您可以获取连接,并以这种方式将其传递给旧版报告工具:

Connection conn = dataSource.getConnection();

答案 3 :(得分:22)

如果您使用EclipseLink: 您应该在JPA事务中访问Connection

entityManager.getTransaction().begin();
java.sql.Connection connection = entityManager.unwrap(java.sql.Connection.class);
...
entityManager.getTransaction().commit();

答案 4 :(得分:14)

由于@Paccal建议的代码已被@Jacob所提及弃用,我发现this another way对我有用。

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

答案 5 :(得分:8)

Hibernate 4/5

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

答案 6 :(得分:5)

单词 pure 与单词 hibernate 不匹配。

我正在分享我的代码。

我们可以说我们可以定义一种使用从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

如上面的链接所述,它有点简单。

  • 请注意,EntityManager必须加入Transaction,否则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");
}

休眠

基本上应该使用以下代码完成。

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

好吧,我们(至少我)可能不想要任何特定于供应商的依赖项。 Proxy来救援。

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),但可以按照上述链接之一的方式完成。

不清楚关闭连接的责任。该文件(上述链接之一)似乎说清楚,但我不擅长英语。

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

答案 7 :(得分:2)

Hibernate在内部使用ConnectionProvider来获取连接。来自hibernate的javadoc:

  

ConnectionProvider接口不应向应用程序公开。相反,Hibernate在内部使用它来获取连接。

解决这个问题的更优雅的方法是自己创建一个数据库连接池,并从那里手动连接到hibernate和你的遗留工具。

答案 8 :(得分:1)

我今天遇到了这个问题,这就是我做的伎俩,对我有用:

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

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

虽然不是最好的方式,但能胜任。

答案 9 :(得分:1)

我正在使用旧版本的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 Controller中(你不能在事务中commit)。我用@TransactionAttribute(value = TransactionAttributeType.NOT_SUPPORTED)注释了方法,问题就消失了。

答案 10 :(得分:0)

这是一个代码片段,可以根据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;
    }

}

答案 11 :(得分:0)

以下是适合我的代码。我们使用jpa 1.0,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;

        }

}