美文网首页
Spring事务的管理

Spring事务的管理

作者: CoderHong | 来源:发表于2017-12-03 09:29 被阅读16次

事务的概念

什么是事务
事务逻辑上的一组操作。组成这组操作的各个逻辑单元,要么一起成功,要么一起失败。

事务特性

原子性:强调事务的不可分割
一致性:事务的执行前后数据完整性保持一致。
隔离性:一个事务执行过程中,不应该受到其它事务的干扰
持久性:事务一旦执行,数据就持久到数据库

如果不考虑隔离性引发的安全性问题

脏读:一个事务读到了另一个事物未提交的数据
不可重复读:一个事务读到了另一个事务提交的update的数据导致多次查询结果不一致。
虚度:一个事务读到了另个一个事务已经提交的insert的数据导致多次查询结果不一致。

平台事务管理器

JdbcDaoSupport学习

如果想用Spring来管理事务需要用到Spring相关的类和API

PlatformTransactionManager接口

image.png

如果用Spring管理事物 第一步就得配置 PlatformTransactionManager这个接口。平台事务管理器(真正的管理事务类)。该类有具体的实现类,根据不同的框架,需要选择不同的实现类

TransactionDefinition
事物的定义信息接口(事物的隔离级别、传播行为,超时,只读)

  • 事务的隔离级别,一般使用默认的
事务隔离级别常量
static  int ISOLATION_DEFAULT - 采用数据库的默认隔离级别
  • 事务的传播行为常量
PROPAGATION_REQUIRED -- A中有事务,使用A中的事物,如果没有,B就会开启一个新的事物,将A包含进来。(保证AB在同一个事物) 默认值。

事务管理的使用

提示:这里使用Spring的JDBC模板方式操作数据库。连接池使用的c3p0。

  • 第一步 导入Jar包
image.png
  • 第二步 搭建项目结构


    image.png
  • 配置JDBC的模板 跟连接池

<!-- c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_day03?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
<!-- 配置JDBC模板类 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        引用属性
        <property name="dataSource" ref="dataSource"></property>
</bean>

  • 配置业务层和持久层
<!-- 配置业务层和持久层 -->
    <bean id="accountService" class="com.coderhong.demo1.AccountServiceIml">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    
    <bean id="accountDao" class="com.coderhong.demo1.AccountDaoImpl">
        <property name="jdbcTemplate" ref="org.springframework.jdbc.core.JdbcTemplate"></property>
    </bean>

Dao操作数据库,可以将JDBC模板注入到Dao中

// 继承 JdbcDaoSupport
public class AccountDaoImpl  implements AccountDao {

    private JdbcTemplate jdbcTemplate;
    
    public void setJdbcTemplate(JdbcTemplate template) {
            this.jdbcTemplate = jdbcTemplate;
    }

    /**
     * 扣钱
     */
    @Override
    public void outMoney(String out, double money) {
        this.getJdbcTemplate().update("update t_account set money = money - ? where name=?", money, out);
    }
    
    /**
     * 加钱
     */
    @Override
    public void inMoney(String in, double money) {
        this.getJdbcTemplate().update("update t_account set money = money + ? where name=?", money, in);
    }

}
  • 修改配置文件中将JDBC模板注入到Dao中注入
<bean id="accountDao" class="com.coderhong.demo1.AccountDaoImpl">
        <property name="jdbcTemplate" ref="org.springframework.jdbc.core.JdbcTemplate"></property>
    </bean>

以上配置及代码完成了业务层注入了dao,dao注入的JDBC模板。可以开发没问题。但是有一个不好的地方,就是我们每一个模块的dao都需要内部写一个JDBC模板成员并提供set方法,并且在配置文件中,到注入模板。

这个时候,Spring为我们提供了一个父类JdbcDaoSupport,到我们让我们的dao继承JdbcDaoSupport,就会报错。原因是JdbcDaoSupport这个父类已经有了这个jdbcTemplate属性并提供了set方法。

JdbcDaoSupport

dao继承了JdbcDaoSupport,需要注释掉JDBC模板属性.在到使用模板通过父类的getJdbcTemplate()获取JDBC模板。

import org.springframework.jdbc.core.support.JdbcDaoSupport;

