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

我们有一个JPA应用程序(使用hibernate),我们需要传递一个需要JDBC数据库连接作为参数的传统报告工具。 有一个简单的方法来获得访问的JDBC连接hibernate设置?

你想得到的连接不清楚。 一种可能性是从EntityManager使用的底层Hibernate Session获取它。 使用JPA 1.0,你将不得不这样做:

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

请注意, getDelegate()不是可移植的,这个方法的结果是特定于实现的:上面的代码在JBoss中工作,对于GlassFish你必须适应它 – 看看使用EntityManager.getDelegate()时要小心 。

在JPA 2.0中,事情好一点,你可以做以下事情:

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

如果您正在容器中运行,还可以在configuration的DataSource上执行查找。

根据这里的hibernate文档,

连接连接()

已弃用 。 (计划在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 } }); 

如果您使用的是JAVA EE 5.0,那么最好的办法是使用@Resource注解将数据源注入到一个类的属性(例如EJB)中,以保存数据源资源(例如Oracle数据源)传统的报告工具,这样:

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

之后,您可以获取连接,并通过以下方式将其传递给旧版报告工具:

 Connection conn = dataSource.getConnection(); 

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

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

由于@Pascalbuild议的代码已被@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(); 

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

ConnectionProvider接口不打算暴露给应用程序。 而是由Hibernate在内部使用来获取连接。

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

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

  EntityManagerFactory emf = Persistence.createEntityManagerFactory("DAOMANAGER"); EntityManagerem = emf.createEntityManager(); org.hibernate.Session session = ((EntityManagerImpl) em).getSession(); java.sql.Connection connectionObj = session.connection(); 

虽然不是最好的方式,但是做这个工作。

这是一个基于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; } } 

下面是为我工作的代码。 我们使用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; } } 

单词purehibernate这个词不匹配。

我正在分享我的代码。

假设我们可以定义一个使用从EntityManager派生的Connection的方法。

 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获取JDBC连接

这在上面的链接中有点简单。

  • 请注意, EntityManager必须连接到一个Transaction否则unwrap方法将返回null 。 (根本不是一个好的举动。)
  • 我不确定closures连接的责任。
 // --------------------------------------------------------- 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); } }); 

那么,我们(至less我)可能不想要任何供应商特定的依赖关系。 代理进入救援。

 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-1803将EntityManager解包到连接

我不确定OpenJPA已经提供了一种使用unwrap(Connection.class)但是可以用上面的链接描述的方式来完成。

closures连接的责任并不清楚。 文件(上面的链接之一)似乎说得很清楚,但我不擅长英语。

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

我正在使用旧版本的Hibernate(3.3.0)和最新版本的OpenEJB(4.6.0)。 我的解决scheme是:

 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)注解了方法,问题就没有了。