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

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

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

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

然后看次方法里的doBegin方法

	@Override
	public 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

 

	@Override
	protected 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的事务隔离级别理解一下
  • (比如,你的mysql隔离事务是RR的,那么在只读事务注解里,多次查询同一个表的范围数据, 结果是一致的,如果不是在同一个事务里,那么前后查询可能会读到的数据条数不一致,造成幻读),如果隔离级别是RC的,可以不用在只读事务里,因为每次查询都会读取到已提交的数据

 

    缺点是:

  • 使用事务,动态生成代理类,增加开销。

 

 

  另记录一篇事务隔离级别的文章:

  https://www.cnblogs.com/wyaokai/p/10921323.html

1.不可重复读(读已提交RC): 开启RC事务,A在事务里查询User表(id=1),然后B去更新事务(id=1)并提交,A再查询是可以看到B提交后的数据的,这就造成了同一个事务里,A看到的数据是不一样的,即不可重复读

 2.可重复读(RR):  开启RR事务, A在事务里查询User表(id=1),然后B开启事务并提交User表(id=1)的更新,A再次查询是查不到B提交的数据的, 这就是A在同一个事务里对数据的读取是一致的

你可能感兴趣的:(spring,mysql)