// 继承 JdbcDaoSupport
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    
    /* 继承父类省略 父类已经实现
        private JdbcTemplate jdbcTemplate;
    
        public void setJdbcTemplate(JdbcTemplate template) {
            this.jdbcTemplate = jdbcTemplate;
        }
    */
    
    /**
     * 扣钱
     */
    @Override
    public void outMoney(String out, double money) {
        this.getJdbcTemplate().update("update t_account set money = money - ? where name=?", money, out);
    }
    
    /**
     * 加钱
     */
    @Override
    public void inMoney(String in, double money) {
        this.getJdbcTemplate().update("update t_account set money = money + ? where name=?", money, in);
    }

}

这样就就解决了只要我们编写dao继承JdbcDaoSupport,配置文件注入JDBC模板就可以了。不用再Dao内部引入JDBC模板属性跟set方法。

现在整体的流程如下:


image.png

在阅读JdbcDaoSupport源码发现下面代码:

image.png

那就是说我们的dao继承了JdbcDaoSupport可以不用注入Jdbc模板。如果内有,直接帮我们创建,并将连接池放入Jdbc模板。可见在配置文件中以后我们不需要配置jdbc模板,之间在到注入连接池。这些前提都是dao继承JdbcDaoSupport。

修改配置配文件

<!-- c3p0连接池 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring_day03?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

<!-- 配置业务层和持久层 -->
    <bean id="accountService" class="com.coderhong.demo1.AccountServiceIml">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

<bean id="accountDao" class="com.coderhong.demo1.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

现在的结构:


image.png

到这里就是最终方案:

  1. 编写的dao继承JdbcDaoSupport
  2. dao中配置文件注入连接池。不需要提供jdbc模板属性跟set方法,父类中已有。

事务管理的引入

如上面的案例,我们使用了JDBC模板来操作转账
我们看下业务层代码

public class AccountServiceIml implements AccountService {
    
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }


    @Override
    public void pay(final String out, final String in, final double monye) {
                
        // 扣钱
        accountDao.outMoney(out, monye);
        //int a = 10 /0 ;
        // 加钱
        accountDao.inMoney(in, monye);
    }

这里存在的问题:
业务层的扣钱跟加钱是两个不同的事务,一旦中间出现异常数据就出现问题,不会回滚。
这里就需要事物来管理了。

Spring框架事务管理分类

  • 手动编写事务管理代码 (不推荐)
  • 声明方式事务管理(底层采用AOP技术)

手动编写事务管理代码 (了解)
不管使用哪种事务分类,都是使用了Spring事务管理接口PlatformTransactionManager来管理事务,所以需要使用到接口的实现类。

提示:这里使用的是JDBC模板,需要使用的实现类是DataSourceTransactionManager

第一步 配置事务管理器

平台事务管理器需要连接池
因为连接池中有连接,平台事务管理器需要拿到连接才可以管理连接。

<!-- 配置平台管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 连接池中有 连接 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

如果使用手动编码的方式,Spring提供了一个类TransactionTemplate模板类。
使用这个类操作事物管理。

配置:

<!-- 配置平台管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 连接池中有 连接 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 手动编码 提供了模板类 使用该类管理事物比较简单版 -->
    <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="transactionManager" ref="transactionManager"></property>
    </bean>

现在整体的结构


Snip20171203_57.png

我们操作TransactionTemplate这个类,其实就是底层AOP技术操作事务管理器
DataSourceTransactionManager。

以上配置完成了,需要在Service中注入TransactionTemplate。使用TransactionTemplate管理事务。

<!-- 配置业务层和持久层 -->
    <bean id="accountService" class="com.coderhong.demo1.AccountServiceIml">
        <property name="accountDao" ref="accountDao"></property>
        <property name="transactionTemplate" ref="transactionTemplate"></property>
    </bean>

在Service使用模板类

public class AccountServiceIml implements AccountService {
    
    // dao
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    // 事务模板
    private TransactionTemplate transactionTemplate;

    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }


    @Override
    public void pay(final String out, final String in, final double monye) {
        
        // 事物的执行
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus arg0) {
                
                // 扣钱
                accountDao.outMoney(out, monye);
                // int a = 10 /0 ;
                // 加钱
                accountDao.inMoney(in, monye);
            }
        });

    }

}

声明方式事务管理(底层采用AOP技术)
两种方式

  • 基于AspectJ的XML方式
  • 基于AspetJ的注解方式

基于AspectJ的XML方式

  • 配置
<!-- 配置连接池 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_day03?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

