Spring6 Transactional 事务

1. 事务使用的示例,便于复制

1. 事务注解在实际中的使用

// 代码是用于测试 隔离级别时用的,没有任何实际意义
  @Transactional(isolation = Isolation.SERIALIZABLE, readOnly = true, timeout = 10)
  public void getAccountByActon(String acton) {
    for (int i = 0; i < 10; i++) {
      AccountBean act01 = accountDao.selectAccount(acton);
      System.out.println(i + "查询到用户名" + acton + "的账户" + act01.toString());
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        throw new RuntimeException(e);
      }
    }
    System.out.println("查询事务结束");
  }

2. 配置事务,使用注解的方式

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;

@Configuration
@ComponentScan({"com.example.bank"})
/*<!--  事务注解驱动器-->
  <tx:annotation-driven transaction-manager="transactionManager"/>*/
@EnableTransactionManagement
public class SpringConfig {
  /*<!--  配置德鲁伊连接池的数据源 -->
  <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/batis"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
  </bean>*/
  @Bean
  public DruidDataSource getDataSource() {
    DruidDataSource dataSource = new DruidDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/batis");
    dataSource.setUsername("root");
    dataSource.setPassword("123456");
    return dataSource;
  }
  /*<!--配置spring JdbcTemplate 使用德鲁伊连接池-->
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="druidDataSource"/>
  </bean>*/
  @Bean(name="jdbcTemplate")  // 在dao中使用,不加名字也行,dao中要按类型注入
  public JdbcTemplate getJdbcTemplate(DataSource dataSource) {  // 参数会自动使用上面的DataSouse
//    JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
    JdbcTemplate jdbcTemplate = new JdbcTemplate();
    jdbcTemplate.setDataSource(dataSource);
    return jdbcTemplate;
  }
  /*<!--  事务管理器 -->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource"/>
  </bean>*/
  @Bean
  public DataSourceTransactionManager getTransactionManager(DataSource dataSource) {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(dataSource);
    return transactionManager;
  }
}

3. 配置事务,使用xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--  配置德鲁伊连接池,及其使用的数据源 -->
  <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
    <property name="url" value="jdbc:mysql://localhost:3306/batis"/>
    <property name="username" value="root"/>
    <property name="password" value="123456"/>
  </bean>
<!--配置JdbcTemplate使用德鲁伊连接池-->
<!--  JdbcTemplate是Spring提供的一个JDBC模板类,是对JDBC的封装,简化JDBC代码-->
  <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="druidDataSource"/>
  </bean>
<!--  事务管理器 -->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="druidDataSource"/>
  </bean>
<!--  事务注解驱动器-->
  <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

2. 事务的配置属性

1. 事务注解源码

@Target({ElementType.TYPE, ElementType.METHOD})	// 用于类、接口、方法
@Retention(RetentionPolicy.RUNTIME)
@Inherited	// 可继承
@Documented
@Reflective
public @interface Transactional {
  @AliasFor("transactionManager")
  String value() default "";

  @AliasFor("value")
  String transactionManager() default "";

  String[] label() default {};

  Propagation propagation() default Propagation.REQUIRED;	// 事务的传播行为

  Isolation isolation() default Isolation.DEFAULT;	// 隔离级别

  int timeout() default -1;	// 事务超时时间

  String timeoutString() default "";	

  boolean readOnly() default false;	// 事务为只读事务

  Class<? extends Throwable>[] rollbackFor() default {};	// 出现哪些异常时回滚事务

  String[] rollbackForClassName() default {};

  Class<? extends Throwable>[] noRollbackFor() default {};	// 出现哪些异常时不回滚事务

  String[] noRollbackForClassName() default {};
}

2. 事务的传播行为

什么是传播行为:a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为

  • 使用
@Transactional(propagation= Propagation.REQUIRED)
  • propagation的值是枚举类型
public enum Propagation {
  REQUIRED(0),	// 支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
  SUPPORTS(1),	// 支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
  MANDATORY(2),	// 必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】
  REQUIRES_NEW(3),	// 开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
  NOT_SUPPORTED(4),	// 以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
  NEVER(5),	// 以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】
  NESTED(6);	// 如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】

3. 事务的隔离级别

参考:三大读问题和四个隔离级别(链接

@Transactional(isolation = Isolation.SERIALIZABLE)
  • isolation 的值是枚举类型:
public enum Isolation {
  DEFAULT(-1),	// 使用数据库的默认隔离级别。不同的数据库可能有不同的默认隔离级别。例如:mysql是READ_COMMITTED,Oracle默认是READ_UNCOMMITTED
  READ_UNCOMMITTED(1),	//  读未提交
  READ_COMMITTED(2),	// 读已提交
  REPEATABLE_READ(4),	// 可重复读
  SERIALIZABLE(8);	// 序列化

4. 事务的超时时间

默认的值是-1,没有超时时间

超时时间指的是哪段时间?
最后一条DML语句(数据库操作语句-增删改查)执行之前的时间


  public void dao() {
  	select ....	
  	insert .....
  	delete .....
  	update .....
  	// 事务的超时时间指的就是方法执行开始到这里的时间
    delte ....	// 最后一条dml语句
    // 在最后一条dml语句之后执行再多的代码,使用再多时间也不会被计入超时时间
  }

5. 只读事务

解释: 事务中只包含查询语句,启动spring的优化策略。提高select语句执行效率

  @Transactional(readOnly = true)

6. 设置哪些异常回滚事务

@Transactional(rollbackFor = RuntimeException.class)

7. 设置哪些异常不回滚事务

@Transactional(noRollbackFor = NullPointerException.class)
上一篇:蓝桥杯第22场小白入门赛2~5题


下一篇:三格电子-EtherNet IP转Modbus TCP网关