Spring5# 事务管理

目录

Spring 事务管理概述

事务是数据库操作的最基本单元,是逻辑上的一组操作。
典型场景:银行转账。

具有四个特性:
1)原子性
2)一致性
3)隔离性
4)持久性

事务一般添加到 service 层。

在 Spring 中有两种事务管理操作:
1. 编程式(一般不使用)
2. 声明式
	2.1 基于 XML配置文件 方式实现(一般不使用)
	2.2 基于注解方式实现
	
声明式事务管理底层原理是 AOP。

Spring 事务管理的 API
1. Spring 提供事务管理器接口,这个接口根据不同的框架提供不同的实现类。

编程式事务管理

//编程式事务管理操作
//Dao
@Override
public void reduceMoney(String id, int money) {
    String sql = "update t_account set money=money-? where id=?";
    Object[] args = {money, id};
    jdbcTemplate.update(sql, args);
}

@Override
public void addMoney(String id, int money) {
    String sql = "update t_account set money=money+? where id=?";
    Object[] args = {money, id};
    jdbcTemplate.update(sql, args);
}

//Service
@Service
public class UserService {
    @Autowired
    private UserDao userDao;

    public void dispote (String outId, String inId, int money) {
        //事务管理操作步骤
        try {
            //开启事务
            //业务操作
            userDao.reduceMoney("1", money);
            //模拟异常
            //int i = 10/0;
            userDao.addMoney("2", money);
            //没有异常,提交事务
        } catch (Exception e) {
            //出现异常,回滚事务
        }

    }
}

声明式事务管理

基于注解方式
步骤: 
1. 在 Spring 配置文件中创建事务管理器。
2. 在 Spring 配置文件中开启事务注解。
	2.1 引入名称空间 tx。
	2.2 开启事务注解。
3. 在 Service 类上面或在 Service 类中的特定方法上面添加事务注解 @Transactional。
	# 在类上面添加,则类中所有方法都添加事务。
<!-- 配置事务管理器(框架不同,实现类不同) -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 注入数据源 -->
	<property name="dataSource" ref="dataSource"></property>
</bean>


<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
//声明式事务管理,基于注解实现
@Service
@Transactional
public class UserService {
    @Autowired
    private UserDao userDao;

    public void dispote (String outId, String inId, int money) {
        //不需要再用 try catch 处理。如果用了,则事务不会生效
        userDao.reduceMoney("1", money);
        //模拟异常
        int i = 10/0;
        userDao.addMoney("2", money);
    }
}
声明式事务管理的参数配置
@Transactional()
1. propagation:事务传播行为,MySQL 默认是 REQUIRED
2. isolation:事务隔离级别
3. timeout:超时
4. readOnly:是否只读
5. rollbackFor:回滚
6. noRollbackFor:不回滚
事务传播行为
指多事务方法之间直接进行调用,这个过程事务是如何进行管理的。
	#事务方法:修改了数据库表数据的方法,如增删改。

Spring 框架有 7 种事务传播行为:
* 1)REQUIRED:如果有事务在运行,当前方法就在这个事务内运行,否则,就启动一个新的事务,并在自己的事务内运行。
* 2)REQUIRED_NEW:当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起。
3)SUPPORTS:如果有事务在运行,当前方法就在这个事务内运行,否则,它可以不在事务内运行。
4)NOT_SUPPORTED:当前的方法不应该运行在事务中,如果有运行的事务,将它挂起。
5)MANDATROY:当前的方法必须运行在事务内部,如果没有正在运行的事务,就抛出异常。
6)NEVER:当前的事务不应该在事务中运行,如果有运行的事务,就抛出异常。
7)NESTED:如果有事务在运行,当前的方法就应该在事务的嵌套事务内运行,否则,就启动一个新的事务,并在它自己的事务内运行。
事务隔离级别
事务隔离性:多事务操作之间不会产生影响。
如果不考虑隔离性,会产生很多问题:
	#读问题:脏读、不可重复读、虚(幻)读
	1. 脏读:一个未提交事务读取到另一个未提交事务的数据。
	2. 不可重复读:一个未提交事务读取到另一个提交事务修改后的数据。
	3. 虚读:一个未提交事务读取到另一个提交事务添加的数据。

事务的四种隔离级别:MySQL 默认是 REPEATABLE READ
1)READ UNCOMMITTED:读未提交。
	#有脏读、有不可重复读、有幻读
2)READ COMMITTED:读已提交。
	#无脏读、有不可重复读、有幻读
3)REPEATABLE READ:可重复读。
	#无脏读、无不可重复读、有幻读
4)SERIALIZABLE:串行化。
	#无脏读、无不可重复读、无幻读
其他参数
timeout:事务需要在规定的时间内提交,否则回滚。默认值是 -1,不超时。单位为秒。
readOnly:是否只读,默认值是 false
rollbackFor:设置出现哪些异常就进行事务回滚。
noRollbackFor:设置出现哪些异常就不进行事务回滚。
基于 XML 方式
步骤:
1. 配置事务
2. 配置通知
3. 配置切入点和切面
<!-- 配置事务 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<!-- 注入数据源 -->
	<property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置通知 -->
<tx:advice id="txAdvice">
    <!-- 配置事务参数 -->
    <tx:attributes>
        <!-- 指定规则,并在符合规则的方法上面添加事务 -->
        <tx:method name="dispote" propagation="REQUIRED"/>
        <!-- <tx:method name="dis*" propagation="REQUIRED"/> -->
    </tx:attributes>
</tx:advice>

<!-- 配置切入点和切面 -->
<aop:config>
    <!-- 配置切入点 -->
    <aop:pointcut id="pt" expression="execution(* work.service.UserService.*(..))"/>
    <!-- 配置切面 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
</aop:config>
完全注解方式
//TxConfig
@Configuration //定义配置类
@ComponentScan(basePackages = {"work"}) //开启组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("root");
        return dataSource;
    }

    //创建 JdbcTemplate 对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        //到 IOC 容器中根据类型找到 dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入 dataSource 到 jdbcTemplate
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) {
        DataSourceTransactionManager dataSourceTransactionManager
                = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

//test
@Test
public void testDispote() {
    ApplicationContext context
        = new AnnotationConfigApplicationContext(TxConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.dispote("1", "2", 100);
}
<!-- 被配置类取代的配置文件 -->
<context:component-scan base-package="jdbctemplate, work"></context:component-scan>

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
    <property name="url" value="jdbc:mysql://localhost:3306/user_db"></property>
    <property name="username" value="root"></property>
    <property name="password" value="root"></property>
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>

<!-- 配置 JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 注入 dataSource -->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源 -->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
上一篇:Spring5学习总结(一)


下一篇:学习Laya过程中遇到的问题