1. 什么是事务?
数据库事务(简称:事务)是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成
为什么需要事务?
事务是为解决数据安全操作提出的,事务控制实际上就是控制数据的安全访问。
Java事务的类型
Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。
-
事务的四个特性 (ACID)
- 原子性 — 事务是一个原子操作,由一系列动作组成,所谓原子性就是,这些操作要么全部成功,要么全部失败
- 一致性 — 一旦事务完成,不能这些事务动作是成功还是失败,这些动作都必须处在一致的状态,而不是一些完成,一些失败,这也可以看成是原子性的一个解释
- 隔离性 — 事务与事务之前是互不影响,相互隔离开来,有利于防止数据被损坏
- 持久性 — 一旦事务完成,无论发生什么系统错误,事务完成的结果,都不应该受到影响
-
并发事务引起的问题 — 脏读,幻读,不可重复读
- 脏读:事务A对数据进行修改,但是在还没有提交事务的时候,事务B读取了数据并进行修改,但是此时,事务B读取到的数据是事务A修改后的数据,如果事务A在提交之前因为某种原因对数据进行了回滚,现在数据应该是跟原来一样的,但是事务B中的数据还是事务A回滚前的数据,这就造成了数据不一致的问题,事务B读取到了事务A未提交的数据,出现了数据库中的值与事务获取的值不同的现象,也就是读到了不正确的值
- 不可重复读:一个事务A执行相同的多次查询,但是每次查询得到的数据不同,这通常是因为另一个事务B在事务A查询期间进行了数据更新,就是事务A在查询过程中读取到了事务B已经提交的数据
- 幻读:幻读和不可重复读相似,一个事务A在查询过程中,事务B插入了一些数据,事务A读到了事务B提交的心得数据,事务A的查询结果中就会多出事务B新加入的数据,不可重复读的重点是修改,而幻读的重点是增加或者删除
-
事务的隔离级别
-
mysql 的事务隔离级别:
- Read uncommitted (RU)读取未提交的数据 – 在这个事务级别中,所有事务都可以看到其他未提交事务的执行结果
- Read committed (RC) 读取已提交的数据 – 在这个事务级别中,可以看已提交事务的执行结果
- Repeatable read (RR) 可重复读 – mysql的默认事务隔离级别,确保同一事物的数据在并发读取时,看到的数据都是一样的,避免了不可重复读
- Serializable(S)可串行化 – 这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题
-
spring的事务隔离级别
- ISOLATION_DEFAULT – 使用后端数据库默认的隔离级别
- ISOLATION_READ_UNCOMMITTED --最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
- ISOLATION_READ_COMMITTED – 允许读取并发事务中已经提交的数据,可以阻止脏读,但是幻读和不可重复读仍然可能发生
- ISOLATION_REPEATABLE_READ – 保证同一事务在并发查询中查询到的数据都是相同的,可以阻止脏读,不可重复读,但是幻读仍然可能发生
- ISOLATION_SERIALIZABLE – 最高的事务隔离级别,完全服从ACID的事务隔离级别,阻止了脏读,不可重复读,幻读,但是效率低下
-
-
事务的传播行为
- PROPAGATION_REQUIRED – 如果存在一个事务,则支持当前事务,如果没有事务,则新开启一个事务
- PROPAGATION_SUPPORTS – 如果存在一个事务,则支持当前事务,如果没有事务,则不开启新的事务,以非事务的方式执行
- PROPAGATION_MANDATORY – 如果存在一个事务,则支持当前事务,如果没有事务,则抛出异常
- PROPAGATION_REQUIRES_NEW – 总是开启一个新的事务,如果原来存在一个事务,则将原来这个事务挂起,使用新创建的事务
- PROPAGATION_NOT_SUPPORTED – 总是以非事务的方式执行,并挂起任何存在的事务
- PROPAGATION_NEVER – 总是以非事务的方式执行,如果存在事务,则抛出异常
- PROPAGATION_NESTED如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按TransactionDefinition.PROPAGATION_REQUIRED 属性执行
-
只读事务
在对后端数据的查询操作,我们可以设置该操作为只读事务,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务对数据库的压力,毕竟事务也是要消耗数据库的资源的。 -
spring事务管理
- 事务的基本原理
Connection conn = DriverManager.getConnection();
try {
conn.setAutoCommit(false); //将自动提交设置为false ,默认是true,一条sql语句就是一次事务操作
执行CRUD操作
conn.commit(); //当两个操作成功后手动提交
} catch (Exception e) {
conn.rollback(); //一旦其中一个操作出错都将回滚,所有操作都不成功
e.printStackTrace();
} finally {
conn.colse();
}
- 编程式事务管理
配置数据源,事务管理器和TransactionTemplate
@Configuration
public class TxConfig {
/**
* 设置数据源
* @return
* @throws PropertyVetoException
*/
@Bean
public DataSource getDataSource() throws PropertyVetoException {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
/**
* 设置事务管理器,并且注入数据源
* @return
* @throws PropertyVetoException
*/
@Bean
public DataSourceTransactionManager dataSourceTransactionManager() throws PropertyVetoException {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(getDataSource());
return transactionManager;
}
/**
* 设置transactionTemplate,并且注入事务管理器
* @return
* @throws PropertyVetoException
*/
@Bean
public TransactionTemplate getTransactionTemplate() throws PropertyVetoException {
TransactionTemplate transactionTemplate = new TransactionTemplate();
transactionTemplate.setTransactionManager(dataSourceTransactionManager());
return transactionTemplate;
}
}
在业务层可以利用容器中的transactionTemplate进行业务代码的事务操作
public Object getObject(String str) {
/*
* 执行带有返回值<Object>的事务管理
*/
transactionTemplate.execute(new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus transactionStatus) {
try {
...
//....... 业务代码
return new Object();
} catch (Exception e) {
//回滚
transactionStatus.setRollbackOnly();
return null;
}
}
});
}
public void update(String str) {
/*
* 执行无返回值的事务管理
*/
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
try {
// .... 业务代码
} catch (Exception e){
//回滚
transactionStatus.setRollbackOnly();
}
}
});
}
- 声明式事务管理
声明式事务管理是基于AOP的,拦截业务层方法,在方法执行前开启事务,若方法无异常,则在方法执行后,提交事务
关键注解:
开启事务支持
@EnableTransactionManagement//开始事务支持
实现事务操作
@Transactional
本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务
- 区别:
编程式事务侵入到业务代码中,但是却提供了更详细的事务管理,而声明式事务管理基于AOP,能起到事务管理效果,也不会影响业务代码的具体实现