spring管理事务
事物的概念
什么是事物,事物是一些sql序列的集合,是多条sql,作为一个整体,
事务指逻辑上的一组操作,这组操作要么全部成功,要么全部失败.
事物有四个特性
原子性,一致性,隔离性,持久性
解释
原子性:意思是不可分割 通俗的说要么全部成功如果有一个失败那全部失败
一致性:操作之前的数据总量变化一致 列 转账: 如果小马转账给小王一千万 如果小马的金额没有变那么小王得到了一千万那
那么就失败不一致 就是操作之前之后数据要一致
隔离性:多个事物操作某一条记录他们之间不会产生影响
持久性:就是提交数据到数据库时就叫持久性
mysql执行事务
beginTransaction 开始事物
insert into student()values....
select * from student where id=1
update school set name=XXX where id=555
endTransaction 事务结束
在程序中事物在哪说明
事物:加在业务类的方法上面(punlic方法上面),表示业务执行时,需要事物的支持
事务管理器
1.不同的数据库访问技术,处理事务是不同的
(1)使用jdbc访问数据库,事务处理
public void updateaccount(){
connection conn =
conn.setAutocommit(false)
stat.insert()
stat.update()
conn.commit()
con.setAutocommit(ture)
}
(2)mybatis执行数据库,处理事务
public void updateAccount(){
SqlSession session=SqlSession.openSession(false);
try{
session.insert(i"insert into student......");
session.update("update school.....")
}catch(Exception e){
session.rollback();
}
}
spring统一管理事务,把不同的数据库访问技术的事务处理统一起来
使用spring的事务管理器,管理不同数据库访问技术的事务处理,开发人员只需要掌握spring的事物处理一个方案,就可以使用不同的数据库访问技术的事务管理。
管理事物面向的时spring,有spring管理事务,做事物提交,事物回滚。
spring事务管理器
spring框架使用事务管理器对象,管理所有的事物
事物管理器接口:PlatformTranscationManager
作用:定义事物的操作方法,主要是commit(),rollback()
事物管理器有很多实现类:一种数据库的访问技术有一个实现类,由具体完成事物的提交,回滚,
意味着:
jdbc或mysql访问数据库有自己的事物管理器实现类,DataSourceTransactionManager,
hibernate框架,他的事务管理器实现类:hibernateTransactionManager,
spring事物工作方式
事物的提交和回滚的时机
什么时候提交事务,什么时候回滚事务?
当你的业务方法正常执行时,没有异常,事物时正常提交的,如果你的事物抛出运行时异常时,事物是回滚的,
异常分类
Error:严重错误,回滚事物,
Exception:异常类,可以出来的异常情况
1.运行时异常,RuntionException和他的子类都是运行时异常,在程序执行过程中,抛出异常,常见的运行时异常;NullpoinerException,NumberFormException,indexBoundException,ArithmeticException,indexOutBoundException,
2.受查异常:编写Java代码的时候,必须处理的异常,例如IO exception,SQLexception,FileNotFoundexception,
即抛出运行时异常,事物回滚,其他情况默认提交,
spring事物使用的是AOP的环绕通知
环绕通知,可以在目标方法的前后都能增强功能,不需要修改代码,
spring给业务在执行时,增加事物的切面功能
@Around("execution( * 所有的业务类中的方法)")
public object mAround(proceedingJoinPoint pjp){
try{
PlatformTransactionManager.beginTransaction(); //使用事物管理器,开启事物
pjp.proceed(); //执行目标方法
PlatformTransactionManager.commit(); //业务方法正常执行
}catcha(exception e){
PlatformTransactionManager.rollback(); //事物回滚
}
}
事物定义接口
事物定义接口TransactionDefinition
TransactionDefinition接口,定义了三类常量,定义了有关事务控制的属性,
事物的属性:1.隔离级别,2.传播行为,3.事物的超时
给业务方法说明事物属性,和ACID不一样
隔离级别
隔离级别:控制事物之间影响的程度,
五个值,只有四个隔离级别
事物的超时
超时时间是以秒为单位,整数值,默认-1,
超时时间:表示一个业务方法最长的执行时间,达到执行时间没有执行完毕,spring事物回滚,
传播行为
传播行为业务在调用时,事物在方法之间的传递和使用,
使用传播行为,标识方法有无事物,
传播行为有七个值
1.REQUIRED:spring默认的传播行为,方法在调用时,存在事物就使用当前的事物,如果没有事物,则新建事物,方法在新事物中执行,
2.SUPORTS:支持,有事物可以正常执行,没有事物也可以正常执行,常用查询操作
3.REQUIRES_NEW:方法需要一个新事物,如果调用方法时,存在一个事物,则原来的事物暂停,直到新事物执行完毕,如果方法调用时,没有事物则新建一个事物,在新事物执行代码,
实例,构建一个简单项目
spring框架使用自己的注解@Transaction控制事物
@Transaction 注解,使用注解的属性控制事物(隔离级别,传播行为,超时)
属性:
1.propagation:事物的传播行为,他使用的Propagation类的枚举值,例如Propagation.REQUIRED
2.isolation:表示隔离级别,使用isolation类的枚举值,表示隔离级别,默认isolation.DEFAULT
3.readOnly:boolean类型的值,表示数据库操作是不是只读的。默认是false,默认不是只读,
4.timeout:事物超时,默认是1,整数值,单位是秒,例如timeout=20
5.rollbackFor:事物回滚的异常类列表,他的值是一个数组,每个值是异常类型的class,
6.rollbackForClassName:表示回滚的异常类列表,他的值是异常名称,是string类型的值,
7.noRollbackFor:不需要回滚的异常列表,是class类型的
8.noRollbackForClassName:不需要回滚的异常类列表,是string类型的值,
注解的使用步骤:
1.在spring的配置文件,声明事物内容
声明事物管理器,说明使用哪个事物管理器对象
声明使用注解管理事务,开启事物注解驱动
2.在类的源代码中加入@Transaction
事物的控制模式,
1.编程式,在代码中编程控制事物,
2.声明事物,不用编码
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: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
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--加载外部文件-->
<context:property-placeholder location="jdbc.properties"/>
<!--声明数据源DateSource-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc.url}"/>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="username" value="${jdbc.user}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--声明sqlessionFactory,在这个类的内部,创建sqlSessionFactory-->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<!-- 指定数据源-->
<property name="dataSource" ref="myDataSource"/>
<!--指定mybatis主配置文件
resource可以直接使用 value属性赋值
-->
<property name="configLocation" value="classpath:mybatis.xml"/>
</bean>
<!--声明扫描器-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定sqlSession-->
<property name="sqlSessionFactoryBeanName" value="factory"/>
<!--指定基本包,dao接口所在的包名-->
<property name="basePackage" value="com.dao"/>
</bean>
<!--声明service-->
<bean id="buyService" class="com.service.impl.BuyGoodsServiceImpl">
<property name="goodsDao" ref="goodsDao"/>
<property name="saleDao" ref="saleDao"/>
</bean>
<!--声明事物的控制-->
<!--声明事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--指定数据源-->
<property name="dataSource" ref="myDataSource"/>
</bean>
<!--开启事物注解驱动:告诉框架使用注解管理事务
transaction-manager:指定事务管理器的id
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
实现类代码
package com.service.impl;
import com.dao.GoodsDao;
import com.dao.SaleDao;
import com.entity.Goods;
import com.entity.Sale;
import com.exception.NotEnougthException;
import com.service.BuyGoodsService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
public class BuyGoodsServiceImpl implements BuyGoodsService {
private SaleDao saleDao;
private GoodsDao goodsDao;
public void setSaleDao(SaleDao saleDao) {
this.saleDao = saleDao;
}
public void setGoodsDao(GoodsDao goodsDao) {
this.goodsDao = goodsDao;
}
/**
* @Transactional 放在public方法的上面,表示方法有事物功能
*
*/
/* 第一种设置方式
@Transactional(propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
timeout = 20,
rollbackFor = {NullPointerException.class,NotEnougthException.class})
*/
/* 第二种设置方式
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,timeout = 20
)
解释 rollbackFor 的使用:
1.框架首先检查方法抛出的异常是不是在rollbackfor数组中,如果在一定回滚,
2.如果方法抛出的异常不在rollbackfor数组中,,框架会继续检查抛出的异常是不是runtimeexception
如果是runtimeException,一定回滚,
例如 抛出sqlexception,IOexception
rollbackfor={sqlexception.class,IOexception.class}
*/
/**
* 第三种方式使用默认方式
* required,发生运行时异常回滚
* */
@Transactional
@Override
public void buy(Integer goodsId, Integer num) {
System.out.println("***buy方法的开始***");
//生成销售记录
Sale sale=new Sale();
sale.setGid(goodsId);
sale.setNum(num);
saleDao.insertSale(sale);
//查询商品
Goods goods=goodsDao.selectById(goodsId);
if (goods==null){
throw new NullPointerException(goodsId+",商品不存在");
}else if (goods.getAmount()<num){
throw new NotEnougthException(goodsId+",库存不足");
}
//更新库存
Goods buygoods=new Goods();
buygoods.setId(goodsId);
buygoods.setAmount(num);
goodsDao.updateGoods(buygoods);
System.out.println("***buy方法的结束***");
}
}
@Transactional 使用的特点
1.spring框架自己提供的事物控制
2.适合中小型项目
3.使用方便,效率高
使用AspectJ框架在spring的配置文件中,声明事物控制
使用aspectJ的AOP,声明事物控制叫做声明事物
使用步骤;
1.在pom.xml加入spring-aspects的依赖
2.在spring的配置文件声明事物的内容
(1)声明事务管理器
(2)声明业务方法需要的事物属性
(3)声明切入点表达式
该内容基本不需要大的改变,使用时注意细节就行
<!--声明事物,不写代码-->
<!--1.声明事物管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource"/>
</bean>
<!--2.声明业务方法的配置属性(隔离级别,传播行为,超时)
id:给业务方法配置事物代码起个名称,唯一值
transaction-manager:事务管理器的id
-->
<tx:advice id="serviceAdvice" transaction-manager="transactionManager">
<!--给具体事务方法增加事物说明
name:业务方法名称,配置name的值,1.业务方法的名称,2.带有部分通配符的方法名称,3.使用*
propagation:指定传播行为的值
isolation 隔离级别
read-only 是否只读,默认false
timeout 超时时间
rollbackfor 指定的回滚的异常类列表,使用的异常全限定名称
-->
<tx:attributes>
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT" read-only="false" timeout="20"/>
<tx:method name="add*" propagation="REQUIRED" isolation="DEFAULT" timeout="20" read-only="false"/>
<!--以上方法以外的都-->
<tx:method name="*" propagation="REQUIRED" timeout="20" read-only="false" isolation="DEFAULT"/>
</tx:attributes>
</tx:advice>
<!--声明切入点表达式,表示哪些类和类中的方法参与事物-->
<aop:config>
<!--声明切入点表达式
expression:切入点表达式,表示哪些类和类中哪些方法要参与事物
id:切入点表达式的名称,唯一值
expression怎么写
-->
<aop:pointcut id="servicepointcut" expression="execution(* *..service..*.*(..))"/>
<!--关联切入点表达式和事物通知-->
<aop:advisor advice-ref="serviceAdvice" pointcut-ref="servicepointcut"/>
</aop:config>
声明式事物优缺点:
1.缺点:难以理解,配置复杂
2.代码和事物的配置是分开的,控制事物源代码不用修改,能快速地了解该项目的事物,适合大型项目