繁体   English   中英

春天:@Transactional与readonly = true不调用conn.setReadOnly(true)

[英]Spring: @Transactional with readonly=true not call conn.setReadOnly(true)

我已经用@Transactional readonly=true注释了我的服务方法。

从那个弹簧/休眠开始,它不调用jdbc连接驱动程序的setReadonly方法。 我能做什么?

因为我将使用主从复制,并且jdbc池在连接上使用readonly标志将查询路由到主服务器或从服务器。

首先,仅当PU事务模式为RESOURCE_LOCAL时,才应将readOnly标志设置为JDBC连接。 如果是JTA,则您不得更改该设置,因为在事务中的每个jdbc调用中都不会获得相同的jdbc连接实例(JTA-而非Hibernate-将确保事务行为)。 当它为LOCAL时,Hibernate会在第一次需要它时打开一个jdbc连接,并在事务期间将其保留。

1. JPA

如果您将JPA与Hibernate用作提供程序,则可以通过向EMF定义提供自己的JpaDialect实现来添加此额外行为。 使用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行为。

这里提到了两个值得研究的解决方案: http : //www.dragishak.com/?p=307

  1. 使用AOP将JDBC连接设置为readOnly。 这是博客文章的重点。
  2. 使用连接池中的挂钩基于TransactionSynchronizationManager.isCurrentTransactionReadOnly()选择不同的连接。 这取决于您所使用的连接池实现(例如BoneCP,c3p0,不确定DBCP是否支持该实现)。 请参阅上方链接的评论部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM