spring 事务控制(06)

spring 事务控制

一. 通常web程序的访问流程是: 用户访问->controller->service->dao. 一个业务的成功,调用的service是执行成功的,意味着service中调用的所有的dao是执行成功的. 所以事务应该在service层统一控制.

1.1 事务控制的基本概念

编程式事务控制:
    自己手动控制事务,叫做编程式事务控制:
        JDBC: connection.setAutoCommite(false); 设置手动控制事务
        Hibernate: session.beginTransaction() 开启一个事务
    细粒度的事务控制: 可以对指定的方法,指定的方法的某几行添加事务控制
    (比较灵活,但是开发起来比较繁琐,每次都要开启,提交,回滚操作)
    
声明式事务控制:
    Spring提供了对事务的管理, 这个就叫声明式事务管理。
    Spring提供了对事务控制的实现。用户如果想用Spring的声明式事务管理,只需要在配置文件中配置即可; 不想使用时直接移除配置。这个实现了对事务控制的最大程度的解耦。
    Spring声明式事务管理,核心实现就是基于Aop。
    【粗粒度的事务控制: 只能给整个方法应用事务,不可以对方法的某几行应用事务。】
    (因为aop拦截的是方法。)

    Spring声明式事务管理器类:
        Jdbc技术:DataSourceTransactionManager
        Hibernate技术:HibernateTransactionManager

1.2 Spring框架的事务管理相关的类和API

1. PlatformTransactionManager接口     -- 平台事务管理器.(真正管理事务的类)。该接口有具体的实现类,根据不同的持久层框架,需要选择不同的实现类!
2. TransactionDefinition接口          -- 事务定义信息.(事务的隔离级别,传播行为,超时,只读)
3. TransactionStatus接口              -- 事务的状态

4. 总结:上述对象之间的关系:平台事务管理器真正管理事务对象.根据事务定义的信息TransactionDefinition 进行事务管理,在管理事务中产生一些状态.将状态记录到TransactionStatus中

5. PlatformTransactionManager接口中实现类和常用的方法
    1. 接口的实现类
        * 如果使用的Spring的JDBC模板或者MyBatis框架,需要选择DataSourceTransactionManager实现类
        * 如果使用的是Hibernate的框架,需要选择HibernateTransactionManager实现类

    2. 该接口的常用方法
        * void commit(TransactionStatus status) 
        * TransactionStatus getTransaction(TransactionDefinition definition) 
        * void rollback(TransactionStatus status) 

6. TransactionDefinition
    1. 事务隔离级别的常量
        * static int ISOLATION_DEFAULT                  -- 采用数据库的默认隔离级别
        * static int ISOLATION_READ_UNCOMMITTED 
        * static int ISOLATION_READ_COMMITTED 
        * static int ISOLATION_REPEATABLE_READ 
        * static int ISOLATION_SERIALIZABLE 

    2. 事务的传播行为常量(不用设置,使用默认值)
        * 先解释什么是事务的传播行为:解决的是业务层之间的方法调用!!

        * PROPAGATION_REQUIRED(默认值) -- A中有事务,使用A中的事务.如果没有,B就会开启一个新的事务,将A包含进来.(保证A,B在同一个事务中),默认值!!
        * PROPAGATION_SUPPORTS          -- A中有事务,使用A中的事务.如果A中没有事务.那么B也不使用事务.
        * PROPAGATION_MANDATORY         -- A中有事务,使用A中的事务.如果A没有事务.抛出异常.

        * PROPAGATION_REQUIRES_NEW(记)-- A中有事务,将A中的事务挂起.B创建一个新的事务.(保证A,B没有在一个事务中)
        * PROPAGATION_NOT_SUPPORTED     -- A中有事务,将A中的事务挂起.
        * PROPAGATION_NEVER             -- A中有事务,抛出异常.

        * PROPAGATION_NESTED(记)     -- 嵌套事务.当A执行之后,就会在这个位置设置一个保存点.如果B没有问题.执行通过.如果B出现异常,运行客户根据需求回滚(选择回滚到保存点或者是最初始状态)
        

二. spring 事务控制的使用

2.1 XML方式配置事务控制

