一、事务

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、问题中的事务的处理方式有什么不足

  1. 不同的数据库访问技术,处理事务的对象,方法不同
  2. 需要了解不同的数据库访问技使用事务的原理
  3. 找我多种数据库的处理逻辑。什么时候提交/回滚
  4. 总结:就是多种数据库的访问技术,有不同的事务处理的机制,对象,方法

    5、怎么解决不足

    spring提供一种处理事务的统一模型,能使用统一步骤,方式完成对多种不同数据库访问技术的事务处理

    二、spring处理事务

    1、处理事务,需要怎么做,做什么

    ① spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring即可


    ② spring内部提交和回滚事务,使用的是事务管理器对象,代理你完成commit/rollback

  • 一个接口和他的众多实现类,接口:PlatformTransactionManager 事务管理器接口
  • 实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了
  • 需要告诉spring 你用的是那种访问数据库的访问技术。

声明数据库访问技术对于的事务管理,在spring的配置文件中使用声明就可以了
<bean id="xxx" class="… DateSourceTransactionManager">

③ 你的业务方法需要什么样的事务,说明需要事务的类型

说明方法需要的事务:

  1. 事务的隔离级别
    • 这些常量都是ISOLATION_开头 如:ISOLATION_XXX
    • 事务的隔离级别有4个值
    • DEFAULT: 采用数据库默认的隔离级别
      • mysql是 REPEATABLE_READ 可重复读
      • Oracle是READ_COMMITED 读已提交
  2. 事务的超时时间 【一般不设置】
    • 表示一个方法最长的执行时间,如果方法执行时超过了这个时间事务就回滚了
    • 单位是【秒】 整数值, 默认是 -1
  3. 事务的传播行为(主要是添加传播行为)
    • 控制业务方法是不是有事务的,是什么样的事务的。
    • 传播行为有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 和他的子类都是运行时异常

  • 当你的业务方法抛出 受检异常 提交事务

在你写代码时 必须处理的异常。 例如IOException

2、总结:

  1. 管理事务的是 事务管理和他的实现类
  2. spring的事务 是一个统一模型
    • 指定要使用的事务管理器实现类 <bean>
    • 指定哪些类,哪些方法需要加入事务的功能
    • 执行方法需要的隔离级别,传播行为,超时
  3. 以上大部分代码都是固定好的 模板 只需要修改一部分即可 项目中类的信息,方法名称,传播行为

三、注解方式实现事务

1、实例代码实现

  1. 创建maven项目
  2. 创建实体类

Sale实体类 Goods实体类

  1. 创建dao接口和Mapper文件
    • SaleDao接口 【添加购买数据 insert】GoodsDao接口【减少商品数量 update 】
    • SaleMappe.xml GoodsMapper.xml
  2. 创建mybatis主配置文件
  3. 创建Service接口和实现类

属性是SaleDao和 GoodsDao

  1. 创建spring的配置文件: 声明myabtis的对象交给spring创建 【和集成MyBatis一致】
    1. 数据源DateSource
    2. SqlSessionFactory
    3. Dao对象
    4. 声明自定义的service
  2. 创建测试类,获取Service对象,给dao对象注入

    2、Spring事务实现

    Spring框架中的提供的事务处理方案

  3. 适合中小项目使用的,注解方案

    • spring框架自动使用AOP实现给业务方法增加事务的功能 使用 **@Transactional**注解
    • @Transactional是spring框架自己的注解,放在public方法的上面,表示当前方法具有事务
    • 可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息

      2.1、@Transactional注解属性:

  • propagation:表示事务的传播行为 默认值为 Propagation.REQUIRED
  • isolation:用于设置事务的隔离级别,默认为 Isolation.DEFAULT
  • readOnly:用于设置该方法对于数据库是否是自读的,属性为boolean 默认false 【都是查询时可设置为真,提高对数据库的效率,不用上锁…】
  • timeout:超时时间 【不用管】
  • rollbackFor: 抛出什么异常 就执行回滚,里面是一个数组,放异常的字节码

rollbackFor = {NullPointerException.class,BuyGoodsException.class}

  • noRollbackFor:抛出执行的异常不回滚

2.2、使用@Transactional注解的步骤

