在纯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; } }
单词pure与hibernate这个词不匹配。
我正在分享我的代码。
假设我们可以定义一个使用从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)
注解了方法,问题就没有了。