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配置同样的作用.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

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