1. 事务使用的示例,便于复制
1. 事务注解在实际中的使用
// 代码是用于测试 隔离级别时用的,没有任何实际意义
@Transactional(isolation = Isolation.SERIALIZABLE, readOnly = true, timeout = 10)
public void getAccountByActon(String acton) {
for (int i = 0; i < 10; i++) {
AccountBean act01 = accountDao.selectAccount(acton);
System.out.println(i + "查询到用户名" + acton + "的账户" + act01.toString());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("查询事务结束");
}
2. 配置事务,使用注解的方式
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan({"com.example.bank"})
/*<!-- 事务注解驱动器-->
<tx:annotation-driven transaction-manager="transactionManager"/>*/
@EnableTransactionManagement
public class SpringConfig {
/*<!-- 配置德鲁伊连接池的数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/batis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>*/
@Bean
public DruidDataSource getDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/batis");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
/*<!--配置spring JdbcTemplate 使用德鲁伊连接池-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"/>
</bean>*/
@Bean(name="jdbcTemplate") // 在dao中使用,不加名字也行,dao中要按类型注入
public JdbcTemplate getJdbcTemplate(DataSource dataSource) { // 参数会自动使用上面的DataSouse
// JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
/*<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>*/
@Bean
public DataSourceTransactionManager getTransactionManager(DataSource dataSource) {
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}
3. 配置事务,使用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:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 配置德鲁伊连接池,及其使用的数据源 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/batis"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!--配置JdbcTemplate使用德鲁伊连接池-->
<!-- JdbcTemplate是Spring提供的一个JDBC模板类,是对JDBC的封装,简化JDBC代码-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druidDataSource"/>
</bean>
<!-- 事务注解驱动器-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
2. 事务的配置属性
1. 事务注解源码
@Target({ElementType.TYPE, ElementType.METHOD}) // 用于类、接口、方法
@Retention(RetentionPolicy.RUNTIME)
@Inherited // 可继承
@Documented
@Reflective
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
@AliasFor("value")
String transactionManager() default "";
String[] label() default {};
Propagation propagation() default Propagation.REQUIRED; // 事务的传播行为
Isolation isolation() default Isolation.DEFAULT; // 隔离级别
int timeout() default -1; // 事务超时时间
String timeoutString() default "";
boolean readOnly() default false; // 事务为只读事务
Class<? extends Throwable>[] rollbackFor() default {}; // 出现哪些异常时回滚事务
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {}; // 出现哪些异常时不回滚事务
String[] noRollbackForClassName() default {};
}
2. 事务的传播行为
什么是传播行为:a()方法上有事务,b()方法上也有事务,当a()方法执行过程中调用了b()方法,事务是如何传递的?合并到一个事务里?还是开启一个新的事务?这就是事务传播行为
- 使用
@Transactional(propagation= Propagation.REQUIRED)
- propagation的值是枚举类型
public enum Propagation {
REQUIRED(0), // 支持当前事务,如果不存在就新建一个(默认)【没有就新建,有就加入】
SUPPORTS(1), // 支持当前事务,如果当前没有事务,就以非事务方式执行【有就加入,没有就不管了】
MANDATORY(2), // 必须运行在一个事务中,如果当前没有事务正在发生,将抛出一个异常【有就加入,没有就抛异常】
REQUIRES_NEW(3), // 开启一个新的事务,如果一个事务已经存在,则将这个存在的事务挂起【不管有没有,直接开启一个新事务,开启的新事务和之前的事务不存在嵌套关系,之前事务被挂起】
NOT_SUPPORTED(4), // 以非事务方式运行,如果有事务存在,挂起当前事务【不支持事务,存在就挂起】
NEVER(5), // 以非事务方式运行,如果有事务存在,抛出异常【不支持事务,存在就抛异常】
NESTED(6); // 如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于外层事务进行提交或回滚。如果外层事务不存在,行为就像REQUIRED一样。【有事务的话,就在这个事务里再嵌套一个完全独立的事务,嵌套的事务可以独立的提交和回滚。没有事务就和REQUIRED一样。】
3. 事务的隔离级别
参考:三大读问题和四个隔离级别(链接)
@Transactional(isolation = Isolation.SERIALIZABLE)
- isolation 的值是枚举类型:
public enum Isolation {
DEFAULT(-1), // 使用数据库的默认隔离级别。不同的数据库可能有不同的默认隔离级别。例如:mysql是READ_COMMITTED,Oracle默认是READ_UNCOMMITTED
READ_UNCOMMITTED(1), // 读未提交
READ_COMMITTED(2), // 读已提交
REPEATABLE_READ(4), // 可重复读
SERIALIZABLE(8); // 序列化
4. 事务的超时时间
默认的值是-1,没有超时时间
超时时间指的是哪段时间?
最后一条DML语句(数据库操作语句-增删改查)执行之前的时间
public void dao() {
select ....
insert .....
delete .....
update .....
// 事务的超时时间指的就是方法执行开始到这里的时间
delte .... // 最后一条dml语句
// 在最后一条dml语句之后执行再多的代码,使用再多时间也不会被计入超时时间
}
5. 只读事务
解释: 事务中只包含查询语句,启动spring的优化策略。提高select语句执行效率
@Transactional(readOnly = true)
6. 设置哪些异常回滚事务
@Transactional(rollbackFor = RuntimeException.class)
7. 设置哪些异常不回滚事务
@Transactional(noRollbackFor = NullPointerException.class)