我们有一个JPA应用程序(使用hibernate),我们需要将调用传递给需要JDBC数据库连接作为参数的旧报告工具。是否有一种简单的方法可以访问hibernate设置的JDBC连接?
答案 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");
}
如上面的链接所述,它有点简单。
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是否已经使用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;
}
}