当前位置: 代码迷 >> 综合 >> 记一次Spring的只读事务(mysql readonly)
  详细解决方案

记一次Spring的只读事务(mysql readonly)

热度:49   发布时间:2024-02-19 20:34:23.0

Spirng加只读事务最简捷方式:@Transactional(readOnly = true)

那么,加了这个玩意,到底起了什么作用呢?来看下源码:

Spring里带事务的service方法会先进入org.springframework.transaction.support.AbstractPlatformTransactionManager#getTransaction方法,

然后看次方法里的doBegin方法

	@Overridepublic final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {Object transaction = doGetTransaction();........省略一些代码// No existing transaction found -> check propagation behavior to find out how to proceed.if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {throw new IllegalTransactionStateException("No existing transaction found for transaction marked with propagation 'mandatory'");}else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {SuspendedResourcesHolder suspendedResources = suspend(null);try {boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);/*** 看doBegin方法 **/doBegin(transaction, definition);prepareSynchronization(status, definition);return status;}........省略}else {........省略}}

org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

 @Overrideprotected void doBegin(Object transaction, TransactionDefinition definition) {DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;Connection con = null;try {if (txObject.getConnectionHolder() == null ||txObject.getConnectionHolder().isSynchronizedWithTransaction()) {Connection newCon = this.dataSource.getConnection();txObject.setConnectionHolder(new ConnectionHolder(newCon), true);}txObject.getConnectionHolder().setSynchronizedWithTransaction(true);con = txObject.getConnectionHolder().getConnection();/*** 看这个 prepareConnectionForTransaction方法: @Transactional(readOnly = true) 执行con.setReadOnly(true);*/Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);txObject.setPreviousIsolationLevel(previousIsolationLevel);// Switch to manual commit if necessary. This is very expensive in some JDBC drivers,// so we don't want to do it unnecessarily (for example if we've explicitly// configured the connection pool to set it already)./*** 看这个 con.setAutoCommit(false);*/if (con.getAutoCommit()) {txObject.setMustRestoreAutoCommit(true);con.setAutoCommit(false);}prepareTransactionalConnection(con, definition);txObject.getConnectionHolder().setTransactionActive(true);........省略一些代码}catch (Throwable ex) {........省略一些代码}}

所以,是设置了 con.setReadOnly(true); 而且代码顺序是

先con.setReadOnly(true); 

再con.setAutoCommit(false);

 

 

加了只读注解后,会有哪些影响呢?

   比如做报表或者做统计:

  • 只读事务的好处,作为ORM框架优化的暗号,保证读一致性,事务内不允许DML操作,否则报错
  • 只读事务的场景,如统计,保证统计结果准确性。
  • 只读事务里,也可以在只读事务里使用 select... for update
  • 因为只读事务,所有查询都是在一个事务里,所以可以配合mysql的事务隔离级别理解一下(比如,多次查询同一个表)

    缺点是:

  • 使用事务,动态生成代理类,增加开销。
  相关解决方案