Spring(七)-事务操作
一、事务相关概念
1)事务管理是企业级应用程序开发中必不可少的技术, 用来确保数据的完整性和一致性.
2)事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用
3)事务的四个关键属性(ACID)
- 原子性(atomicity): 事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.
- 一致性(consistency): 一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.
- 隔离性(isolation): 可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.
- 持久性(durability): 一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.
二、Spring事务管理
2.1、Spring事务介绍
1)事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
2)在 Spring 进行事务管理操作有两种方式
- 编程式事务管理
- 声明式事务管理(使用)
3)声明式事务管理
- 基于注解方式(使用)
- 基于 xml 配置文件方式
4)在 Spring 进行声明式事务管理,底层使用 AOP 原理
5)Spring 事务管理 API
提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
2.2、注解声明式事务管理
以转账为例:
1)Dao方法:
UserDao:
package com.dianchou.spring.tx; /** * @author lawrence * @create 2020-07-09 11:11 */ public interface UserDao { //账户转出 public void reduceBalance(); //账户转入 public void addBalance(); }
UserDaoImpl:
package com.dianchou.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; /** * @author lawrence * @create 2020-07-09 11:11 */ @Repository public class UserDaoImpl implements UserDao{ @Autowired private JdbcTemplate jdbcTemplate; @Override public void reduceBalance() { String sql = "update account set balance=balance-? where username = ?"; jdbcTemplate.update(sql,100,"Tom"); } @Override public void addBalance() { String sql = "update account set balance=balance+? where username = ?"; jdbcTemplate.update(sql,100,"Jerry"); } }
2)Service方法
package com.dianchou.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; /** * @author lawrence * @create 2020-07-09 11:10 */ //@Transactional, //(1)这个注解添加到类上面,也可以添加方法上面 //(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务 //(3)如果把这个注解添加方法上面,为这个方法添加事务 @Service @Transactional public class UserService { @Autowired private UserDao userDao; public void accountBalance(){ userDao.reduceBalance(); // int i = 10 / 0; userDao.addBalance(); } }
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:aop="http://www.springframework.org/schema/aop" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--引入外部资源--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--开启组件扫描--> <context:component-scan base-package="com.dianchou.spring.tx"></context:component-scan> <!--配置数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${prop.url}" /> <property name="username" value="${prop.userName}" /> <property name="password" value="${prop.password}" /> <property name="driverClassName" value="${prop.driverClass}" /> </bean> <!--配置JdbcTemplate对象并注入datasource--> <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> </beans>
4)测试
@Test public void testTX(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml"); UserService userService = context.getBean("userService", UserService.class); userService.accountBalance(); }
2.3、事务配置相关参数
在 service 类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数
2.3.1、propagation:事务传播行为
当事务方法被另一个事务方法调用时, 必须指定事务应该如何传播. 例如: 方法可能继续在现有事务中运行, 也可能开启一个新事务, 并在自己的事务中运行.
事务的传播行为可以由传播属性指定. Spring 定义了 7 种类传播行为.
2.3.2、ioslation:事务隔离级别
关于三个读问题:
- 脏读:一个未提交事务读取到另一个未提交事务的数据
- 不可重复读:一个未提交事务读取到另一提交事务修改数据
- 虚读:一个未提交事务读取到另一提交事务添加数据
解决:通过设置事务隔离级别,解决读问题
2.3.3、timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚
(2)默认值是 -1 ,设置时间以秒单位进行计算
2.3.4、readOnly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作
(3)设置 readOnly 值是 true,设置成 true 之后,只能查询
2.3.5、rollbackFor:回滚
(1)设置出现哪些异常进行事务回滚
2.3.6、noRollbackFor:不回滚
(1)设置出现哪些异常不进行事务回滚
2.4、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:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" 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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--引入外部资源--> <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder> <!--开启组件扫描--> <context:component-scan base-package="com.dianchou.spring.tx"></context:component-scan> <!--配置数据库连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="url" value="${prop.url}" /> <property name="username" value="${prop.userName}" /> <property name="password" value="${prop.password}" /> <property name="driverClassName" value="${prop.driverClass}" /> </bean> <!--配置JdbcTemplate对象并注入datasource--> <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="accountBalance" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!--配置切入点和切面--> <aop:config> <!--配置切入点--> <aop:pointcut id="pt" expression="execution(* com.dianchou.spring.tx.UserService.*(..))"/> <!--配置切面--> <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor> </aop:config> <!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven> </beans>
2.5、完全注解声明式事务管理
package com.dianchou.spring.tx; import com.alibaba.druid.pool.DruidDataSource; import org.springframework.beans.factory.annotation.Value; 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; import java.io.IOException; import java.util.Properties; /** * @author lawrence * @create 2020-07-09 16:08 */ @Configuration //配置类 @ComponentScan(basePackages = {"com.dianchou.spring.tx"}) @EnableTransactionManagement public class TxConfig { /** * 创建数据库连接池 */ @Bean public DruidDataSource getDruidDataSource() throws IOException { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName("com.mysql.jdbc.Driver"); dataSource.setUrl("jdbc:mysql://47.110.61.153:3306/mybatis"); dataSource.setUsername("root"); dataSource.setPassword("123456"); return dataSource; } /** * 创建JdbcTemplate 对象 */ @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource) { //到 ioc 容器中根据类型找到 dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(); //注入 dataSource jdbcTemplate.setDataSource(dataSource); return jdbcTemplate; } /** * *创建事务管理器 */ @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(); transactionManager.setDataSource(dataSource); return transactionManager; } }
-------------------------------------------
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!