SSM框架,spring-tx的学习

编程式事务和声明式事务

编程式事务

编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚

声明式事务

声明式事务是指使用注解或xml方式配置来控制事务的提交和回滚

事务管理器

管理事务是在一个事务的增强类中,调用各种事务方法,但是因为有不同数据库的操作的方式(不同持久化层的框架),事务操作的代码也不同。所以在增强类中,调用的是数据库操作方式的相应的实现类的事务方法,这些不同的相应的实现类就是事务管理器。事务管理器都实现了事务管理器接口。

Spring声明式事务对应依赖

  • spring-tx: 包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等等)
  • spring-jdbc: 包含DataSource方式事务管理器实现类DataSourceTransactionManager
  • spring-orm: 包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa等

事务管理器主要使用DataSourceTransactionManager,整合 JDBC方式、JdbcTemplate方式、Mybatis方式的事务实现

DataSourceTransactionManager类中的主要方法:

  • doBegin():开启事务
  • doSuspend():挂起事务
  • doResume():恢复挂起的事务
  • doCommit():提交事务
  • doRollback():回滚事务

基本事务控制

步骤

  1. 配置数据库连接
  2. 将事务管理器加入ioc容器(在配置类中进行),并将数据库连接的对象传入事务管理器
  3. 在配置类上方写上@EnableTransactionManagement 注解,开启事务注解的支持
  4. 在要配置事务管理的方法上写上@Transactional 注解即可(也可以在类的声明上加,意思是此类的所有方法都进行事务管理)
@Configuration
@ComponentScan("com.ergou")
@PropertySource("classpath:jdbc.properties")
@EnableAspectJAutoProxy
//开启事务注解的支持
@EnableTransactionManagement
public class JavaConfig {
//配置数据库连接
@Value("${ergou.url}")
    private String url;
    @Value("${ergou.driver}")
    private String driver;
    @Value("${ergou.username}")
    private String username;
    @Value("${ergou.password}")
    private String password;
    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driver);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
//配置事务管理器
@Bean
    public TransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
//需要连接池对象
dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}
@Service
public class StudentService {

    @Autowired
    private StudentDao studentDao;

/**
     *加上@Transactional后就是配置有事务管理的方法
*/
@Transactional
    public void changeInfo(){
        studentDao.updateAgeById(100,1);
        System.out.println("-----------");
        studentDao.updateNameById("test1",1);
    }
}

事务的属性

注:对于一个方法,若其类上有注解@Transactional注解,方法上没有@Transactional注解,则以类上的@Transactional注解为准;若方法上有注解,则无视类上的注解,以其方法上的@Transactional注解为准

只读

对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。

设置方式:在@Transactional注解中加上属性readOnly,设置为true,即此方法的事务操作为只读模式,如果有DML的语句,就会报错。

  1. 生效原则

    如果一个类中每一个方法上都使用了 @Transactional 注解,那么就可以将 @Transactional 注解提取到类上。反过来说:@Transactional 注解在类级别标记,会影响到类中的每一个方法。同时,类级别标记的 @Transactional 注解中设置的事务属性也会延续影响到方法执行时的事务属性。除非在方法上又设置了 @Transactional 注解。

    对一个方法来说,离它最近的 @Transactional 注解中的事务属性设置生效。

  2. 用法举例

    在类级别@Transactional注解中设置只读,这样类中所有的查询方法都不需要设置@Transactional注解了。因为对查询操作来说,其他属性通常不需要设置,所以使用公共设置即可。

    然后在这个基础上,对增删改方法设置@Transactional注解 readOnly 属性为 false。


@Service
@Transactional(readOnly = true)
public class EmpService {
    
    // 为了便于核对数据库操作结果,不要修改同一条记录
    @Transactional(readOnly = false)
    public void updateTwice(……) {
    ……
    }
    
    // readOnly = true把当前事务设置为只读
    // @Transactional(readOnly = true)
    public String getEmpName(Integer empId) {
    ……
    }
    
}

超时时间

事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。

此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。

概括来说就是:超时回滚,释放资源

设置方式:在@Transactional注解中加上属性timeout,写上设置的时间的数字(单位为秒),超过时间,就会回滚事务和释放异常

@Transactional(timeout = 3)
    public void changeInfo(){
        studentDao.updateAgeById(100,1);
//        try {
//            Thread.sleep(4000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
System.out.println("-----------");
        studentDao.updateNameById("test2",1);
    }

事务异常

默认情况下,事务管理的方法只有发生运行时异常时才会事务回滚,如果是IOException异常等其他异常,则需要设置事务的事务异常属性,指定哪些异常要回滚,哪些异常不回滚。

指定异常要回滚的属性:rollbackFor

指定异常不回滚的属性:noRollbackFor

属性值赋值为对应异常的.class对象

@Transactional(timeout = 3,rollbackFor = Exception.class,noRollbackFor = FileNotFoundException.class)
    public void changeInfo(){
        studentDao.updateAgeById(100,1);
				System.out.println("-----------");
        studentDao.updateNameById("test2",1);
    }

事务隔离级别

数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:

  1. 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
  2. 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。(推荐)
  3. 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
  4. 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。 不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整。

设置方式:在@Transactional注解中加上isolation属性,属性值为指定枚举类型,比如可重复读为Isolation.REPEATABLE_READ,其他三个对应其上面括号中的英文改大写空格改下划线即可。

@Transactional(timeout = 3,isolation = Isolation.REPEATABLE_READ)
public void changeInfo(){
    studentDao.updateAgeById(100,1);
    System.out.println("-----------");
    studentDao.updateNameById("test2",1);
}

事务传播特性

如果方法1(配置了事务管理)调用了方法2(配置了事务管理),要设置方法2是否也加入方法1的事务,就要设置方法2的事务传播行为的属性

设置方式:在@Transactional注解中加上属性propagation,设置其属性值

propagation属性

@Transactional 注解通过 propagation 属性设置事务的传播行为。它的默认值是:

Propagation propagation() default Propagation.REQUIRED;

propagation 属性的可选值由 org.springframework.transaction.annotation.Propagation 枚举类提供:

名称含义
REQUIRED(默认值)如果父方法有事务,就加入,如果没有就新建,自己独立
REQUIRES_NEW不管父方法是否有事务,我都新建事务,都是独立的

在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。这是因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果。

其他传播行为值(了解)

  1. Propagation.REQUIRED:如果当前存在事务,则加入当前事务,否则创建一个新事务。
  2. Propagation.REQUIRES_NEW:创建一个新事务,并在新事务中执行。如果当前存在事务,则挂起当前事务,即使新事务抛出异常,也不会影响当前事务。
  3. Propagation.NESTED:如果当前存在事务,则在该事务中嵌套一个新事务,如果没有事务,则与Propagation.REQUIRED一样。
  4. Propagation.SUPPORTS:如果当前存在事务,则加入该事务,否则以非事务方式执行。
  5. Propagation.NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,挂起该事务。
  6. Propagation.MANDATORY:必须在一个已有的事务中执行,否则抛出异常。
  7. Propagation.NEVER:必须在没有事务的情况下执行,否则抛出异常。

至此Spring框架学习完毕

  • 16
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二狗mao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值