Spring+JdbcTemplate进行事务管理
1.概念
(1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
(2)典型场景:银行转账
2.事务四个特性(ACID)
(1)原子性:指事务是一个不可分割的整体,类似一个不可分割的原子
(2)一致性:保障事务前后这组数据的状态是一致的。要么都是成功的,要么都是失败的.
(3)隔离性:多个事务之间要相互隔离,不能互相干扰
(4)持久性:指事务一旦被提交,这组操作修改的数据就真的的发生变化了。即使接下来数据库故障也不应该对其有影响。
3.环境准备
-
创建数据库表account,添加记录

-
配置JdbcTemplate
@Configuration @ComponentScan(basePackages = "com.apple") public class TxConfig { @Bean("dataSource") public DruidDataSource getDruidDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/exercise"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate; } } -
创建service,搭建dao,完成对象创建和注入关系
service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
@Service public class AccountService { //注入Dao @Autowired private AccountDao accountDao; }public interface AccountDao { }@Repository public class AccountDaoImpl implements AccountDao{ //注入JdbcTemplate @Autowired private JdbcTemplate jdbcTemplate; } -
在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账的方法)
@Repository public class AccountDaoImpl implements AccountDao{ @Autowired private JdbcTemplate jdbcTemplate; //多钱 @Override public void addMoney() { String sql = "update account set money = money + ? where username = ?"; jdbcTemplate.update(sql,100,"mary"); } //少钱 //lucy转账100给mary @Override public void reduceMoney() { String sql = "update account set money = money - ? where username = ?"; jdbcTemplate.update(sql,100,"lucy"); } } -
测试
@Test public void testAccount(){ ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class); AccountService accountService = context.getBean("accountService", AccountService.class); accountService.accountMoney(); }可以看到程序正常执行,lucy少了100,mary多了100

-
问题:上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常,就会发现lucy的钱减少了,mary的钱并没有增加,这在现实生活中是不被接受的。那这个问题怎么解决呢?就需要用到我们今天说的事务
@Service public class AccountService { @Autowired private AccountDao accountDao; //转账 public void accountMoney(){ //lucy少100 accountDao.reduceMoney(); //模拟异常 int i = 10/0; //mary多100 accountDao.addMoney(); } }
4.Spring事务管理
4.1 介绍
-
事务添加到 JavaEE 三层结构里面Service层(业务逻辑层)
-
在Spring进行事务管理操作有两种方式:编程式事务管理和声明式事务管理(使用)
-
声明式事务管理
(1)基于注解方式(使用)
(2)基于xml配置文件方式
-
在Spring进行声明式事务管理,底层使用AOP原理
-
Spring事务管理API。提供一个接口PlatformTransactionManager,代表事务管理器,这个接口针对不同的框架提供不同的实现类。如果我们是使用JDBC数据源访问数据库的,无论是JdbcTemplate还是Mybatis,都可以使用DataSourceTransactionManager进行事务管理
4.2 注解声明式事务管理
-
开启事务@EnableTransactionManagement
-
向Spring容器注入DataSourceTransactionManager
@Configuration @ComponentScan(basePackages = "com.apple") @EnableTransactionManagement //开启事务 public class TxConfig { @Bean("dataSource") public DruidDataSource getDruidDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://localhost:3306/exercise"); dataSource.setUsername("root"); dataSource.setPassword("root"); return dataSource; } @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate; } //创建事务管理器 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){ DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } } -
在service类上面(或者service类里面方法上面)添加事务注解
(1)@Transactional,这个注解添加到类上面,也可以添加到方法上面
(2)如果把这个注解添加到类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加到方法上面,为这个方法添加事务
@Service @Transactional public class AccountService { @Autowired private AccountDao accountDao; //转账 public void accountMoney(){ //lucy少100 accountDao.reduceMoney(); //模拟异常 int i = 10/0; //mary多100 accountDao.addMoney(); } } -
测试结果:账户金额没变,事务生效了

4.3 @Transactional参数配置
-
propagation:事务传播行为
当一个事务方法被另一个事务方法调用的时候,这个事务方法如何进行
传播属性 描述 REQUIRED 使用当前的事务,如果当前没有事务,则自己新建一个事务,子方法必须运行在一个事务,如果当前存在事务,则加入这个事务,成为一个整体。 REQUIRED_NEW 如果当前有事务,则挂起该事物,并且自己创建一个新的事务给自己使用;如果当前没有事务,则跟required一样 SUPPORTS 如果当前有事务,则使用事务;如果当前没有事务,则不使用事务。 -
isolation:事务隔离级别
(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
(2)有三个读问题:脏读、不可重复读、虚(幻)读
- 脏读:一个未提交事务读取到另一个未提交事务的数据
- 不可重复读:一个未提交事务读取到另一提交事务修改数据
- 虚读:一个未提交事务读取到另一提交事务添加数据
(3)解决:通过设置事务隔离性,解决读问题
脏读 不可重复读 幻读 READ UNCOMMITTED(读未提交) 有 有 有 READ COMMITTED(读已提交) 无 有 有 REPEATABLE READ(可重复读) 无 无 有 SERIALIZABLE(串行化) 无 无 无 @Service @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ) public class AccountService{...} -
timeout:超时时间
(1)事务需要在一定时间内进行提交
(2)默认值是-1,设置时间以秒单位进行计算
-
readOnly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readOnly默认值false,表示可以查询,可以添加修改删除操作
(3)设置readOnly值是true,设置成true之后,只能查询
-
rollbackFor:回滚
设置出现哪些异常进行事务回滚
-
noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚
4.4 xml声明式事务管理
在spring配置文件中进行配置,要引入spring-aop和spring-aspects依赖
(1)配置事务管理器
(2)配置通知
(3)配置切入点和切面
<?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: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 http://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:component-scan base-package="com.apple"></context:component-scan>
<!--直接配置连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/exercise"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></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:advice id="txadvice">
<!--配置事务参数-->
<tx:attributes>
<tx:method name="accountMoney" propagation="REQUIRED"/>
<!--以account开头的<tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!--配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.apple.service.AccountService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
</beans>
本文介绍了Spring中使用JdbcTemplate进行事务管理的概念和实践。从事务的四大特性(ACID)出发,详细阐述了Spring的声明式事务管理,包括注解方式和XML配置方式,重点讲解了@Transactional注解的各种参数配置,如propagation、isolation、timeout和readOnly等。通过实例展示了事务在确保数据一致性中的关键作用。

2960

被折叠的 条评论
为什么被折叠?



