事务操作
事务概念
什么是事务?
事务是数据库操作最基本的单元,逻辑上一组操作,要么都成功,如果有一个失败,则都会失败
典型场景:银行转帐
事务特性:ACID
原子性:事务不可分割,都成功或都失败
一致性:操作之前和操作之后他的总量是不变的
隔离性:多事务操作时,他们之间不会产生影响
持久性:事务最终要提交,表中数据真正发生变化
搭建事务操作环境
创建数据库表,添加记录
完成Service和Dao,完成对象创建和注入
service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
在dao创建两个方法,一个加钱,一个扣钱;service创建转账方法
上述代码如果正常执行,是没有问题的,但是如果代码在执行过程中出现异常,则会产生问题。
使用事务进行解决
事务操作过程:
开启事务
进行业务操作
对异常进行捕获
如果没有异常,则提交事务
如果出现异常,则事务回滚
Spring事务管理介绍
事务一般添加到service层(业务逻辑层)
在Spring进行事务管理操作
有两种方式:==编程式事务管理==和==声明式事务管理==
一般使用声明式,编程式太麻烦
声明式事务管理:
注解方式(常用)
基于XML配置文件方式(不常用)
在Spring进行声明式事务管理,==底层使用AOP==
在Spring事务管理API:
提供一个接口:代码事务管理器,这个接口针对不同框架提供不同实现类 PlatformTractionManager
例如针对Jdbc的模版:DataSourceTractionManage
声明式事务管理(注解实现)
在spring配置文件中配置事务管理器
<!-- 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
在spring配置文件,开启事务注解
在spring配置文件引入命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
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" >
开启事务注解
<!-- 开启事务注解 -->
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven>
在service类上面(或service类方法上面加上事务注解)
@Service
@Transactional
public class UserService {
@Autowired
private UserDao userDao;
// 转账方法
public void accountMoney(){
// 扣钱
userDao.reduceMoney();
// 加钱
userDao.addMoney();
}
}
这个注解可以加在类上面也可以加在方法上面
加在类上:这个类里面的所有方法都添加事务
加在方法上:仅仅这个方法添加事务
一般为了方便,加在类上
参数配置
Propagation() :事务的传播行为,一个事务的方法被另外一个事务方法调用时,这个事务方法如何进行
一共有7种行为:REQUIRED REQUIRED_NEW SUPPORTS NOT_SUPPORTED MANDATORY NEVER NESTED
REQUIRED:如果有事务在运行,当前的方法就在这个事务内运行,否则,就启动一个新事物,并在自己的事务内运行
如果单独调用方法1,则会开启事务B;方法2调用时会开启事务A,这时如果调用方法1,则会把方法1加入到事务A中
REQUIRED_NEW:当前的方法必须启动新事物,并且在它自己的事务内运行,如果有事务正在运行,应该将它挂起
方法的执行会启动自己所在的事务,例如方法1一定会启动事务B,方法2一定会启动事务A,会形成一个内外层嵌套关系,内层事务的执行不受外层事务影响,
SUPPORTS:如果有事务在运行,当前的方法就在这个事务内运行,否则它可以不运行在事务中(到时候再理解吧,现在有点抽象)
isolation() :隔离级别
事务有特性成为隔离性,多事务操作之前不会产生影响。不考虑隔离性会产生很多问题。
有三个读问题:脏读、不可重复读、幻读
脏读:一个未提交事务读取到了另一个未提交的事务数据,由于事务可以回滚,如果回滚的话,读的数据就不对了
不可重复读:一个未提交事务读取到另一个提交事务修改的数据,导致每次读到的数据不一样,是一种现象不是问题
幻读:一个未提交事务读取到一个提交事务添加的数据,本来不存在的数据可以被读到
通过设置事务隔离性,解决读问题
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
timeout() :超时时间,事务需要在一定时间内进行提交,不提交就会做事务回滚,默认值为-1,就是没有设置,我们设置时以秒为单位:
readOnly:是否只读,读:查询操作,写:添加修改操作;默认值是false,可以设置只读,不允许写数据库
rollbackFor:回滚,可以设置哪些异常进行事务回滚. rollbackFor = NullPointerException.class
noRollbackFor:不回滚,设置出现哪些异常时不进行回滚
事务操作(XML声明式事务管理)
在spring配置文件中进行配置
配置事务管理器
配置通知(AOP增强的部分就叫通知)
配置切入点和切面
<!-- 0. 提前创建好数据源datasource -->
<!--1. 创建事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 2. 配置通知 -->
<tx:advice id="txadvice">
<!-- 配置事务参数 -->
<tx:attributes>
<!-- 指定哪种规则的方法上面添加事务 -->
<tx:method name="accountMoney" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 3. 配置切入点和切面 -->
<aop:config>
<!-- 配置切入点 -->
<aop:pointcut id="pt" expression="execution(* com.emnets.transaction.service.UserService.*(..))"/>
<!-- 配置切面 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt" />
</aop:config>
事务完全注解方式(声明式事务管理)
创建配置类,使用配置类代替xml配置文件
@Configuration // 配置类
@ComponentScan(basePackages = "com.emnets") //组建扫描
@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?characterEncoding=utf8");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
// 创建Jdbc模版对象
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){ // 注意这里没有创建dataSource对象就之间调用了,和AutoWired原理类似
// 到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;
}
}