【问题标题】:Change isolation level with Spring 4.0 & Hibernate 4.2.8使用 Spring 4.0 和 Hibernate 4.2.8 更改隔离级别
【发布时间】:2014-04-10 10:10:43
【问题描述】:

我们正在使用 Spring 4.0.0、Hibernate 4.2.8 和 Ms SQL Server 8 开发一个应用程序,它使用由 DB 表支持并与 Hibernate VO (CustomSequence) 映射的自定义序列

在服务调用中访问此序列:

  • 主服务启动自己的事务
  • 执行代码、做一些事情、查询...
  • 为序列值调用序列服务 (SequenceService)

  • SequenceService 启动自己的事务 (REQUIRES_NEW)

  • SequenceService 找到对象,返回值并保存下一个值

  • 主服务获取值,设置业务对象并保存(此时序列值已被内部新事务提交)

  • 退出

管理自定义序列的服务片段:

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE)
@Service("sequenceService")
public class SequenceService implements ISequenceService {

    @Autowired
    private ISequenceDao sequenceDao;

    private Integer getSequence() {

        CustomSequence sequenceOut = sequenceDao.find();

        final Integer nextVal = sequenceOut.getNextVal();
        sequenceOut.setNextVal(nextVal + 1);
        sequenceDao.save(sequenceOut);

        return nextVal;    
    }    
}

我们的问题是serializable属性被完全忽略,所以2个并发线程访问getSequence方法并获得相同的值。

如果我们使用 TransactionSynchronizationManager 检查隔离,则该值对于可序列化(值=8)来说似乎是正确的:

...
Integer isolation = TransactionSynchronizationManager.getCurrentTransactionIsolationLevel();
...

我们的spring xml文件就是这个:

<context:annotation-config />
<context:component-scan base-package="dev.app"/>
<tx:annotation-driven /> 

<bean name="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/appDatasource"/>
</bean>    

<bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" lazy-init="false" >
    <property name="dataSource"> <ref bean="dataSource" /></property>
    <property name="packagesToScan" value="dev.app.model"/>
    <property name="hibernateProperties">
       <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.SQLServerDialect</prop>
            <prop key="hibernate.show_sql">true</prop>
            <prop key="hibernate.format_sql">true</prop>
            <!-- Disable LOB creation as connection -->
            <prop key="hibernate.temp.use_jdbc_metadata_defaults">false</prop>
        </props>
    </property>
 </bean>

我已经使用 MS SQL Management Studio 使用这些命令检查了数据库可序列化功能,然后执行了应用程序代码,并且它有效(在工作室提交之前阻止代码):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE  

BEGIN TRAN  

UPDATE CUSTOM_SEQUENCE set NEXTVAL = 1000;
WAITFOR DELAY '00:1:00'

COMMIT

¿关于发生了什么的任何线索?我在网上看了很多资料,但无济于事

非常感谢!

【问题讨论】:

    标签: spring hibernate transactions sequence isolation


    【解决方案1】:

    根据HibernateTransactionManager的代码,这可能是因为事务管理器的prepareConnection标志设置为false:

    /**
     * Set whether to prepare the underlying JDBC Connection of a transactional
     * Hibernate Session, that is, whether to apply a transaction-specific
     * isolation level and/or the transaction's read-only flag to the underlying
     * JDBC Connection.
     * <p>Default is "true". If you turn this flag off, the transaction manager
     * will not support per-transaction isolation levels anymore.  ...
     */
    public void setPrepareConnection(boolean prepareConnection) {
        this.prepareConnection = prepareConnection;
    }
    

    尝试下一个断点,看看是不是这样。这个标志也总是和isSameConnectionForEntireSession(session)一起使用:

    if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
        ....
    }
    

    isSameConnectionForEntireSession 说:

    /**
     * Return whether the given Hibernate Session will always hold the same
     * JDBC Connection. This is used to check whether the transaction manager
     * can safely prepare and clean up the JDBC Connection used for a transaction.
     * <p>The default implementation checks the Session's connection release mode
     * to be "on_close".
     */
    protected boolean isSameConnectionForEntireSession(Session session) ...
    

    这意味着只有在以下情况下才能应用自定义隔离级别:在事务管理器中启用了这样做的标志,并且保证相同的数据库连接将始终用于相同的休眠会话。

    如果不是这样,那么事务管理器不会更改隔离设置,因为如果一个会话可以为不同的查询使用多个会话,事务管理器将不知道会话何时被发送回池。

    这基本上意味着事务管理器只更改数据库会话的隔离设置,前提是它保证可以在会话发送到池之前清除这些相同的设置。

    【讨论】:

    • 感谢您的回答。我已经调试了这个类,看起来一切正常,因为标志永远不会改变(真实值)并且 isSameConnectionForEntireSession(session) 值也是真实的。它进入 DataSourceUtils.prepareConnectionForTransaction(con, definition) 并将当前隔离设置为 8(之前的隔离为 2)。内部事务结束后,执行“doCleanupAfterCompletion”恢复之前的隔离值(2)。我只是不知道这里出了什么问题。
    • 我认为问题与服务器有关。从头开始在新服务器中再次部署解决了问题...
    猜你喜欢
    • 2016-01-16
    • 2015-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-20
    • 1970-01-01
    • 2015-07-21
    • 1970-01-01
    相关资源
    最近更新 更多