一、事务
1、什么是事务?
- 一组sql语句的集合,集合中有多条SQL,要么都不执行,要么都执行,这些SQL语句的执行是一致的,作为一个整体执行
2、在什么时候想到使用事务
- 当操作涉及到多个表或者多条增删改SQL语句时,需要保证这些SQL语句要么都成功,要么都失败,这时就需要使用事务【经典转账案例】
在java代码中写程序,控制事务,此时事务应该放在哪里呢?
- 之前是在SQL语句上写事务,使用spring框架时,事务应该放在Service类的业务方法上,因为业务方法会调用多个dao方法,执行多个SQL语句。
3、通常使用JDBC访问数据库,还是mybatis访问数据库怎么处理事务?
- jdbc访问数据库,处理事务 : 开始事务 ——->SQL——->提交/回滚 conn.commit/conn.rollback;
- Mybatis SqlSession.commit() SqlSession.rollback()
- Hibernat Session.commit() Session.rollback()
4、问题中的事务的处理方式有什么不足
- 不同的数据库访问技术,处理事务的对象,方法不同
- 需要了解不同的数据库访问技使用事务的原理
- 找我多种数据库的处理逻辑。什么时候提交/回滚
- 总结:就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法
5、怎么解决不足
spring提供一种处理事务的统一模型,能使用统一步骤,方式完成对多种不同数据库访问技术的事务处理二、spring处理事务
1、处理事务,需要怎么做,做什么
① spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring即可
② spring内部提交和回滚事务,使用的是事务管理器对象,代理你完成commit/rollback
- 一个接口和他的众多实现类,接口:PlatformTransactionManager 事务管理器接口
- 实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了
- 需要告诉spring 你用的是那种访问数据库的访问技术。
声明数据库访问技术对于的事务管理,在spring的配置文件中使用<bean id="xxx" class="… DateSourceTransactionManager">
③ 你的业务方法需要什么样的事务,说明需要事务的类型
说明方法需要的事务:
- 事务的隔离级别
- 这些常量都是ISOLATION_开头 如:ISOLATION_XXX
- 事务的隔离级别有4个值
DEFAULT
: 采用数据库默认的隔离级别- mysql是
REPEATABLE_READ
可重复读 - Oracle是
READ_COMMITED
读已提交
- mysql是
- 事务的超时时间 【一般不设置】
- 表示一个方法最长的执行时间,如果方法执行时超过了这个时间事务就回滚了
- 单位是【秒】 整数值, 默认是 -1
- 事务的传播行为(主要是添加传播行为)
- 控制业务方法是不是有事务的,是什么样的事务的。
- 传播行为有7个,表示业务方法在调用时,事务在方法之间是如何使用的
事务传播行为常量都是以PROPAGATION_
开头,形如 PROAPAGATION_XXX
PROPAGATION_REQUIRED | 支持当前事务,如果当前没有事务,则新建一个事务执行 一定会有事务 【Spring默认值】 |
---|---|
PROPAGATION_SUPPORTS | 支持当前事务,如果没有当前事务,则以非事务的方式执行 有没有事务都可以【查询】 |
PROPAGATION_MANDATORY | 支持当前事务,如果当前没有事务,则抛出异常 |
PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前已经有事务了,则将当前事务挂起 方法在执行时,总是新建一个新的事务,如果有别事务的就挂起来 |
PROPAGATION_NOT_SUPPORTED | 不支持当前事务,而且总是以非事务方式执行 |
PROPAGATION_NEVER | 不支持当前事务,如果存在事务,则抛出异常 |
PROPAGATION_NESTED | 如果当前事务存在,则在嵌套事务中执行,否则行为类似于PROPAGATION_REQUIRED。 EJB中没有类似的功能。 |
④ spring提交事务,回滚事务的时机
- 当你的业务方法执行成功,没有异常抛出,当方法执行完毕 spring在方法执行后自动提交事务
commit()
- 当你的业务方法抛出运行时异常或Error,spring执行回滚,自动调用事务管理器的
rollback()
方法
运行时异常的定义是 RunTimeException
和他的子类都是运行时异常
- 当你的业务方法抛出 受检异常 提交事务
2、总结:
- 管理事务的是 事务管理和他的实现类
- spring的事务 是一个统一模型
- 指定要使用的事务管理器实现类
<bean>
- 指定哪些类,哪些方法需要加入事务的功能
- 执行方法需要的隔离级别,传播行为,超时
- 指定要使用的事务管理器实现类
- 以上大部分代码都是固定好的 模板 只需要修改一部分即可 项目中类的信息,方法名称,传播行为
三、注解方式实现事务
1、实例代码实现
- 创建maven项目
- 创建实体类
Sale实体类 Goods实体类
- 创建dao接口和Mapper文件
SaleDao
接口 【添加购买数据 insert】GoodsDao接口【减少商品数量 update 】SaleMappe.xml GoodsMapper.xml
- 创建mybatis主配置文件
- 创建Service接口和实现类
属性是SaleDao和 GoodsDao
- 创建spring的配置文件: 声明myabtis的对象交给spring创建 【和集成MyBatis一致】
- 数据源
DateSource
SqlSessionFactory
Dao
对象- 声明自定义的
service
- 数据源
-
2、Spring事务实现
Spring框架中的提供的事务处理方案
适合中小项目使用的,注解方案
propagation
:表示事务的传播行为 默认值为 Propagation.REQUIREDisolation
:用于设置事务的隔离级别,默认为 Isolation.DEFAULTreadOnly
:用于设置该方法对于数据库是否是自读的,属性为boolean 默认false 【都是查询时可设置为真,提高对数据库的效率,不用上锁…】timeout
:超时时间 【不用管】rollbackFor
: 抛出什么异常 就执行回滚,里面是一个数组,放异常的字节码
rollbackFor = {NullPointerException.class,BuyGoodsException.class}
noRollbackFor
:抛出执行的异常不回滚
2.2、使用@Transactional
注解的步骤
1、需要声明事务管理器对象
<!--声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--【配置数据源】需要告诉spring这是连接的哪个数据库,采用对应的数据库事务-->
<property name="dataSource" ref="dataSource"/>
</bean>
2、开启事务注解驱动
【告诉spring框架,我要使用注解的方式去管理事务】
spring自动使用AOP方式 创建@Transactional所在的类代理对象,自动给方法加入事务功能
spring给业务方法加入事务的原理
- 在你的业务方法开启之前 开始事务,在业务方法结束时 提交/回滚事务
使用的是AOP的环绕通知
@Around
<!--开始事务注解驱动,告诉sprng使用注解管理事务 创建代理对象
transaction-manager:事务管理器对象的id
-->
<tx:annotation-driven transaction-manager="transactionManager"/>
3、在方法的上面添加注解
@Transactional
//一般情况直接加注解就行,不需要设置别的
- 【使用默认值,默认的传播行为,默认的隔离级别,默认的….】
四、AspectJ方式解决事务
- 适合大型项目解决事务的方式,有很多的类,很多的方法,需要大量的配置事务。
- 这样就需要AspectJ框架功能,在spring配置文件中声明类,方法需要的事务。
- 这种方式是业务代码和事务配置完全分离
1、代码实现
实现步骤:都是在xml配置文件中实现1. 使用AspectJ框架,需要加入AspectJ依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2. 声明事务管理器对象
<!--声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--【配置数据源】需要告诉spring这是连接的哪个数据库,采用对应的数据库事务-->
<property name="dataSource" ref="dataSource"/>
</bean>
3. 声明方法需要的事务的类型
(配置方法的事务属性【隔离级别,传播行为,超时时间】)
<!--声明业务方法他的事务属性【隔离级别,传播行为,超时时间】-->
<!--id:自定义名称,起个名-->
<!--transaction-manager:事务管理器对象的id-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--配置事务的属性-->
<tx:attributes>
<!--method表示指定哪个方法配置事务【可以出现多次】根据后面的值进行配置-->
<!--name配置有两种方式:1.完整的方法名,不带包名2.方法名可以使用通配符的方式*表示任意字符-->
<!--propagation:传播方式
isolation:隔离级别
rollback-for:执行的异常类型,全限定类名,发生异常一定回滚,多个异常逗号隔开
-->
<tx:method name="buyGoods" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
<tx:method name=""/>
<!--。。。。-->
</tx:attributes>
</tx:advice>
4. 配置AOP:指定哪些类要创建代理
<!--配置AOP-->
<aop:config>
<!--配置切入点表达式,指定哪些包中的类,要使用事务-->
<!--id是切入点表达式的名称
expression:切入点表达式,指定那些类要使用事务,aspectJ会创建代理对象
execution(**..service..*.*(..)):所有的service包和所有的service包中类类中的方法全部创建代理对象
-->
<aop:pointcutid="servicePt"expression="execution(**..service..*.*(..))"/>
<!--配置增强器,关联advice和pointcutid对id
advice-ref:通知,就是上面的tx:advice的id
pointcut-ref:切入点表达式的id
也就是说切入点表达式里面的方法和上面tx:advice的方法对应起来的话就是创建代理对象增加事务
-->
<aop:advisoradvice-ref="myAdvice"pointcut-ref="servicePt"/>
</aop:config>
- 在开发中,一般有上千个类需要进行事务的管理,而一个一个的写太麻烦,所以就要用到通配符的方式
- 所以在开发中,如果有很多需要事务的话,名字尽量找到相同点 例如 add remove