对于一些业务网站而言 , 产 品库存的扣减、 交易记录以及账户都必须是要么 同时成功, 要么 同时失败 ,这便是一种事务机制,而在一些特殊的场景下 ,如一个批处理 ,它将处理多个交易 ,但是在一些交易中发生了异常 , 这个时候则不能将所有的交易都回滚。如果所有的交易都回渎,那么那些本能够正常处理的业务也无端地被回滚。 通过 Spring 的数据库事务传播行为,可以很方便地处理这样的场景 。
首先配置数据库信息
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver=com.mysql.jdbc.Driver
spring.datasource.tomcat.max-idle=10
spring.datasource.tomcat.max-active=50
spring.datasource.tomcat.max-wait=10000
spring.datasource.tomcat.initial-size=5
一、JDBC数据库事务
@Service
public class JdbcServiceImpl implements JdbcService{
@Autowired
private DataSource dataSource=null;
@Override
public int insertUser(String name,String note){
Connection conn=null;
int result=0;
try{
//获取连接
conn=dataSource.getConnection();
//开启事务
conn.setAutoCommit(false);
//设置隔离级别
conn.setTransactionIsolation(TransactionIsolationLevel.RRAD_COMMITED.getLevel());
//执行SQL
PreparedStatement ps=conn.prepareStatement("insert into t_user(user_name,note)values(?,?)");
ps.setString(1,userName);
ps,setString(2,note);
result=ps.executeUpdate();
//提交事务
conn.commit();
}catch(Exception e){
//回滚事务
if(conn !=null){
try{
conn.rollback();
}catch(SqlException e1)
e1.printStackTrace();
}
}
e.printStackTrace();
}finally{
try{
if(conn !=null && !conn.isClosed()){
conn.close()
}
}catch(SQLException e){
e.printStackTrace();
}
}
return result;
}
}
使用JDBC需要使用大量的try...catch...finally...语句,和关于连接的获取关闭,事务的提交和回滚。使用Hibernate、myBatis可以减少try...catch...finally的使用,但是依旧不能减少开闭数据库连接和事务控制的代码。而AOP可以解决这样的问题。
二、Spring 声明式事务的使用
Spring AOP 会把我们的代码织入到约定的流程中,同样,同样执行的SQL的代码也可以织入的哦Spring 约定的数据库事务的流程中。首先要掌握这个约定
1.Spring 声明式数据库事务约定
对于事务需要通过标注告诉Spring在什么地方启用数据库事务功能,对于声明式数据库,是使用@Transactional进行标注的。
@Transactional 这个注解可以标注类和方法上,当它标注在类上时,代表这个类所有公共(public)非静态的方法东将启用事务功能。在@Transactonal 中还可以进行事务的隔离级别和传播行为,异常类型的配置。这些配置,是在Sprng IoC容器在加载时就会将这些配置信息解析出来,然后帮这些喜喜存储到事务定义器(TransactonDefinition接口实现的类)里,并且记录了那些类或者方法需要启动事务的功能,采取什么策略去执行事务。在这个过程中我们所需要做的就是给需要事务的类和方法标注@Transactional并配置属性
spring的事务处理机制
Spring 通过对注解@Transactional 属性配置去设置数据库事务 , 跟着 Spring 就会
开始调用开发者编写 的业务代码 。 执行开发者 的业务代码,可能发生异常,也可能不发生异常 。 在Spring 数据库事务 的流程 中,它会根据是否发生异常采取不同的策略 如果都没有发生异常, Spring 数据库拦截器就会帮助我们提交事务 , 这点也并不需要我们干预 。如果发生异常,就要判断一次事务定义器内的配置,如果事务定义器己经约定了该类型的异常不回段事务就提交事务 , 如果没有任何配置或者不是配置不回滚事务的异常,则会回滚事务,并且将异常抛出 , 这步也是由事务拦截器完成的。论发生异常与否, Spring 都会释放事务资源,这样就可以保证数据库连接池正常可用了,这
也是由 Spring 事务拦截器完成的内容 。
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao=null;
@Override
@Transactional
public int insertUser(User user){
return userDao.insertUser(user);
}
}
2.@Transactional配置项
spring中关于数据库属性是由@Transactional 来配置的,源码如下所示
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetenttionPolicy.RUNTIME)
@InHerited
@Documented
public @interface Transactional{
//通过bean name指定事务管理器
@ALiasFor("transactionManager")
String value() default"";
//同value属性
@ALiasFor(value)
String transactionManager() default"";
//指定传播行为
Propagation propagation() default Propagetion.REQUIRED;
//指定隔离级别
Isolation isolation() default IsoLation.DEFAULT
//指定超时时间(单位为秒)
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//时候只读事务
boolean readOnly() default false;
//方法发生指定异常时回滚,默认是所有的异常都回滚
Class<? extends Throwable>[] rollbackFor() default();
//方法发生指定异常名称时回滚,默认是所有异常都回滚
String[] rollbackForClassName() default();
//方法发生指定异常时不回滚,默认是所有的 异常都回滚
Class<? extends Throwable>[] noRollBackFor() default();
//方法在发生指定异常名称时不回滚,默认所有异常都回滚
String noRollbackForClassName() default();
}
- value和transactionManager属性是配置一个Spring的事务管理器
- timeout是事务允许存在的时间戳
- read Only 属性 定义的是事务是否是只读事务;
- rollbackFor 、 rollbackForClassName 、 noRollbackFor 和 noRollbackForClassName 都是指定异常 ,我们从流程中可以看到在带有事务的方法时,可能发生异常,通过这些属性的设置可以指定在什么异常的情况下依旧提交事务,在什么异常的情况下回滚事务 , 这些可以根据自己的需要进行指定
- propagation 传播行为(重点)
- isolation隔离级别(重点)
@Transactional可以放在接口上也可以放在实现类上,spring推荐放在实现类上。
3.Spring 事务管理器
在spring的事务流程中事务的打开回滚和提交是由事务管理器来完成的。事务管理器的顶层接口是PlatformTransactionManager.
当我们使用myBatis框架时最常用的的是DataSourceTransactionManager它实现了PlatformTransactionManager接口,关于PlatformTransactionManager的源码如下
public interface PlatformTransactionManager{
//获取事务,它还会设置数据属性
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
//提交事务
void commit(TransactionStatus status) throws TransactionException;
//回滚事务
void rollback(TransactionStatus status) throws TransactionException;
}
Spring在事务管理时,就是将这些方法按照约定织入对应的流程,其中getTransaction方法的参数是一个事务定义器,它依赖于我们配置的@Transactional的配置项生成的。于是通过它就能够设置事务的属性了
在 Spring Boot 中,当你依赖于 mybatis-spring-boot-starter 之后 , 它会自动创建一个 DataSourceTransactionManager 对象 ,作为事务管理器 ,如果依赖于 spring-boot-starter-data-j pa ,则它会自动创建JpaTransactionManager 对象作为事务管理器 ,所以我们一般不需要自己创建事务管理器而直接使用它们即可。
4.事务的使用
创建一张表
create table t_user(
id int(12) auto_increment,
user_name varchar(60) not null,
note varchar(512),
primary key(id)
);
创建POJO
package com.demo.dao
@Repository
public interface UserDao{
User getUser(Long id);
int intsertUser(User user);
}
服务接口实现类
package com.demo.service.demo
@Service
public class UserServiceImpl implements UserService{
@Autowired
private UserDao userDao=null;
@Override
@Transactional(isolation=Isolation.READ_COMMITTED,timeout=1)
public int insertUser(User user){
return userDao.insertUser(user);
}
@Override
@Transactional(isolation=Isolation.READ_COMMITTED,timeout=1)
public User insertUser(id){
return userDao.getUser(id);
}
}