事务基础
事务的概念
- 事务指数据库中多个操作合并在一起形成的操作序列
- 由多个操作组成的一个逻辑单元,组成这个逻辑单元的多个操作要么都成功要么都失败
事务的特性
-
原子性(Atomicity)
- 指事务是一个不可分割的整体,其中的操作要么全执行或全不执行
-
一致性(Consistency)
- 事务前后数据的完整性必须保持一致
-
隔离性(Isolation)
- 事务的隔离性是多个用户并发访问数据库时,
- 数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,
- 多个并发事务之间要相互隔离
-
持久性(Durability)
- 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,
- 接下来即使数据库发生故障也不应该对其有任何影响
名词解释
-
脏读
- 允许读取未提交的信息
- 解决方案:表级读锁
-
不可重复读
- 读取过程中单个数据发生了变化
- 解决方案:行级写锁
-
幻读
- 读取过程中数据条目发生了变化
- 解决方案:表级写锁
隔离级别
-
Read uncommitted
- 存在:脏读、不可重复读、幻读
-
Read committed
- 存在:不可重复读、幻读
- 解决:脏读
-
Repeatable read
- 存在:幻读
- 解决:脏读、不可重复读
-
Serializable
- 解决:脏读、不可重复读、幻读
编程式事务控制
applicationContext.xml 配置
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:*.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="accountService" class="com.asaawan.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.asaawan.domain"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.asaawan.dao"/>
</bean>
<!-- aop 控制事务 -->
<!-- 为 TxAdvice 注入数据源 -->
<bean id="txAdvice" class="com.asaawan.aop.TxAdvice">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 为 transfer 方法添加事务控制 -->
<aop:config>
<aop:pointcut id="pt" expression="execution(* *..transfer(..))"/>
<aop:aspect ref="txAdvice">
<aop:around method="transactionManager" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
</beans>
TxAdvice 配置
public class TxAdvice {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Object transactionManager(ProceedingJoinPoint pjp) throws Throwable {
// 开启事务
// 创建事务管理器并为事务管理器设置与数据层相同的数据源
PlatformTransactionManager ptm = new DataSourceTransactionManager(dataSource);
// 创建事务定义对象
TransactionDefinition td = new DefaultTransactionDefinition();
// 创建事务状态对象,用于控制事务执行
TransactionStatus ts = ptm.getTransaction(td);
// 执行切入点方法
Object ret = pjp.proceed(pjp.getArgs());
// 提交事务
ptm.commit(ts);
// 返回数据
return ret;
}
}
声明式事务控制
applicationContext.xml 配置
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:*.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.asaawan.domain"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.asaawan.dao"/>
</bean>
<bean id="accountService" class="com.asaawan.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- tx 控制事务 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--定义事务管理的通知类-->
<tx:advice id="txAdvice" transaction-manager="txManager">
<!--定义控制的事务-->
<tx:attributes>
<tx:method
name="transfer"
read-only="false"
timeout="-1"
isolation="DEFAULT"
no-rollback-for=""
rollback-for=""
propagation="REQUIRED"
/>
<!--<tx:method name="transfer" read-only="false"/>-->
<!--name="transfer" 待添加事务的方法名表达式(支持*号通配符),例如get*
read-only="false" 设置事务的读写属性,true为只读,false为只写
timeout="-1" 设置事务超时时长,单位秒
isolation="DEFAULT" 设置事务的隔离级别,该隔离级设定是基于Spring的设定,非数据库端
no-rollback-for="" 设置事务中不回滚的异常,多个异常间使用 ',' 分割
rollback-for="" 设置事务中必回滚的异常,多个异常间使用 ',' 分割
propagation="REQUIRED" 设置事务的传播行为-->
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.asaawan.service.*Service.*(..))"/>
<aop:pointcut id="pt2" expression="execution(* com.asaawan.dao.*.b(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt2"/>
</aop:config>
</beans>
applicationContext.xml 注解
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:property-placeholder location="classpath:*.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="typeAliasesPackage" value="com.asaawan.domain"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.asaawan.dao"/>
</bean>
<bean id="accountService" class="com.asaawan.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"/>
</bean>
<!-- tx 事务控制注解版 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解驱动 -->
<tx:annotation-driven transaction-manager="txManager"/>
</beans>
AccountService 配置
//对当前接口的所有方法添加事务
//添加事务一般在 *Service 添加
@Transactional(isolation = Isolation.DEFAULT)
public interface AccountService {
/**
* 转账操作
* @param outName 出账用户名
* @param inName 入账用户名
* @param money 转账金额
*/
//对当前方法添加事务,该配置将替换接口的配置
@Transactional(
readOnly = false,
timeout = -1,
isolation = Isolation.DEFAULT,
rollbackFor = {}, //java.lang.ArithmeticException.class, IOException.class
noRollbackFor = {},
propagation = Propagation.REQUIRED
)
public void transfer(String outName, String inName, Double money);
}
声明式事务控制纯注解
TxManagerConfig 注解
public class TxManagerConfig {
@Bean
public PlatformTransactionManager getTransactionManager(@Autowired DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
}
SpringConfiguration 注解
@Configuration
@ComponentScan("com.asaawan")
@PropertySource(value = {"classpath:jdbc.properties"},ignoreResourceNotFound = true)
@Import({JdbcConfiguration.class, MyBatisConfiguration.class})
@EnableTransactionManagement
public class SpringConfiguration {
}
aop:advice 与 aop:advisor
aop:advice
- 配置的通知类可以是普通 java 对象,不实现接口,也不使用继承关系
aop:advisor
- 配置的通知类必须实现通知接口
MethodBeforeAdvice
AfterReturningAdvice
ThrowsAdvice
- ...
事务的传播行为
概念
- 指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行