Spring 学习 (七) 声明式事务管理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_36533951/article/details/79151241

先来回顾一下事务

事务这个概念一开始是在数据库中被提起的
事务的特性:
ACID
原子性:指事务是一个不可分割的工作单位,事务的操作要么都发生,要么都不发生
一致性:指事务前后数据的完整性必须保证一致
隔离性:指多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离
持久性:指一个事务一旦被提交,他对数据库中数据的改变就是永久性的,即时数据库发生故障也不应该对其有任何影响

隔离性的解释有点绕,所以百度下找个容易理解的说法:
比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  即要达到这么一种效果:对于任意两个并发的事务A和B,在事务A看来,B要么在A开始之前就已经结束,要么在A结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

所以说事务的隔离很重要,如果没有隔离,会发生什么呢
脏读:一个事务读取了另一个事务改写但是还没有提交的数据,如果这些数据被回滚,则读到的数据是无效的
不可重复读:在同一个事务中,多次读取同一个数据但是返回的结果有所不同
幻读:一个事务读取了几行记录后,另一个事务插入了一些记录,幻读由此产生。然后再次查询,第一个事务就会发现有些原来没有的记录
幻读和不可重复读区别:
不可重复读:例如在读取数据时,别的事务对其所读数据提交的修改,所以在此读取发现不一样
幻读:例如读取时,别的事务又提交了新增数据

如何解决这些问题(隔离机制)
READ_UNCOMMITED(读未提交) 允许读取还未提交的改变了的数据。 会导致:脏读,幻读,不可重复读
READ_COMMITTED:(读已提交) 允许在并发事务已经提交后读取。可防止脏读,但幻读,不可重复读仍可发生
REPEATABLE_READ:(可重复读) 对相同字段多次读取是一样的,除非数据被事务本身改变,可防止脏,不可重复读,但幻读认可发生
SERIALIZABLE:(串行化) 完全服从acid隔离级别,确保不发生 脏 幻 不可重复读,但是效率是最慢的

复习了事务后,开始Spring 事务的学习

先了解Spring为什么要有事务?
因为在不同平台,操作事务的代码各不相同.spring提供了一个接口PlaformTransactionManager

Spring事务操作对象
PlaformTransactionManager声明了事务中有哪些操作,并且针对不同平台给出不同的实现类
例:DataSourceTransactionManager HibernateTransactionManager
注意:在Spring中使用事务管理,最为核心的对象是TransactionManager对象

Spring管理事务的属性

事务隔离级别:
是否只读
事务传播行为(决定业务方法相互调用,事务该如何处理)
PROPAGION_XXX :事务的传播行为
* 保证同一个事务中
PROPAGATION_REQUIRED 支持当前事务,如果不存在 就新建一个(默认)
PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务
PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常
* 保证没有在同一个事务中
PROPAGATION_REQUIRES_NEW 如果有事务存在,挂起当前事务,创建一个新的事务
PROPAGATION_NOT_SUPPORTED 以非事务方式运行,如果有事务存在,挂起当前事务
PROPAGATION_NEVER 以非事务方式运行,如果有事务存在,抛出异厂
PROPAGATION_NESTED 如果当前事务存在,则嵌套事务执行
这里写图片描述

下面放一个案例(后面加事务要用):
模拟转账程序

dao接口

public interface AccountDao {

   //加钱
   void addMoney ( Integer id , Double money ) ;
   //减钱
   void decreaseMoney ( Integer id , Double money ) ;

}

dao实现

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao

{
   //加钱
   public void addMoney(Integer id, Double money)

   {
      String sql = "update MoneyDemo set money = money + ? where id = ?" ;

      super.getJdbcTemplate().update( sql , money , id ) ;
   }

   //减钱
   public void decreaseMoney(Integer id, Double money)

   {
      String sql = "update MoneyDemo set money = money - ? where id = ?" ;

      super.getJdbcTemplate().update( sql , money , id ) ;
   }

}

service接口

public interface AccountService {

   //转账
   void transfer ( Integer from , Integer to , Double money ) ;

}

service接口实现

public class AccountServiceImpl implements AccountService {

   private AccountDao accountDao ;

   public AccountDao getAccountDao() {
      return accountDao;
   }
   public void setAccountDao(AccountDao accountDao) {
      this.accountDao = accountDao;
   }

   public void transfer(Integer from, Integer to, Double money) {

      //减钱
      accountDao.decreaseMoney(from, money);

       //int i = 1 / 0 ;
      //加钱
      accountDao.addMoney(to, money);

   }

}

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: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">

  <context:component-scan base-package="cn.itcast"></context:component-scan>

   <!-- 指定Spring读取DB.properties配置 -->
   <context:property-placeholder location="classpath:DB.properties"/>

   <!-- 将数据库连接池交由Spring管理 -->
   <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
      <property name="jdbcUrl" value="${jdbcUrl}"></property>
      <property name="driverClass" value="${driverClass}"></property>
      <property name="user" value="${user}"></property>
      <property name="password" value="${password}"></property>
   </bean>

   <!-- dao -->
   <bean name="accountDao" class="cn.itcast.dao.AccountDaoImpl">
      <property name="dataSource" ref="dataSource"></property>
   </bean>

   <bean name="accountService" class="cn.itcast.service.AccountServiceImpl">
      <property name="accountDao" ref="accountDao"></property>
   </bean>


