spring_day04
事务的概念回顾
事务的概念:
一个包含多个操作的逻辑单元,要么同时成功,要么同时失败。
事务的四大特性:
-
原子性
-
一致性
-
持久性
-
隔离性
事务并发带来的读问题:
-
脏读
-
不可重复读
-
虚读/幻读
隔离级别:
- read uncommitted
- read committed
- repeatable read
- serializeable
事务管理的核心对象
事务管理器对象:PlatformTransactionManager
事务管理器的顶层接口,定义了事务操作最基础的提交,回滚等方法。在使用时,需要根据不同的dao层技术选择不同的实现类,如果dao层的实现技术为jdbc或者mybatis,则应选择DataSourceTransactionManager
事务定义对象:TransactionDefinition
用于封装事务相关属性
- 隔离级别
- 是否只读
- 传播行为
- 超时时间
事务状态对象:TransactionStatus
用于封装事务的状态
使用什么样的事务管理器,设置哪些属性,最终决定事务的状态。
编程式事务
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void transfer(String outName, String inName, Double money) {
// 准备事务对象(开启事务)
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = manager.getTransaction(definition);
try {
accountDao.outMoney(outName, money);
int i = 1 / 0;
accountDao.inMoney(inName, money);
// 提交事务
manager.commit(status);
} catch (Exception e) {
e.printStackTrace();
// 回滚事务
manager.rollback(status);
}
}
}
AOP改造编程式事务
1)业务层只保留核心业务功能
public class AccountServiceImpl implements AccountService {
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
public void transfer(String outName, String inName, Double money) {
accountDao.outMoney(outName, money);
int i = 1 / 0;
accountDao.inMoney(inName, money);
}
}
- 编写事务通知类,通过环绕通知方法实现事务管理
public class TxAdvice {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Object tx(ProceedingJoinPoint pjp) {
// 准备事务管理相关对象
DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);
TransactionDefinition definition = new DefaultTransactionDefinition();
TransactionStatus status = manager.getTransaction(definition);
try {
// 调用切入点方法
Object rtValue = pjp.proceed();
// 提交事务
manager.commit(status);
return rtValue;
} catch (Throwable throwable) {
throwable.printStackTrace();
// 回滚事务
manager.rollback(status);
return null;
}
}
}
3)完成aop的配置
<!--配置通知对象-->
<bean id="txAdvice" class="com.itheima.advice.TxAdvice">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置aop-->
<aop:config>
<!--公共切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<!--配置切面-->
<aop:aspect ref="txAdvice">
<aop:around method="tx" pointcut-ref="pt1"/>
</aop:aspect>
</aop:config>
基于xml的声明式事务
声明式事务的概念
所谓的声明式事务指的是通过配置的方式实现事务管理。由于事务管理的代码是模板写法,几乎固定。所以spring内部对事务管理进行了再一次封装。
- 引入aspectjweaver坐标
由于声明式事务的底层原理还是aop,所以需要引入aspectjweaver坐标
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.4</version>
</dependency>
2)在spring核心配置文件中完成配置
-
配置事务管理器
-
配置事务通知
事务管理器想要能进行事务管理需要设置事务的相关属性
-
配置aop
在进行aop配置时,不需要配置
aop:aspect
切面,而是配置aop:advisor
顾问,顾问知道如何管理事务,只用将通知和需要管理的业务方法告知它即可。
<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:tx="http://www.springframework.org/schema/tx"
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/tx http://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 id="accountService" class="com.itheima.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.itheima.domain"/>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.itheima.dao"/>
</bean>
<!--配置事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务的通知:给事务管理器对象指定相关的属性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*"/>
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<!--抽取公共切入点表达式-->
<aop:pointcut id="pt1" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
</aop:config>
</beans>
tx:method相关属性介绍
属性名 | 解释 |
---|---|
method | 需要被事务管理的方法名,支持通配符 |
read-only | 是否只读,默认值为false, 查询方法可以配置为true,提升connection底层效率 |
isolation | 事务的隔离级别,默认为DEFAULT,跟数据库的默认隔离级别一致 |
timeout | 事务的超时时间,默认为-1 |
rollback-for | 进行回滚的异常类型,写法为异常的全类名 多个值之间通过,分割 |
no-rollback-for | 不进行回滚的异常类型,写法为异常的全类名 多个值之间通过,分割 |
propagation | 事务的传播行为 |
事务传播行为
传播行为的概念
事务传播行为(Propagation Behavior)指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行。 所以讨论传播行为的前提是有业务方法的相互嵌套。 例如methodA事务方法调用methodB方法时,传播行为就是用来控制,methodB的事务该如何进行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jhBkWElZ-1608041302511)(.\传播行为.png)]
基于xml结合注解的声明式事务
1)在xml配置文件中删除tx:advice
和事务的aop相关配置
2)配置事务的注解驱动
<!--配置事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解驱动-->
<tx:annotation-driven transaction-manager="transactionManager"/>
- 在需要被事务管理的业务层接口,或者业务层实现类上,或者接口和类的方法上添加
@Transactional
注解
@Transactional
public interface AccountService {
/**
* 转账操作
* @param outName 出账用户名
* @param inName 入账用户名
* @param money 转账金额
*/
public void transfer(String outName, String inName, Double money);
}
通常情况注解设置在业务层接口上,接口上的为通用配置,对于特殊的方法,可以在方法上添加特殊的配置,也就是方法上的@Transactional
注解优先级高于接口和类上的,可以在该注解中配置事务的属性,配置格式如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bUrC1VvR-1608041302515)(C:\Users\65710\Desktop\spring_day04\注解属性.png)]
纯注解驱动
实现纯注解驱动,核心是通过EnableTransactionManagement
注解替换<tx:annotation-driven/>
标签
1)编写Spring核心配置类SpringConfig
@Configuration
@ComponentScan("com.itheima")
@EnableTransactionManagement // 事务的注解驱动
@Import(MybatisConfig.class)
public class SpringConfig {
// 配置事务管理器
@Bean("transactionManager")
public PlatformTransactionManager getTransactionManager(DataSource dataSource){
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource);
return manager;
}
}
2)编写Jdbc配置类JdbcConfig
@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
@Bean("dataSource")
public DataSource getDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
- 编写Mybatis配置类MybatisConfig
@Configuration
@Import(JdbcConfig.class)
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setTypeAliasesPackage("com.itheima.domain");
return factoryBean;
}
@Bean
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.itheima.dao");
return configurer;
}
}
taSource);
factoryBean.setTypeAliasesPackage(“com.itheima.domain”);
return factoryBean;
}
@Bean
public MapperScannerConfigurer getMapperScannerConfigurer(){
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("com.itheima.dao");
return configurer;
}
}