Hibernate、spring、JPS 和隔离 - 不支持自定义隔离

Posted

技术标签:

【中文标题】Hibernate、spring、JPS 和隔离 - 不支持自定义隔离【英文标题】:Hibernate, spring, JPS & isolation - custom isolation not supported 【发布时间】:2011-07-11 04:37:12 【问题描述】:

我一直在尝试这个:

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

关于我的服务方法,但是 spring 抱怨说:

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

我该如何解决这个问题?

【问题讨论】:

+1:我也遇到过。但是问题并没有出现在我的单元测试中,尽管使用了AbstractTransactionalJUnit4SpringContextTests,这很奇怪。 amitstechblog.wordpress.com/2011/05/31/… 我在 Amit 的博客上尝试了该解决方案,但它使我的所有数据库连接都是只读的。我在持久性配置中使用 AOP 管理事务。 【参考方案1】:

@Shahzad Mughal 我给你留下了两点;-) 你的答案应该被接受为正确的答案。接受的答案将源自以下问题,例如随机错过导致开发人员认为 mysql 驱动程序存在错误:

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

您可以在http://thinkinginsoftware.blogspot.com/2013/10/connection-is-read-only-queries-leading.htmlhttp://thinkinginsoftware.blogspot.com/2013/10/connection-is-read-only-queries-leading.html 上阅读更多关于我们的冒险经历

【讨论】:

【参考方案2】:

您也可以使用“IsolationLevelDataSourceAdapter”包装“数据源”bean,只需执行以下操作:

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

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

【讨论】:

这是一个糟糕的解决方案。您随后发出的任何连接和查询都将具有可序列化的隔离。 在某些情况下,此解决方案可能适用 - 例如,在我的项目中,我有多个数据源,我需要在其中一个上设置特定的隔离级别。对于这种情况,此解决方案比扩展 JpaDialect 更优雅。【参考方案3】:

借鉴 Bozho 的答案并考虑其上的 cmets,以下似乎是一个完整的(与 Hibernate 4 兼容)解决方案,解决了重置连接的需要。尽我所能,spring 层将保证调用 cleanupTransaction 方法,但如果实际上不能保证,这可能需要重新考虑,因为可能存在 permGen 内存泄漏和连接对象上的 post request 副作用.

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();
    
  

【讨论】:

我认为你应该有一个 ThreadLocal>>> 而不是两个线程本地。 在某些情况下,没有为 resetConnectionAfterTransaction 提供连接,在这种情况下,IllegalArgumentException 会抛出 Assert.notNull(con, "No Connection specified");。你能说我吗,我需要先检查 con 不是 null 还是别的什么?【参考方案4】:

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

【讨论】:

+1 进行彻底调查和完整的解决方案,包括做家务和自己打扫卫生;) 它不考虑只读标志。如果我们在连接上执行只读事务然后进入 rw 事务,它会失败,因为连接处于只读状态。 明确一点,我认为这与设置 org.hibernate.dialect 不同,例如 PostgreSQL82Dialect?【参考方案5】:

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>

【讨论】:

嗨@Bozho你能解释一下吗,你的意思是它与其他人建议的jpadialects无关? @user582862 好吧,试试看,但据我所知,JPA 不支持隔离级别。但是,底层的 JDBC 确实如此,因此您可以使用它。 嗯,我认为你是对的。这是对此的抱怨:jira.springsource.org/browse/SPR-3812 据我了解,上述解决方案背后的想法是调用 DataSourceUtils 来设置隔离级别。上述代码的一个问题是 session.connection() 方法已被 hibernate 弃用,并将从 Hibernate 4.x 中删除(根据 java 文档)。我正在寻找更好的选择。 session.doWork() 是 connection() 的建议替代方法【参考方案6】:

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

【讨论】:

与此无关。 HibernateJpaDialect 的 beginTransaction 调用 super.beginTransaction(entityManager, definition) (super=DefaultJpaDialect) 这就是从 DefaultJpaDialect 抛出 InvalidIsolationLevelException 的原因。

以上是关于Hibernate、spring、JPS 和隔离 - 不支持自定义隔离的主要内容,如果未能解决你的问题,请参考以下文章

使用hsql hibernate数据源隔离Junit测试

使用 hsql hibernate 数据源隔离 Junit 测试

哪个帮我详细的讲解下 spring struts hibernate 的事物咯 还有他们的区别

Hibernate 事物隔离级别

如何在 Spring 生态系统中使用 JPA hibernate 连接到不同的 mysql 数据库模式?

Hibernate 隔离集成测试