Hibernate,spring,JPA&隔离 - 不支持自定义隔离

时间:2011-03-08 15:06:41

标签: java database hibernate spring jpa

我一直在尝试这个:

@Transactional(isolation=Isolation.SERIALIZABLE, 
               rollbackFor={Exception.class}, 
               propagation=Propagation.REQUIRES_NEW)

我的服务方法,但春天抱怨说:

Standard JPA does not support custom isolation levels - use a special JpaDialect

我该如何解决这个问题?

6 个答案:

答案 0 :(得分:10)

JPA不支持自定义隔离级别。您可以扩展HibernateJpaDialect类并覆盖与连接相关的方法,以便您可以在Connection上设置自定义隔离级别

这是我写过的东西,但还没有测试过:

public class HibernateExtendedJpaDialect extends HibernateJpaDialect {

    @Override
    public Object beginTransaction(EntityManager entityManager,
            TransactionDefinition definition) throws PersistenceException,
            SQLException, TransactionException {

        Session session = (Session) entityManager.getDelegate();
        DataSourceUtils.prepareConnectionForTransaction(session.connection(), definition);

        entityManager.getTransaction().begin();

        return prepareTransaction(entityManager, definition.isReadOnly(), definition.getName());
    }

}

您可以将其定义为EntityManagerFactory

的属性
<property name="jpaDialect">
    <bean class="com.company.util.HibernateExtendedJpaDialect" />
</property>

答案 1 :(得分:10)

这个实现不是清理工作的原因,我已经实现了类似的解决方案,但也考虑了清理。 该解决方案可以在这里找到: http://shahzad-mughal.blogspot.com/2012/04/spring-jpa-hibernate-support-for-custom.html

答案 2 :(得分:3)

借鉴Bozho的答案并考虑其上的评论,以下似乎是一个完整的(Hibernate 4兼容)解决方案,解决了重置连接的需要。最好我可以告诉,spring层将保证调用cleanupTransaction方法,但如果实际上没有保证,可能需要重新考虑,因为permGen内存泄漏的可能性和对连接对象的请求后副作用

public class HibernateExtendedJpaDialect extends HibernateJpaDialect {

  ThreadLocal<Connection> connectionThreadLocal = new ThreadLocal<>();
  ThreadLocal<Integer> originalIsolation = new ThreadLocal<>();

  @Override
  public Object beginTransaction(EntityManager entityManager,
                                 TransactionDefinition definition) 
      throws PersistenceException, SQLException, TransactionException {

    boolean readOnly = definition.isReadOnly();
    Connection connection = 
        this.getJdbcConnection(entityManager, readOnly).getConnection();
    connectionThreadLocal.set(connection);
    originalIsolation.set(DataSourceUtils
        .prepareConnectionForTransaction(connection, definition));

    entityManager.getTransaction().begin();

    return prepareTransaction(entityManager, readOnly, definition.getName());
  }

  /*

   We just have to trust that spring won't forget to call us. If they forget,
   we get a thread-local/classloader memory leak and messed up isolation 
   levels. The finally blocks on line 805 and 876 of 
   AbstractPlatformTransactionManager (Spring 3.2.3) seem to ensure this, 
   though there is a bit of logic between there and the actual call to this 
   method.

   */
  @Override
  public void cleanupTransaction(Object transactionData) {
    try {
      super.cleanupTransaction(transactionData);
      DataSourceUtils.resetConnectionAfterTransaction(
          connectionThreadLocal.get(), originalIsolation.get());
    } finally {
      connectionThreadLocal.remove();
      originalIsolation.remove();
    }
  }
}

答案 3 :(得分:0)

@Shahzad Mughal我给你留了两分;-)你的答案应该被接受为正确答案。接受的答案将产生以下问题随机错过领先的开发人员认为有错误的mysql驱动程序,例如:

  

WARN [org.hibernate.util.JDBCExceptionReporter] - SQL错误:0,   SQLState:S1009 ERROR [org.hibernate.util.JDBCExceptionReporter] -   连接是只读的。导致数据修改的查询不是   允许

您可以在http://thinkinginsoftware.blogspot.com/2013/10/connection-is-read-only-queries-leading.html

了解有关此问题的更多信息

答案 4 :(得分:-1)

指定JpaTransactionManager时是否指定了JPADialect?默认情况下,我认为它使用的是DefaultJpaDialect,您需要HibernateJpaDialect

答案 5 :(得分:-1)

您也可以使用“IsolationLevelDataSourceAdapter”封装“datasource”bean,只需执行以下操作:

<bean id="dataSource" class="org.springframework.jdbc.datasource.IsolationLevelDataSourceAdapter">
    <property name="isolationLevelName" value="ISOLATION_READ_COMMITTED"/>
    <property name="targetDataSource" ref="_dataSource"/>
</bean>

其中“_dataSource”是对实际数据源的引用。