</beans>

什么叫声明式事务管理?
Spring提供了对事务的管理, 这个就叫声明式事务管理。
Spring声明式事务管理,核心实现就是基于Aop。

Spring 管理事务的方式(3种):

编码式
xml配置(aop)
注解(aop)

注意Spring管理事务是用aop来实现的 所以当你的类实现接口的话,接收时也要是用接口类型

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: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">

  <context:component-scan base-package="cn.itcast"></context:component-scan>
 <context:component-scan base-package="com.study.spring"></context:component-scan>
   <!-- 指定Spring读取DB.properties配置 -->
   <context:property-placeholder location="classpath:DB.properties"/>


   <!-- 将数据库连接池交由Spring管理 -->
   <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
      <property name="jdbcUrl" value="${jdbcUrl}"></property>
      <property name="driverClass" value="${driverClass}"></property>
      <property name="user" value="${user}"></property>
      <property name="password" value="${password}"></property>
   </bean>

   <!-- dao -->
   <bean name="accountDao" class="cn.itcast.dao.AccountDaoImpl">
      <property name="dataSource" ref="dataSource"></property>
   </bean>

   <bean name="accountService" class="cn.itcast.service.AccountServiceImpl">
      <property name="accountDao" ref="accountDao"></property>
   </bean>

   <!-- 事务核心管理器,封装了所有事务操作,依赖于连接池  -->
   <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!-- 配置事务通知 -->
   <tx:advice transaction-manager="txManager" id="tx">
      <tx:attributes>
   <!--  配置针对特定方法应用特定事务属性(*是通配符)   isolation 隔离级别   propagation 传播行为 -->
         <tx:method name="transfer*" isolation="DEFAULT" propagation="REQUIRED" />
         <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" />
         <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" />
      </tx:attributes>
   </tx:advice>

   <!--
   将通知织入 -->
   <aop:config>
   <!--  使用切入点表达式来定位切入点 -->
      <aop:pointcut expression="execution(* cn.itcast.service.*ServiceImpl.*(..))" id="pt"/>
      <!-- 配置切面 ( 切面是由 通知+切入点 构成的 ) -->
      <aop:advisor advice-ref="tx" pointcut-ref="pt"/>
   </aop:config>


</beans>

测试案例

@RunWith(SpringJUnit4ClassRunner.class)//使用帮我们自动创建容器,就不用自己手动创建Spring容器
@ContextConfiguration("classpath:bean.xml")//指定配置文件路径
public class MoneyDemo {

    @Resource(name="accountService")
    private AccountService asi ;

    @Test
    public void fun1 ()
    {
        asi.transfer(1, 2, (double) 200);
    }

}

注解方式

<?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: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">

  <context:component-scan base-package="cn.itcast"></context:component-scan>
 <context:component-scan base-package="com.study.spring"></context:component-scan>
   <!-- 指定Spring读取DB.properties配置 -->
   <context:property-placeholder location="classpath:DB.properties"/>


   <!-- 事务核心管理器,封装了所有事务操作,依赖于连接池  -->
   <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

   <!-- 将数据库连接池交由Spring管理 -->
   <bean name="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" >
      <property name="jdbcUrl" value="${jdbcUrl}"></property>
      <property name="driverClass" value="${driverClass}"></property>
      <property name="user" value="${user}"></property>
      <property name="password" value="${password}"></property>
   </bean>

   <!-- dao -->
   <bean name="accountDao" class="cn.itcast.dao.AccountDaoImpl">
      <property name="dataSource" ref="dataSource"></property>
   </bean>

   <bean name="accountService" class="cn.itcast.service.AccountServiceImpl">
      <property name="accountDao" ref="accountDao"></property>
   </bean>

      <!-- 注解方式实现事务: 指定注解方式实现事务 -->
    <tx:annotation-driven transaction-manager="txManager"/>


</beans>

service
注解定义到方法上: 当前方法应用spring的声明式事务
定义到类上: 当前类的所有的方法都应用Spring声明式事务管理;
定义到父类上: 当执行父类的方法时候应用事务。

   @Transactional
   public void transfer(Integer from, Integer to, Double money) {

      //减钱
      accountDao.decreaseMoney(from, money);

//    int i  =  1 / 0 ;

      //加钱
      accountDao.addMoney(to, money);

   }

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_36533951/article/details/79151241