我用@Transactional readonly = true注释了我的服务方法.
从那以后,spring / hibernate就不会调用jdbc连接驱动的setReadonly方法了.我能做什么?
因为我将使用主从复制,并且jdbc池使用连接上的readonly标志将查询路由到主服务器或从服务器.
最佳答案 首先,当PU事务模式为RESOURCE_LOCAL时,您应该只将readOnly标志设置为JDBC连接.如果它是JTA,那么你不能改变那个设置,因为你不会为事务中的每个jdbc调用获得相同的jdbc连接实例(JTA – 而不是Hibernate – 将确保事务行为).当它是LOCAL时,Hibernate在第一次需要它时打开一个jdbc连接,并在事务持续期间保持它.
1. JPA
如果您将JPA与Hibernate一起用作提供程序,则可以添加此额外行为
将您自己的JpaDialect实现提供给EMF定义.使用Hibernate时,通常会注入HibernateJpaDialect.
JpaDialect接口有一个getJdbcConnection(em,readOnly)方法,该方法返回实际JDBC连接的句柄.事务开始时,JpaTransactionManager调用此方法.默认情况下,由于此JTA / RESOURCE_LOCAL二元性,HibernateJpaDialect不会更改返回连接的readOnly设置,但如果仅运行本地事务,则可以执行此操作.
以下是实现此目标的JpaDialect的实现:
ResourceLocalReadOnlyAwareHibernateJpaDialect
public class ResourceLocalReadOnlyAwareHibernateJpaDialect extends HibernateJpaDialect {
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly) throws PersistenceException, SQLException {
Session session = getSession(entityManager);
return new HibernateReadOnlyAwareConnectionHandle(session, readOnly);
}
// this is similar to spring's HibernateJpaDialect own internal class,
// except for the readonly flags.
private static class HibernateReadOnlyAwareConnectionHandleimplements ConnectionHandle {
private final Session session;
private final boolean readOnly;
private static volatile Method connectionMethod;
public HibernateConnectionHandle(Session session, boolean readOnly) {
this.session = session;
this.readOnly = readOnly;
}
public Connection getConnection() {
try {
if (connectionMethod == null) {
// reflective lookup to bridge between Hibernate 3.x and 4.x
connectionMethod = this.session.getClass().getMethod("connection");
}
Connection con = (Connection) ReflectionUtils.invokeMethod(connectionMethod, this.session);
con.setReadOnly(this.readOnly);
return con;
} catch (NoSuchMethodException ex) {
throw new IllegalStateException("Cannot find connection() method on Hibernate session", ex);
}
}
public void releaseConnection(Connection con) { // #1
con.setReadOnly(false);
JdbcUtils.closeConnection(con);
}
}
}
注意#1:在关闭连接之前将readOnly标志重置为false(实际上不是真正的connection.close()调用,而只是释放与池的连接).不太确定是什么触发了这个方法调用,但重置readOnly标志在与更改它的位置相同的类中看起来是合法的.
2.纯净的冬眠
首先,确保HibernateTransactionManager.prepareConnection保持为true.
然后,我不知道该怎么做.你必须调试Spring的HibernateTransactionManager.isSameConnectionForEntireSession():如果方法返回true,将调用connection.setReadOnly(),因此一切正常.
如果没有,您可以将Hibernate的connectionReleaseMode设置更改为ON_CLOSE(hibernate属性hibernate.transaction.auto_close_session = true,这是Hibernate 3.1之前的默认设置),或者覆盖HibernateTransactionManager.isSameConnectionForEntireSession()以便始终返回true(这被认为是安全的)关于HibernateTransactionManager评论).两者都是“高级调音”,但应该是安全的AFAIK.实际上,我认为HibernateTransactionManager.isSameConnectionForEntireSession()应该更改为对于ON_CLOSE和AFTER_TRANSACTION释放模式都返回true:对于HibernateTransactionManager,在事务完成后无论如何都会发生清理,因此不会改变Hibernate行为.