什么是事务:
一组操作,要么全部执行,要么全部不执行
常见场景,银行转账
事物的特性:ACID
- 原子性
- 一致性
- 隔离性
- 持久性
实际情况下一一般不会全部保证
事物的属性:
隔离级别 :多个事务同时下的控制,越高越能保证ACID,但是性能随之下降
传播行为 :事务服务之间相互调用,事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播
是否只读 :事务内不允许修改操作,此时再进行写操作会出现错误
事务超时 :事务允许执行的最长时间,如果超过这个时间还没完成则自动回滚
回滚规则 :事务的回滚规则
事物的隔离级别:
-
读未提交
-
读已提交
-
可重复读
-
序列化
TransactionDefinition.ISOLATION_READ_UNCOMMITTED: 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED: 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
TransactionDefinition.ISOLATION_REPEATABLE_READ: 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE: 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
事物的传播行为:
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
-
TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
-
TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-
TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
-
TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
-
TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-
TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
-
TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
spring事务(编程式)
三个主要接口
1. TransactionDefinition: TransactionDefinition定义事务,它包含了事务的静态属性,比如:事务传播行为、超时时间等等。Spring 为我们提供了一个默认的实现类:DefaultTransactionDefinition,该类适用于大多数情况
**2. PlatformTransactionManager: **PlatformTransactionManager 用于执行具体的事务操作
Public interface PlatformTransactionManager{
TransactionStatus getTransaction(TransactionDefinition definition)
throws TransactionException;
void commit(TransactionStatus status)throws TransactionException;
void rollback(TransactionStatus status)throws TransactionException;
}
根据底层所使用的不同的持久化 API 或框架,PlatformTransactionManager 的主要实现类大致如下:
- DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。
- HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。
- JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。
- 另外还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等
3. TransactionStatus: 事物的状态
public interface TransactionStatus{
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
}
下面是实例讲解
使用mysql、Spring、mybatis
首先创建数据库,使用mysql
CREATE TABLE `account` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`accountName` varchar(20) DEFAULT NULL,
`money` decimal(10,2) DEFAULT NULL,
`password` varchar(20) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8
创建实体类
package com.liu.pojo;
import java.math.BigDecimal;
public class Account {
private int id;
private String accountName;
private BigDecimal money;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
创建mapper接口
package com.liu.mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
public interface AccountMapper {
int addMoney(@Param("accountName") String accountName,@Param("money") BigDecimal money);
int reduceMoney(@Param("accountName") String accountName,@Param("money") BigDecimal money);
}
创建mapper映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.liu.mapper.AccountMapper">
<update id="addMoney" parameterType="java.math.BigDecimal">
update account set money = money + ${money}
where accountName = #{accountName}
</update>
<update id="reduceMoney" parameterType="java.math.BigDecimal">
update account set money = money - ${money}
where accountName = #{accountName}
</update>
</mapper>
service层
package com.liu.service;
import java.math.BigDecimal;
public interface AccountService {
//转账
boolean trans(String fromAccount, String toAccount, BigDecimal money);
}
service实现类
调用Transaction的execute方法
execute方法的参数是TransactionCallback接口,一般我们使用匿名类来直接实现
将需要进行事务处理的代码放进 TransactionCallback接口方法 doInTransaction中
doInTransaction方法有一个TransactionStatus 类型参数
发生运行时异常时调用transactionStatus.setRollbackOnly()方法,对事物进行回滚
直接看代码:
package com.liu.service.Impl;
import com.liu.mapper.AccountMapper;
import com.liu.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import java.math.BigDecimal;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Autowired
private TransactionTemplate transactionTemplate;
@Override
public boolean trans(String fromAccount, String toAccount, BigDecimal money) {
/**
* 对于不需要返回值的情况
* TransactionCallback 接口有一个子接口 TransactionCallbackWithoutResult
* 或者直接TransactionCallback返回无意义的值
* */
return transactionTemplate.execute(new TransactionCallback<Boolean>(){
@Override
public Boolean doInTransaction(TransactionStatus transactionStatus) {
boolean result;
try{
//需要事务
int a = accountMapper.reduceMoney(fromAccount,money);
int c = 1 / 0;//出了异常,钱就没了
int b = accountMapper.addMoney(toAccount,money);
result = a>0&&b>0;
}catch (Exception e){
transactionStatus.setRollbackOnly();//回滚
result = false;
e.printStackTrace();
}
return result;
}
});
}
}
配置文件:
<?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:context="http://www.springframework.org/schema/context"
xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://mybatis.org/schema/mybatis-spring
http://mybatis.org/schema/mybatis-spring.xsd">
<context:property-placeholder location="classpath:properties/jdbc.properties"/>
<context:component-scan base-package="com.liu"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driver}"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sqlSessionFactory">
<property name="dataSource" ref="druidDataSource"/>
<property name="configLocation" value="classpath:mybatis/SqlMapConfig.xml"/>
<!--mapperLocations - 如果MyBatis映射器XML文件在和映射器类在不同路径下则需要配置-->
<!--<property name="mapperLocations" value="classpath:com/liu/mymapper/*.xml" />-->
</bean>
<!--<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">-->
<!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
<!--<property name="basePackage" value="com.liu.mapper"/>-->
<!--</bean>-->
<!--or -->
<mybatis:scan base-package="com.liu.mapper"/>
<!--jdbc事务-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg ref="druidDataSource" />
</bean>
<!--事务模板 -->
<bean name="TransactionTemplate " class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
<property name="isolationLevelName" value="ISOLATION_REPEATABLE_READ"/>
</bean>
</beans>
测试类:
package com.liu;
import com.liu.mapper.AccountMapper;
import com.liu.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.math.BigDecimal;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/applicationContext.xml")
public class TestTranscation {
@Autowired
private AccountService accountService;
@Test
public void test(){
boolean f = accountService.trans("lisi", "zhangsan", BigDecimal.valueOf(100));
System.out.println(f);
}
}
我们让lisi给zhangsan转账100元,但是由于转账过程中抛出异常,
我已经执行了setRollbackOnly()操作,所以事务回滚,钱并不会转出
思维导图