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 测试
哪个帮我详细的讲解下 spring struts hibernate 的事物咯 还有他们的区别