1. 步骤一:引入开发包
        c3p0-0.9.1.2.jar
        commons-logging-1.1.3.jar
        mysql-connector-java-5.1.7-bin.jar
        spring-aop-4.1.6.RELEASE.jar
        spring-aspects-4.1.6.RELEASE.jar
        spring-beans-4.1.6.RELEASE.jar
        spring-context-4.1.6.RELEASE.jar
        spring-core-4.1.6.RELEASE.jar
        spring-expression-4.1.6.RELEASE.jar
        spring-jdbc-4.1.6.RELEASE.jar
        spring-tx-4.1.6.RELEASE.jar

2. 步骤二:配置事务管理器
    <!-- 配置事务管理器 -->
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>

3. 步骤三:配置事务增强
    <!-- 配置事务增强 -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <!--
                name        :绑定事务的方法名,可以使用通配符,可以配置多个。
                propagation :传播行为
                isolation   :隔离级别
                read-only   :是否只读
                timeout     :超时信息
                rollback-for:发生哪些异常回滚.
                no-rollback-for:发生哪些异常不回滚.
             -->
            <!-- 哪些方法加事务 -->
             <tx:method name="get*" read-only="true"></tx:method>
                <tx:method name="find*" read-only="true"></tx:method>
                <tx:method name="*" read-only="false"></tx:method>
        </tx:attributes>
    </tx:advice>

4. 步骤四:配置AOP的切面:哪些方法需要事务控制
    <!-- 配置AOP切面产生代理 -->
        <aop:config>
            <aop:pointcut expression="execution(* com.it.service.impl.DeptServiceImpl.*(..))" id="pt"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
        </aop:config>

    * 注意:如果是自己编写的切面,使用<aop:aspect>标签,如果是系统制作的,使用<aop:advisor>标签。

5. 步骤5:编写测试类
    package com.it.c_transaction;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.it.entity.Dept;
import com.it.service.DeptService;

public class TransactionDemo {
    
    //xml方式使用事务控制
    @Test
    public void testTransaction(){
        
        ApplicationContext aContext = new ClassPathXmlApplicationContext("bean.xml");
        DeptService service = (DeptService) aContext.getBean("deptService");
        Dept dept = new Dept();
        dept.setDeptName("技术部");
        service.save(dept);
        System.out.println("保存成功!");
    }
}

主要类的配置
实体类Entity:
Dept.java

package com.it.entity;

public class Dept {
    private int deptId;
    private String deptName;
    public int getDeptId() {
        return deptId;
    }
    public void setDeptId(int deptId) {
        this.deptId = deptId;
    }
    public String getDeptName() {
        return deptName;
    }
    public void setDeptName(String deptName) {
        this.deptName = deptName;
    }
}

Repository: DeptDaoImpl.java

package com.it.dao.impl;

import org.springframework.jdbc.core.JdbcTemplate;

import com.it.dao.DeptDao;
import com.it.entity.Dept;

public class DeptDaoImpl implements DeptDao{

    private JdbcTemplate jdbcTemplate;
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    @Override
    public void save(Dept dept) {
        // TODO Auto-generated method stub
        String sql = "insert into t_dept(deptName) values(?)";
        jdbcTemplate.update(sql,dept.getDeptName());
        
    }

}

service层: DeptServiceImpl.java

package com.it.service.impl;

import com.it.dao.DeptDao;
import com.it.entity.Dept;
import com.it.service.DeptService;

public class DeptServiceImpl implements DeptService{

