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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5234240/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 10:07:32  来源:igfitidea点击:

Hibernate, spring, JPS & isolation - custom isolation not supported

javadatabasehibernatespringjpa

提问by Ikthiander

I have been trying this:

我一直在尝试这个:

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

on my service methods, but spring complains saying:

在我的服务方法上,但 spring 抱怨说:

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

How can I resolve this?

我该如何解决这个问题?

采纳答案by Shahzad Mughal

This implementation is not account for the clean up stuff, I have implemented a similar solution but that accounts for the clean up as well. That solution can be found here: http://shahzad-mughal.blogspot.com/2012/04/spring-jpa-hibernate-support-for-custom.html

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

回答by Bozho

No custom isolation levels are supported by JPA. You can extend the HibernateJpaDialectclass and override connection-related methods so that you can set custom isolation levels on the Connection

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

Here's something that I've written, but have not tested yet:

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

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

}

And you define this as a property of your EntityManagerFactory:

您将其定义为您的属性EntityManagerFactory

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

回答by Gus

Drawing on Bozho's answer and considering the comments thereon, The following seems to be a complete (Hibernate 4 compatible) solution addressing the need reset the connection. Best I can tell, the spring layer will guarantee call the cleanupTransaction method, but if that isn't actually guaranteed, this may need to be re-thought due to the potential for a permGen memory leak and post request side effects on the connection object.

借鉴 Bozho 的回答并考虑对其的评论,以下似乎是一个完整的(与 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();
    }
  }
}

回答by Nestor Urquiza

@Shahzad Mughal I left you two points ;-) Your answer should be accepted as the correct one. The accepted answer will originate the below issue randomly miss leading developers to think there are bugs with mysql driver for example:

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

WARN [org.hibernate.util.JDBCExceptionReporter] - SQL Error: 0, SQLState: S1009 ERROR [org.hibernate.util.JDBCExceptionReporter] - Connection is read-only. Queries leading to data modification are not allowed

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

You can read more about our adventure with this issue at http://thinkinginsoftware.blogspot.com/2013/10/connection-is-read-only-queries-leading.html

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

回答by Robby Pond

When specifying the JpaTransactionManagerdid you specify a JPADialect? By default I think it uses the DefaultJpaDialectand you need the HibernateJpaDialect.

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

回答by polesen

You can also wrap the "datasource" bean with the "IsolationLevelDataSourceAdapter", by simply doing this:

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

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

where "_dataSource" is a ref to an actual datasource.

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