目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

Spring 事务抽象

Spring 的事务抽象

 Spring 为数据访问层提供了很多抽象,在这些抽象下面我们可以非常方便的使用统一的方式来操作不同的框架。
 Spring 的事务抽象采用了一致的事务模型,方便你使用不同的

  • JDBC/Hibernate/myBatis
  • DataSource/JTA

事务抽象的核心接口

 不同框架的事务管理器都现了 AbstractPlatformTransactionManager,AbstractPlatformTransactionManager 实现了 PlatformTransactionManager,所以 PlatformTransactionManager 的实现类可以是以下类

  • DataSourceTransactionManager
  • HibernateTransactionManager
  • JtaTransactionManager
public interface PlatformTransactionManager {
	//TransactionStatus 获取事务状态,TransactionDefinition用于设置事务传播特性、隔离级别、超时设置、是否只读。
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

	void commit(TransactionStatus status) throws TransactionException;

	void rollback(TransactionStatus status) throws TransactionException;
}

事务传播行为

事务传播特性

REQUIRES_NEW 对比 NESTED

  • REQUIRES_NEW:两个事物没有关联。
  • NESTED:两个事物有关联、外部事物回滚、内嵌事物也会回滚。

事务隔离级别

事务隔离级别

编程式事务

 TransactionTemplate 继承了 DefaultTransactionDefinition,所以所有事务相关的属性都可以直接设置给 TransactionTemplate.

TransactionTemplate

  • TransactionCallback():有返回值的操作。
  • TransactionCallbackWithoutResult():没有返回值的操作。

如何设置事务的特性

  • TransactionTemplate中可以设置当前事务的播特性和隔离特性
  • 另外在定义PlatfromTransactionManager的时候可以传入 TransactironDefinition 进行定义前事务的播特性和隔离特性。
TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException;

编程式事务示例

package geektime.spring.data.programmatictransactiondemo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

@SpringBootApplication
@Slf4j
public class ProgrammaticTransactionDemoApplication implements CommandLineRunner {
	@Autowired
	private TransactionTemplate transactionTemplate;
	@Autowired
	private JdbcTemplate jdbcTemplate;

	public static void main(String[] args) {
		SpringApplication.run(ProgrammaticTransactionDemoApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		//设置传播行为(默认是PROPAGATION_REQUIRED)
		transactionTemplate.setPropagationBehavior(0);
		//设置隔离级别(默认是ISOLATION_DEFAULT:使用的是底层数据库的默认的隔离级别)
		transactionTemplate.setIsolationLevel(3);
		//设置是否只读
		transactionTemplate.setReadOnly(true);
		//设置超时时间(默认)
		transactionTemplate.setTimeout(30000);

		log.info("COUNT BEFORE TRANSACTION: {}", getCount());// COUNT BEFORE TRANSACTION: 0
		transactionTemplate.execute(new TransactionCallbackWithoutResult() {
			@Override
			protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
				jdbcTemplate.execute("INSERT INTO FOO (ID, BAR) VALUES (1, 'aaa')");
				log.info("COUNT IN TRANSACTION: {}", getCount());// COUNT IN TRANSACTION: 1
				transactionStatus.setRollbackOnly();//设置事务为rollback
			}
		});
		log.info("COUNT AFTER TRANSACTION: {}", getCount());// COUNT AFTER TRANSACTION: 0
	}

	private long getCount() {
		return (long) jdbcTemplate.queryForList("SELECT COUNT(*) AS CNT FROM FOO")
				.get(0).get("CNT");
	}
}

声明式事务

@Transactional

  • transactionManager
  • propagation
  • isolation
  • timeout
  • readOnly
  • 判断回滚:当碰到特定的异常类时,才会进行回滚。

FooService

package geektime.spring.data.declarativetransactiondemo;

public interface FooService {
    void insertRecord();
    void insertThenRollback() throws RollbackException;
    void invokeInsertThenRollback() throws RollbackException;
}

FooServiceImpl

package geektime.spring.data.declarativetransactiondemo;

import org.springframework.aop.config.AopConfigUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class FooServiceImpl implements FooService {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Autowired
    private FooService fooService;

    @Override
    @Transactional
    public void insertRecord() {
        jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('AAA')");
    }

    @Override
    //@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRES_NEW)
    @Transactional(rollbackFor = RollbackException.class)
    public void insertThenRollback() throws RollbackException {
        jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ('BBB')");
        throw new RollbackException();
    }

    /**
     * AopContext.currentProxy() 可以获取当前类的代理类,命中切点
     */
    @Override
    public void invokeInsertThenRollback() throws RollbackException {
        //((FooService) AopContext.currentProxy()).insertThenRollback();
        fooService.insertThenRollback();
    }
}

RollbackException

package geektime.spring.data.declarativetransactiondemo;

public class RollbackException extends Exception {
}

启动类

package geektime.spring.data.declarativetransactiondemo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@SpringBootApplication
//开启事务注解的支持
@EnableTransactionManagement(mode = AdviceMode.PROXY)
@Slf4j
public class DeclarativeTransactionDemoApplication implements CommandLineRunner {
	@Autowired
	private FooService fooService;
	@Autowired
	private JdbcTemplate jdbcTemplate;

	public static void main(String[] args) {
		SpringApplication.run(DeclarativeTransactionDemoApplication.class, args);
	}

	@Override
	public void run(String... args) throws Exception {
		//insertRecord()
		fooService.insertRecord();
		log.info("AAA {}",
				jdbcTemplate
						.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='AAA'", Long.class));//AAA 1

		//insertThenRollback()
		try {
			fooService.insertThenRollback();
		} catch (Exception e) {
			log.info("BBB {}",
					jdbcTemplate
							.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class));//BBB 0
		}

		/**
		 * invokeInsertThenRollback()
		 * Spring的声明式事务本质是利用 AOP Proxy,它在目标方法上做了一层封装,进而帮助用户做一些模板式的事务操作。
		 * 需要直接调用带有@Transaction注解的类才会生成代理类,被同类种其他方法调用则不会生成代理类,所以@Trasaction注解失效
		 */
		try {
			fooService.invokeInsertThenRollback();
		} catch (Exception e) {
			log.info("BBB {}",
					jdbcTemplate
							.queryForObject("SELECT COUNT(*) FROM FOO WHERE BAR='BBB'", Long.class));//BBB 1  正确应该是 BBB 0
		}
	}
}

作者:Soulboy