Spring5_事务学习
1.事务概念
1.1什么是事务
事务是数据库操作的基本单元,逻辑上一组操作,一组操作为一个整体,要么都成功,如果有其中一个操作失败,则所有操作都失败
2.2事务的四个特征 (ACID)
- 原子性:事务要么都成功,要么都失败。
- 一致性:保证事务只能把数据库从一个有效(正确)的状态“转移”到另一个有效(正确)的状态。
- 隔离性:多事务操作时,不会相互影响
- 持久性:在事务提交之后,表中数据真正发生变化
2.事务操作(搭建事务操作环境-idea)
以银行转账为例
张三转账100元给李四
张三少100元,李四多100元
2.1 创建数据库,添加记录
-- 创建一个表t_account
CREATE TABLE t_account(
id INT PRIMARY KEY,
username VARCHAR(20),
money INT
);
-- 向表中添加数据
INSERT INTO t_account VALUES
(1,'张三',1000),
(2,'李四',1000);
2.2配置bean.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"
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">
<!--组件扫描-->
<context:component-scan base-package="com.ccby.spring5"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql:///user_db"></property>
<property name="username" value="root"></property>
<property name="password" value="123456abcd"></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>
</beans>
2.3创建service ,搭建dao,完成对象创建和注入
@Service
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
}
@Repository
public class UserDaoImpl implements UserDao {
//注入JdbcTemplate
private JdbcTemplate jdbcTemplate;
}
2.4在dao创建:转入和转出的方法;在service创建转账的方法
@Repository
public class UserDaoImpl implements UserDao {
//注入JdbcTemplate
private JdbcTemplate jdbcTemplate;
//张三转账100给李四
/**
* 转入
*/
@Override
public void addMoney() {
String sql = "update t_account set money = money-? where username = ?";
jdbcTemplate.update(sql, 100, "张三");
}
/**
* 转出
*/
@Override
public void reduceMoney() {
String sql = "update t_account set money = money+? where username = ?";
jdbcTemplate.update(sql, 100, "李四");
}
}
//转账
public void accountMoney() {
userDao.reduceMoney();
userDao.addMoney();
}
3.Spring 事务管理介绍
-
事务添加到JavaEE三层架构里面Service层(业务逻辑层)
-
在Spring进行事务管理操作(两种方式)
(1) 编程式业务管理
(2) 声明式业务管理 (使用) -
声明式业务管理
(1)基于注解方式 (使用)
(2)基于xml配置文件方式 -
在Spring进行声明式业务管理,底层使用Aop原理
-
Spring事务管理API
提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类 (PlatformTransactionManager)
4.注解声明式事务管理
4.1 在spring配置文件中配置事务管理器
<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
4.2 在spring配置文件中开启事务注解
-
在spring配置文件中引入名称空间tx
<?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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
-
开启事务注解
<!--开启事务注解--> <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
-
在service类上面添加事务注解
- @Transactional,这个注解添加到类上面,也可以添加到方法上面
- 如果把这个注解添加到类上面,这个类里面所有的方法都添加事务
- 如果把这个注解添加到方法上面,为这个方法添加事务
@Service
@Transactional
public class UserService {
//注入dao
@Autowired
private UserDao userDao;
//转账
public void accountMoney() {
userDao.reduceMoney();
//模拟异常
int i = 10/0;
userDao.addMoney();
}
}
@Test
public void test1() {
ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}
事务成功回滚
5. 声明式事务管理参数配置
在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数。
-
propagation:事务传播行为
-
多事务方法直接调用。(事务方法:对数据库表数据进行变化操作)
-
Spring定义了7种类传播行为
-
传播属性 描述 举例 REQUUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新事务,并在自己的事务内运行 如果说,当前方法(方法1)本身有事务,调用一个没有事务的方法(方法2),则方法2使用方法1中的事务;如果方法1本身没有事务,调用方法2之后,创建新事务 REQUIRED_NEW 当前的方法必须启动新事务,并在它自己的事务内运行,如果有事务正在运行,应该将它挂起 使用方法1调用方法2,无论方法1是否有事务,都创建新的事务 SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务内 NOT_SUPPORTS 当前的方法不应该运行在事务中,如果有事务,将它挂起 MANDATORY 当前的方法必须运行在事务内,如果没有正在运行的的事务,就抛出异常 NEVER 当前的方法不应该运行在事务中,如果有运行的事务,就抛出异常 NESTED 如果有事务在运行,当前的方法就应该在这个事物的嵌套事务内运行,否则就启动一个新事务,并在它自己的事务内运行
-
-
isolation:事务隔离级别
-
事务有特性称为隔离性,多事务操作之间不会产生运行,不考虑隔离性会产生:脏读、不可重复读、幻读
- 脏读:一个未提交事务读取到了另外一个未提交事务的值 (事务1读取到事务2改变的数据,此时事务2回滚,但是事务1中的数据还为事务2改变的数据)
- 不可重复读:同一次事务前后查询到的数据不一致
- 幻读:一个未提交事务读取到另一提交事务添加(删除)数据(一次事务中前后数据量发生变化,用户产生不可预料的问题)
-
提供设置事务隔离级别,解决读问题
脏读 不可重复读 幻读 READ UNCOMMITTED (只读未提交) 有 有 有 READ COMMITTED (读以提交) 无 有 有 REPEATABLE READ (可重复读) 无 无 有 SERIALIZABLE (串行化) 无 无 无
-
-
timeout:超时时间
- 事务需要在一定时间内进行提交,如果不提交进行回滚,设置时间以秒为单位
-
readOnly:是否只读
- 默认值为false,若设置成true后,只可以查询
-
rollbackFor:回滚
- 可设置出现哪些异常进行回滚
-
noRollbackFor:不回滚
- 可设置出现哪些异常不进行回滚
6.XML声明式事务管理
6.1在spring配置文件中进行配置
- 配置事务管理器
- 配置通知
- 配置切入点和切面
<?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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--组件扫描-->
<context:component-scan base-package="com.ccby.spring5"></context:component-scan>
<!--数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
<property name="url" value="jdbc:mysql:///user_db"></property>
<property name="username" value="root"></property>
<property name="password" value="123456abcd"></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>
<!--1.创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--2.配置通知-->
<tx:advice id="tx_advice">
<!--配置事务参数-->
<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.atguigu.spring5.service.UserService.*(..))"/>
<!--配置切面-->
<aop:advisor advice-ref="tx_advice" pointcut-ref="pt"></aop:advisor>
</aop:config>
</beans>
7. 完全注解声明式管理事务
7.1创建配置类,使用配置类代替xml配置文件
package com.ccby.spring5.config;
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(basePackages = "com.ccby.spring5") //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
//创建数据库连接池
@Bean
public DruidDataSource getDruidDataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///user_db");
dataSource.setUsername("root");
dataSource.setPassword("123456abcd");
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;
}
}
//测试
@Test
public void test2() {
ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
UserService userService = context.getBean("userService", UserService.class);
userService.accountMoney();
}