一、事务的相关配置
1. 添加测试标签
在 tx:advice 中可以进行事务的相关配置:
tx:method 中的属性:
- name:指定配置的方法。 * 表示所有方法, find* 表示所有以find开头的方法。
- read-only:是否是只读事务,只读事务不存在数据的修改,数据库将会为只读事务提供一些
- 优化手段,会对性能有一定提升,建议在查询中开启只读事务。
- timeout:指定超时时间,在限定的时间内不能完成所有操作就会抛异常。默认永不超时
- rollback-for:指定某个异常事务回滚,其他异常不回滚。默认所有异常回滚。
- no-rollback-for:指定某个异常不回滚,其他异常回滚。默认所有异常回滚。
- propagation:事务的传播行为
- isolation:事务的隔离级别
添加 tx:advice标签
<!-- 进行事务相关配置 -->
<tx:advice id = "txAdvice">
<tx:attributes>
<!-- 代表以find开头的方法 -->
<tx:method name="find*" read-only="true"/>
</tx:attributes>
</tx:advice>
2. 添加对应方法
这里我们对查找用户id的时候进行用户修改,看看测试的时候是否报异常,因为上面我们已经设置了find方法开头为只读事务,不能对数据进行修改
public Account findById(int id){
Account account = accountDao.findById(1);
account.setBalance(1000);
accountDao.update(account);
return accountDao.findById(id);
}
3. 测试
添加测试方法
@Test
public void testFindById(){
Account account = accountService.findById(1);
System.out.println(account);
}
测试结果
OK,因此我们可以看到确实以find开头的方法确实是只能读取,不能修改。
二、事务的传播行为
事务传播行为是指多个含有事务的方法相互调用时,事务如何在这些方法间传播。 如果在service层的方法中调用了其他的service方法,假设每次执行service方法都要开启事务,此时就无法保证外层方法和内层方法处于同一个事务当中。
例如:
// method1的所有方法在同一个事务中
public void method1(){
// 此时会开启一个新事务,这就无法保证method1()
中所有的代码是在同一个事务中
method2();
System.out.println("method1");
}
public void method2(){
System.out.println("method2");
}
事务的传播特性就是解决这个问题的,Spring帮助我们将外层方法和内层方法放入同一事务中。
传播行为介绍REQUIRED默认。支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。MANDATORY支持当前事务,如果当前没有事务,就抛出异常。REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起。NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。NEVER以非事务方式执行,如果当前存在事务,则抛出异常。NESTED必须在事务状态下执行,如果没有事务则新建事务,如果当前有事务则创建一个嵌套事务
三、事务的隔离级别
事务隔离级别反映事务提交并发访问时的处理态度,隔离级别越高,数据出问题的可能性越低,但效率也会越低。
隔离级别 脏读 不可重复读 幻读READ_UNCOMMITED(读取未提交内容) Yes Yes Yes READ_COMMITED(读取提交内容) NoYes Yes REPEATABLE_READ(重复读)NoNoYes SERIALIZABLE(可串行化) NoNoNo
如果设置为DEFAULT会使用数据库的隔离级别。
- SqlServer , Oracle默认的事务隔离级别是READ_COMMITED
- Mysql的默认隔离级别是REPEATABLE_READ
四、注解配置声明式事务
Spring支持使用注解配置声明式事务。用法如下:
1. 注册事务注解驱动
<!-- 注册事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven>
2. 加上注解
在需要事务支持的方法或类上加@Transactional注解
package com.example.service;
import com.example.dao.AccountDao;
import com.example.pojo.Account;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Service
// 作用类上时,该类所有public方法将都具有该类型的事务属性
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
public class AccountService {
@Autowired
private AccountDao accountDao;
/**
*
* @param id1 转出人id
* @param id2 转入人id
* @param price 金额
*/
// 作用方法上时,该方法都将具有该类型事务的事务属性
public void transfer(int id1,int id2, double price){
// 转出人减少余额
Account account1 = accountDao.findById(id1);
account1.setBalance(account1.getBalance() - price);
accountDao.update(account1);
// 模拟程序出错
int i = 1 / 0;
// 转入人增加余额
Account account2 = accountDao.findById(id2);
account2.setBalance(account2.getBalance() + price);
accountDao.update(account2);
}
}
3. 配置类代替xml文件中的注解事务支持
配置类代替xml中的注解事务支持:需要在配置类上方写@EnableTranscationManagement
import com.alibaba.druid.pool.DruidDataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan("com.example")
@EnableTransactionManagement
public class SpringConfig {
@Bean
public DataSource getDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
druidDataSource.setUrl("jdbc:mysql:///spring");
druidDataSource.setUsername("root");
druidDataSource.setPassword("666666");
return druidDataSource;
}
@Bean
public SqlSessionFactoryBean getSqlSession(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
public MapperScannerConfigurer getMapperScanner(){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setBasePackage("com.example.dao");
return mapperScannerConfigurer;
}
@Bean
public DataSourceTransactionManager getTransactionManger(DataSource dataSource){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource);
return dataSourceTransactionManager;
}
}
4. 测试
添加测试方法
// 测试注解配置类
@Test
public void testSpringConfig(){
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
AccountService service = (AccountService) ac.getBean("accountService");
accountService.transfer(1,2,500);
}
测试结果
OK,可以看到确实出现异常中断了,因此测试成功,Spring专栏也到此告一段落啦 ,接下来就开启了SpringMVC框架学习。希望大家可以持续关注啊!!!
往期专栏&文章相关导读
大家如果对于本期内容有什么不了解的话也可以去看看往期的内容,下面列出了博主往期精心制作的Maven,Mybatis等专栏系列文章,走过路过不要错过哎!如果对您有所帮助的话就点点赞,收藏一下啪。其中Spring专栏有些正在更,所以无法查看,但是当博主全部更完之后就可以看啦。
1. Maven系列专栏文章
Maven系列专栏Maven工程开发Maven聚合开发【实例详解---5555字】
2. Mybatis系列专栏文章
Mybatis系列专栏MyBatis入门配置Mybatis入门案例【超详细】MyBatis配置文件 —— 相关标签详解Mybatis模糊查询——三种定义参数方法和聚合查询、主键回填Mybatis动态SQL查询 --(附实战案例--8888个字--88质量分)Mybatis分页查询——四种传参方式Mybatis一级缓存和二级缓存(带测试方法)Mybatis分解式查询Mybatis关联查询【附实战案例】MyBatis注解开发---实现增删查改和动态SQLMyBatis注解开发---实现自定义映射关系和关联查询
3. Spring系列专栏文章
Spring系列专栏Spring IOC 入门简介【自定义容器实例】IOC使用Spring实现附实例详解Spring IOC之对象的创建方式、策略及销毁时机和生命周期且获取方式Spring DI简介及依赖注入方式和依赖注入类型Spring IOC相关注解运用——上篇Spring IOC相关注解运用——下篇Spring AOP简介及相关案例注解、原生Spring、SchemaBased三种方式实现AOP【附详细案例】Spring事务简介及相关案例Spring 事务管理方案和事务管理器及事务控制的APISpring 事务的相关配置、传播行为、隔离级别及注解配置声明式事务
版权归原作者 会洗碗的CV工程师 所有, 如有侵权,请联系我们删除。