<!-- 配置平台管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 连接池中有 连接 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!-- 声明式事物 (采用XML配置文件的方式) -->
<!-- 先配置通知 -->
    <tx:advice id="MyAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 可以给方法设置方法属性(隔离级别 传播行为) -->
            <tx:method name="pay" propagation="REQUIRED"/>
            <!-- 可以添加多个方法 -->
        </tx:attributes>
    </tx:advice>

!-- 配置AOP 
        如果是自己编写的aop 使用<aop:aspect></aop:aspect>这个切面
        如果使用Spring提供的切面 <aop:advisor advice-ref=""/>
        -->
    <aop:config>
        <!-- aop:advisor是spring框架提供的通知  -->
        <aop:advisor advice-ref="MyAdvice" pointcut="execution(public * com.coderhong.demo2.AccountServiceIml.pay(..))"/>
    </aop:config>

<!-- 配置业务层和持久层 -->
    <bean id="accountService" class="com.coderhong.demo2.AccountServiceIml">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    
    <bean id="accountDao" class="com.coderhong.demo2.AccountDaoImpl">
        <!-- <property name="jdbcTemplate" ref="org.springframework.jdbc.core.JdbcTemplate"></property> -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

关系图:


image.png

然后配置完成业务层跟dao层的代码就很简单了。
业务层

public class AccountServiceIml implements AccountService {
    
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    @Override
    public void pay(final String out, final String in, final double monye) {

            // 扣钱
            accountDao.outMoney(out, monye);
            // int a = 10 /0 ;
            // 加钱
            accountDao.inMoney(in, monye);
    }
    
}

dao层

// 继承 JdbcDaoSupport
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {  
    /**
     * 扣钱
     */
    @Override
    public void outMoney(String out, double money) {
        this.getJdbcTemplate().update("update t_account set money = money - ? where name=?", money, out);
    }
    
    /**
     * 加钱
     */
    @Override
    public void inMoney(String in, double money) {
        this.getJdbcTemplate().update("update t_account set money = money + ? where name=?", money, in);
    }

}

基于AspetJ的注解方式

配置文件- 开启事务注解

<!-- dbcp连接池 -->
     <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring_day03?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean> 
    
    <!-- 配置平台管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 连接池中有 连接 -->
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 开启事物的注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    
    <!-- 配置业务层和持久层 -->
    <bean id="accountService" class="com.coderhong.demo3.AccountServiceIml">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    
    <bean id="accountDao" class="com.coderhong.demo3.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

在业务层类添加注解@Transactional代表给类的所有方法全部都有了事物
业务层的代码

@Transactional
public class AccountServiceIml implements AccountService {
    
    private AccountDao accountDao;
    
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    @Override
    public void pay(final String out, final String in, final double monye) {

            // 扣钱
            accountDao.outMoney(out, monye);
            // int a = 10 /0 ;
            // 加钱
            accountDao.inMoney(in, monye);
    }
    
}

dao层代码不变。

相关文章

  • 关于事务的思考

    Spring对于事务的支持 Spring事务接口 Spring事务管理器 Spring并不直接管理事务,而是提供多...

  • Spring之事务管理

    Spring事务管理(详解+实例)Spring详解(八)------事务管理 一. 概念 事务(Transacti...

  • Spring-事务机制

    一、Spring事务 事务管理概述 Spring事务管理分为编程式事务管理和声明式事务管理两种 编程式事务:允许用...

  • Spring中的AOP事务

    【目录】1 Spring的事务管理机制2 Spring事务管理两种方式 1 Spring的事务管理机制 Sprin...

  • Spring基础(三)

    11. 事务管理 11.1 Spring Framework事务管理介绍 广泛的事务支持是Spring Frame...

  • Spring事务管理只对出现运行期异常进行回滚

    使用spring难免要用到spring的事务管理,要用事务管理又会很自然的选择声明式的事务管理,在spring的文...

  • Spring事务

    Spring 事务 分类 Spring可以支持编程式事务和声明式事务。 编程式事务 实现 Spring使用事务管理...

  • Spring声明式事务管理之一:五大属性分析

    1.Spring事务管理概述 Spring事务管理分为编程式事务管理和声明式事务管理两种。编程式事务允许用户在实现...

  • spring事务

    1、spring事务管理器PlatformTransactionManager 1.1、没有spring事务管理器...

  • Spring事务的传播特性引发的一场血案

    Spring事务的传播特性是对于Spring事务管理的一项特殊配置;Spring事务基于Spring AOP特性,...

网友评论

      本文标题:Spring事务的管理

      本文链接:https://www.haomeiwen.com/subject/mtgobxtx.html