    private DeptDao deptDao;
    public void setDeptDao(DeptDao deptDao) {
        this.deptDao = deptDao;
    }
    
    
    @Override
    public void save(Dept dept) {
        // TODO Auto-generated method stub
        deptDao.save(dept);
        //int i = 10 / 0;
        //deptDao.save(dept);
    }

}

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:tx="http://www.springframework.org/schema/tx"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
        
        
        <!-- 数据源对象: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:///spring_jdbc?useUnicode=true&amp;characterEncoding=utf8"></property>
            <property name="user" value="root"></property>
            <property name="password" value="1l9o9v0e"></property>
            <property name="initialPoolSize" value="3"></property>
            <property name="maxPoolSize" value="10"></property>
            <property name="maxStatements" value="100"></property>
            <property name="acquireIncrement" value="2"></property>
        
        </bean>
        
        
        <!-- jdbcTemplate工具类实例 -->
        <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <bean id="userDao" class="com.it.dao.impl.UserDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>
        
        <!-- 3. dao实例 -->
        <bean id="deptDao" class="com.it.dao.impl.DeptDaoImpl">
            <property name="jdbcTemplate" ref="jdbcTemplate"></property>
        </bean>
     
        <!-- 4. service实例 -->
        <bean id="deptService" class="com.it.service.impl.DeptServiceImpl">
            <property name="deptDao" ref="deptDao"></property>
       </bean>
       
       <bean id="annoDeptService" class="com.it.service.impl.anno.DeptServiceImplAnno">
            <property name="deptDao" ref="deptDao"></property>
       </bean>
       
       <!-- spring声明式事务配置 -->
        
        <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
        
        <!-- 配置事务增强 -->
        <tx:advice id="txAdvice" transaction-manager="txManager">
            <tx:attributes>
                <tx:method name="get*" read-only="true"></tx:method>
                <tx:method name="find*" read-only="true"></tx:method>
                <tx:method name="*" read-only="false"></tx:method>
            
            </tx:attributes>
        </tx:advice>
        
        <!-- aop配置:哪些方式需要拦截 -->
        
        <aop:config>
            <aop:pointcut expression="execution(* com.it.service.impl.DeptServiceImpl.*(..))" id="pt"/>
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
        </aop:config>
</beans>

测试事务控制
TransactionDemo.java

package com.it.c_transaction;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.it.entity.Dept;
import com.it.service.DeptService;

public class TransactionDemo {
    
    //xml方式使用事务控制
    @Test
    public void testTransaction(){
        
        ApplicationContext aContext = new ClassPathXmlApplicationContext("bean.xml");
        DeptService service = (DeptService) aContext.getBean("deptService");
        Dept dept = new Dept();
        dept.setDeptName("技术部");
        service.save(dept);
        System.out.println("保存成功!");
    }
}

当运行测试方法时,成功的插入一条记录;当我们在service中加入: int i = 10/0;时,运行
测试方式,发现没有记录插入到数据库,这正是使用了事务管理,进行了rollback回滚操作.

2.2 使用注解方式进行事务管理

2.2.1

XML文件中添加配置


        <!-- 注解方式的声明式事务管理 -->
        
            <!-- 开启注解扫描 -->
        <context:component-scan base-package="com.it.service.impl.anno"></context:component-scan>
        
        <!-- 1.开启注解事务 -->
        <tx:annotation-driven transaction-manager="txManager" />

创建另外的service文件

DeptServiceImplAnno.java


package com.it.service.impl.anno;

import org.springframework.transaction.annotation.Transactional;

import com.it.dao.DeptDao;
import com.it.entity.Dept;
import com.it.service.DeptService;

public class DeptServiceImplAnno implements DeptService{
    
    private DeptDao deptDao;
    public void setDeptDao(DeptDao deptDao) {
        this.deptDao = deptDao;
    }
    
    
    @Transactional(
                value="txManager"
                //noRollbackFor = ArithmeticException.class
            )
    @Override
    public void save(Dept dept) {
        // TODO Auto-generated method stub
        deptDao.save(dept);
        int i = 10 / 0;
        deptDao.save(dept);
    }
}

进行测试

//注解方式使用事务控制
    
    @Test
    public void testTransactionAnno(){
        
        ApplicationContext aContext = new ClassPathXmlApplicationContext("bean.xml");
        DeptService service = (DeptService) aContext.getBean("annoDeptService");
        Dept dept = new Dept();
        dept.setDeptName("行政部");
        service.save(dept);
        System.out.println("保存成功!");
    }

可以实现和XML配置同样的作用.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,591评论 18 139
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,732评论 6 342
  • 水调歌头·昵昵儿女语宋 · 苏轼欧阳文忠公尝问余:琴诗何者最善?答以退之听颖师琴诗最善。公曰:此诗最奇丽,然非听琴...
    于斯用心阅读 315评论 0 0
  • 忽然发现自己有时候会情绪低落,这情绪低落会让人很不开心。有时候刨根问底地感受到这个低落来自于事态的失控感。 事态的...
    一十七掌阅读 1,539评论 0 1
  • 难过了何必说?说了反而像是要别人施舍可怜和安慰那样……多可悲啊,别人不是你,没有经历你正处于的情况,他们不会理解你...
    周二二二二二二二二阅读 142评论 0 2