一、Java事务
1、通常的观念认为,事务仅与数据库相关。
事务必须服从ISO/IEC所制定的ACID原则。ACID是原子性(atomicity)、一致性(consistency)、**隔离性 (isolation)和持久性(durability)**的缩写。
事务的原子性:表示事务执行过程中的任何失败都将导致事务所做的任何修改失效。 事务的一致性:表示当事务执行失败时,所有被该事务影响的数据都应该恢复到事务执行前的状态。 事务的隔离性:表示在事务执行过程中对数据的修改,在事务提交之前对其他事务不可见。 事务的持久性:表示已提交的数据在事务执行失败时,数据的状态都应该正确。
通俗的理解,事务是一组原子操作单元,从数据库角度说,就是一组SQL指令,要么全部执行成功,若因为某个原因其中一条指令执行有错误,则撤销先前执行过的所有指令。更简答的说就是:要么全部执行成功,要么撤销不执行。
既然事务的概念从数据库而来,那Java事务是什么?之间有什么联系? 实际上,一个Java应用系统,如果要操作数据库,则通过JDBC来实现的。增加、修改、删除都是通过相应方法间接来实现的,事务的控制也相应转移到Java程序代码中。 因此,数据库操作的事务习惯上就称为Java事务。
2、为什么需要Java事务
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。
举一个简单例子: 比如银行转帐业务,账户A要将自己账户上的1000元转到B账 户下面,A账户余额首先要减去1000元,然后B账户要增加1000元。假如在中间网络出现了问题,A账户减去1000元已经结束, B因为网络中断而操作 失败,那么整个业务失败,必须做出控制,要求A账户转帐业务撤销。这才能保证业务的正确性,完成这个操作就需要事务,将A账户资金减少和B账户资金增加方 到一个事务里面,要么全部执行成功,要么操作全部撤销,这样就保持了数据的安全性。
二、SpringBoot事务
在Spring中,事务有两种实现方式,分别是编程式事务管理和声明式事务管理两种方式。 1、编程式事务管理: 编程式事务管理使用TransactionTemplate或者直接使用底层的PlatformTransactionManager。对于编程式事务管理,spring推荐使用TransactionTemplate。 2、声明式事务管理: 建立在AOP之上的。其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。 声明式事务管理不需要入侵代码,通过@Transactional就可以进行事务操作,更快捷而且简单。推荐使用
在我们使用Springboot框架来开发时,Springboot已经帮我们封装好对底层数据库事务的操作(Springboot版本2.3.1.RELEASE,整合了mybatis,数据库使用MySQL),降低了我们学习、操作使用数据库事务的成本。
三、SpringBoot事务配置使用
1、依赖的Jar包**pom.xml **
Spring Boot中实现事务没有额外的Jar包,还是基本的数据库访问包,比如mybatis
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
2、配置事务,使用注解@Transactional 事务
在Mybatis中使用事务,非常简单,只需要在方法上增加注解@Transactional,无需任何配置
@Service
public class UserServiceImpl implements UserService
{
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Autowired
private AuthService authService;
/**
* 更新user的权限,角色信息
*/
@Override
@Transactional
public updateUser(User user){
userMapper.updateUser(user);
roleService.insertRoleUser(user);
authService.updateAuthUser(user);
}
}
@Transactional可以作用于接口、接口方法、类以及类方法上。当作用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。因此可以在Service层和Controller层使用
虽然 @Transactional 注解可以作用于接口、接口方法、类以及类方法上,但是 Spring 建议不要在接口或者接口方法上使用该注解,因为这只有在使用基于接口的代理时它才会生效。另外, @Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。如果你在 protected、private 或者默认可见性的方法上使用 @Transactional 注解,这将被忽略,也不会抛出任何异常。
我看网上说还需要导入Spring boot提供的JDBC或JPA依赖,
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<scope>test</scope>
</dependency>
在_@SpringBootApplication 上添加注解@EnableTransactionManagement ,如下: _
关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。如果你添加的是 spring-boot-starter-data-jpa 依赖,框架会默认注入 JpaTransactionManager 实例。
我们通过Debug测试,就能知道自动注入的是 PlatformTransactionManager 接口的哪个实现类
//网上说在springboot的启动中添加注解@EnableTransactionManagement
@SpringBootApplication
@EnableTransactionManagement //开启事务管理注解模式 最新的版本可以省略
@MapperScan("com.xz.springboot.mapper") //扫描该包下所有的接口并为该接口生成实现类
public class SpringbootApplication {
@Bean
public Object testBean(PlatformTransactionManager platformTransactionManager){
System.out.println(">>>>>>>>>>" +
platformTransactionManager.getClass().getName());
return new Object();
}
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
注:
博主亲测,现在 版本Springboot确实不需要上述导入包和启动类添加@EnableTransactionManagement注解
传统工程中使用XML配置事务时,需要给DataSourceTransactionManager事务管理器配置数据源DataSource,那么Spring Boot进行自动配置的话, Spring Boot在注入DataSourceTransactionManager事务管理器时,Spring Boot会自动到Spring容器中寻找我们配置好的DataSource。也即是之前我们的手动操作,现在使用Spring Boot变成了自动化操作。
四、事务注解详解
默认遇到throw new RuntimeException(“…”);会回滚 需要捕获的throw new Exception(“…”);不会回滚
指定回滚
@Transactional(rollbackFor=Exception.class)
public void methodName() {
// 不会回滚
throw new Exception("...");
}
指定不回滚
@Transactional(noRollbackFor=Exception.class)
public ItimDaoImpl getItemDaoImpl() {
// 会回滚
throw new RuntimeException("注释");
}
如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.REQUIRED)
容器不为这个方法开启事务
@Transactional(propagation=Propagation.NOT_SUPPORTED)
readOnly=true只读,不能更新,删除
@Transactional (propagation = Propagation.REQUIRED,readOnly=true)
设置超时时间
@Transactional (propagation = Propagation.REQUIRED,timeout=30)
设置数据库隔离级别
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)
五、事务注解 – @Transactional 使用注意事项
1、不要在接口上声明@Transactional,而要在具体类的方法上使用@Transactional注解,否则注解可能无效。
2、将@Transactional放置在类级的声明中放在类声明会使得所有方法都有事务故@Transactional应该放在方法级别不需要使用事务的方法,就不要放置事务,比如查询方法。否则对性能是有影响的。
3、使用了@Transactional的方法,对同一个类里面的方法调用,@Transactional无效。比如有一个类Test,它的一个方法A,A再调用Test本类的方法B(不管B是否public还是private),但A没有声明注解事务,而B有。则外部调用A之后,B的事务是不会起作用的。(经常在这里出错)
4、使用了@Transactional的方法,只能是public,@Transactional注解的方法都是被外部其他类调用才有效,故只能是public。道理和上面的有关联。故在protected、private或者package-visible的方法上使用@Transactional注解,它也不会报错,但事务无效。
5、经过在ICORE-CLAIM中测试,效果如下:
6、抛出受查异常XXXException,事务会回滚。
7、抛出运行时异常NullPointerException,事务会回滚。
8、Quartz中,execute直接调用加了@Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)
9、异步任务中,execute直接调用加了@Transactional方法,可以回滚;间接调用,不会回滚。(即上文3点提到的)
10、在action中加上@Transactional,不会回滚。切记不要在action中加上事务。
11、在service中加上@Transactional,如果是action直接调该方法,会回滚,如果是间接调,不会回滚。(即上文3提到的)
12、在service中的private加上@Transactional,事务不会回滚。
先自我介绍一下,小编13年上师交大毕业,曾经在小公司待过,去过华为OPPO等大厂,18年进入阿里,直到现在。深知大多数初中级java工程师,想要升技能,往往是需要自己摸索成长或是报班学习,但对于培训机构动则近万元的学费,着实压力不小。自己不成体系的自学效率很低又漫长,而且容易碰到天花板技术停止不前。因此我收集了一份《java开发全套学习资料》送给大家,初衷也很简单,就是希望帮助到想自学又不知道该从何学起的朋友,同时减轻大家的负担。添加下方名片,即可获取全套学习资料哦
|