1、需要声明事务管理器对象

  1. <!--声明事务管理器对象-->
  2. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  3. <!--【配置数据源】需要告诉spring这是连接的哪个数据库,采用对应的数据库事务-->
  4. <property name="dataSource" ref="dataSource"/>
  5. </bean>

未命名图片.png

2、开启事务注解驱动

【告诉spring框架,我要使用注解的方式去管理事务】

spring自动使用AOP方式 创建@Transactional所在的类代理对象,自动给方法加入事务功能
spring给业务方法加入事务的原理

  • 在你的业务方法开启之前 开始事务,在业务方法结束时 提交/回滚事务
  • 使用的是AOP的环绕通知@Around

    1. <!--开始事务注解驱动,告诉sprng使用注解管理事务 创建代理对象
    2. transaction-manager:事务管理器对象的id
    3. -->
    4. <tx:annotation-driven transaction-manager="transactionManager"/>

    未命名图片.png

    3、在方法的上面添加注解 @Transactional

  • //一般情况直接加注解就行,不需要设置别的

  • 【使用默认值,默认的传播行为,默认的隔离级别,默认的….】

未命名图片.png

四、AspectJ方式解决事务

  • 适合大型项目解决事务的方式,有很多的类,很多的方法,需要大量的配置事务。
  • 这样就需要AspectJ框架功能,在spring配置文件中声明类,方法需要的事务。
  • 这种方式是业务代码和事务配置完全分离

    1、代码实现

    实现步骤:都是在xml配置文件中实现

    1. 使用AspectJ框架,需要加入AspectJ依赖

    1. <dependency>
    2. <groupId>org.springframework</groupId>
    3. <artifactId>spring-aspects</artifactId>
    4. <version>5.2.5.RELEASE</version>
    5. </dependency>

    2. 声明事务管理器对象

    1. <!--声明事务管理器对象-->
    2. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    3. <!--【配置数据源】需要告诉spring这是连接的哪个数据库,采用对应的数据库事务-->
    4. <property name="dataSource" ref="dataSource"/>
    5. </bean>

    3. 声明方法需要的事务的类型

    (配置方法的事务属性【隔离级别,传播行为,超时时间】)

  1. <!--声明业务方法他的事务属性【隔离级别,传播行为,超时时间】-->
  2. <!--id:自定义名称,起个名-->
  3. <!--transaction-manager:事务管理器对象的id-->
  4. <tx:advice id="myAdvice" transaction-manager="transactionManager">
  5. <!--配置事务的属性-->
  6. <tx:attributes>
  7. <!--method表示指定哪个方法配置事务【可以出现多次】根据后面的值进行配置-->
  8. <!--name配置有两种方式:1.完整的方法名,不带包名2.方法名可以使用通配符的方式*表示任意字符-->
  9. <!--propagation:传播方式
  10. isolation:隔离级别
  11. rollback-for:执行的异常类型,全限定类名,发生异常一定回滚,多个异常逗号隔开
  12. -->
  13. <tx:method name="buyGoods" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
  14. <tx:method name=""/>
  15. <!--。。。。-->
  16. </tx:attributes>
  17. </tx:advice>

未命名图片.png

4. 配置AOP:指定哪些类要创建代理

  1. <!--配置AOP-->
  2. <aop:config>
  3. <!--配置切入点表达式,指定哪些包中的类,要使用事务-->
  4. <!--id是切入点表达式的名称
  5. expression:切入点表达式,指定那些类要使用事务,aspectJ会创建代理对象
  6. execution(**..service..*.*(..)):所有的service包和所有的service包中类类中的方法全部创建代理对象
  7. -->
  8. <aop:pointcutid="servicePt"expression="execution(**..service..*.*(..))"/>
  9. <!--配置增强器,关联advice和pointcutid对id
  10. advice-ref:通知,就是上面的tx:advice的id
  11. pointcut-ref:切入点表达式的id
  12. 也就是说切入点表达式里面的方法和上面tx:advice的方法对应起来的话就是创建代理对象增加事务
  13. -->
  14. <aop:advisoradvice-ref="myAdvice"pointcut-ref="servicePt"/>
  15. </aop:config>

未命名图片.png

  • 在开发中,一般有上千个类需要进行事务的管理,而一个一个的写太麻烦,所以就要用到通配符的方式
  • 所以在开发中,如果有很多需要事务的话,名字尽量找到相同点 例如 add remove