0. 前提
如果你要了解事务,请你先学会这些知识
- spring (IOC和AOP,AOP是重点)
- spring使用JDBCTemplate (这个很简单)
- 会配置一个数据源 (DBCP、C3p0或者Druid都行)
- 数据库至少会一种(Mysql、Oracle)
1. 什么是事务
对于最简单的理解就是,一个不可分割的事情,例如转账的时候,
A 转给 B 100块,那么就是A先减去100,然后B加上100。这个是一个不可分割的事情。就是一个简单的事务
实际上就是一组 sql一起执行
2. 事务的基本原则(AICD 原则)
原子性:
一致性:
隔离性:
持久性:
3. 事务隔离级别
事务的隔离级别有四种
- read uncommit (读未提交)
- read commit (读已提交)
- repeatable read (可重复读)
- serialzale (序列化)就是锁表
在事务的并发过程中有可能出现下面的问题,根据业务来选择使用哪种隔离级别
脏读:
幻读:
不可重复读:
第一类丢失更新:
第二类丢失更新:
数据库默认的隔离级别
例如:oracle、sqlserver默认使用的 read commited (读已提交)
mysql 默认使用的 repeatable read (可重复读)
Spring怎么配置的事务?
有几种配置,我这里就简单的说三种吧
- 使用TransactionTemplate实现事务
这种方法只需要配置事务管理器(DataSourceTransactionManager)和模板(TransactionTemplate)
<!-- 配置事务-->
<!-- 事务管理器-->
<bean id="dataTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 管理事务的模板 -->
<bean id="dataTxTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="dataTxManager"/>
</bean>
在使用事务的时候就要用 transactionTemplate 的 execute 来执行事务。。
@Component
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private TransactionTemplate transactionTemplate;
public void transfer(String from,String to,int mola){
transactionTemplate.execute((transactionStatus)->{
userDao.out(from,mola);
int num = 10;
if (num==10){
throw new RuntimeException("出错了");
}
userDao.in(to,mola);
return null;
});
}
}
这种方法的缺点就是对代码的侵入性比较强。。。
- 使用AOP来实现事务
第二种写法就是用Aop动态代理来实现事务,这种写法是现在最常用的方法
具体的xml配置如下
<!-- 事务管理器-->
<bean id="dataTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="dataTxManager">
<tx:attributes>
<tx:method name="get*" propagation="NOT_SUPPORTED" />
<tx:method name="in" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="transfer" propagation="REQUIRED" rollback-for="Exception" />
<!-- ... -->
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的类 -->
<aop:config>
<aop:pointcut id="transcationPointcut" expression="execution(public void com.liliya.service.UserService.*(..))"/>
<aop:advisor pointcut-ref="transcationPointcut" advice-ref="txAdvice" />
</aop:config>
- 使用注解的方式来实现事务
使用注解的好处就是没有配置文件了。。使用事务的时候就在类上添加一个
@Transactional
就好了,,
当然,, 如果某个方法上不想使用事务的话,那就用@Transactional(propagation = Propagation.NOT_SUPPORTED)
这个注解放到那个方法上。。。qwq
AppConfig.java
@ComponentScan("com.liliya")
@EnableAspectJAutoProxy
@EnableTransactionManagement
@Configuration
public class AppConfig {
@Bean
public TransactionManager getTransactionManager() {
DataSourceTransactionManager manager = new DataSourceTransactionManager();
manager.setDataSource(dataSource());
return manager;
}
@Bean
public JdbcTemplate getJdbcTemplate() {
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/txdemo?serverTimezone=UTC");
dataSource.setUser("root");
dataSource.setPassword("admin");
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
return dataSource;
}
}
例如我是这么写的
//转账
@Component
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
//转账
public void transfer(String from,String to,int mola){
userDao.out(from,mola);
int num = 10;
//故意出一个错误,导致转账失败
if (num==10){
throw new RuntimeException("出错了");
}
userDao.in(to,mola);
}
}
测试类就要注解的来写了
@Test
public void tss(){
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
UserService bean = context.getBean(UserService.class);
bean.transfer("刘国威","屎魔翔",100);
System.out.println("跑完了!!!");
}
其他补充
- 用到了依赖有这些
<!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.3.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.23</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.4</version>
</dependency>
- 使用事务管理器
DataSourceTransactionManager
是需要配置数据源dataSource
的
每种数据源的配置都不同,所以我这里没有贴出来。。
而且这个百度一大堆