1

我们正在使用 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

¿关于发生了什么的任何线索?我在互联网上阅读了很多信息,但无济于事

非常感谢提前!

4

1 回答 1

0

根据 的代码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) ...

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

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

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

于 2014-03-07T10:38:02.330 回答