spring中的事务控制

spring中的事务控制

环境:
Idea:2019.3.1
系统:windows10 家庭版
Jdk: 8
spring:5.0.3 release
spring文档
项目代码

一.概述

1.JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案
2.spring框架为我们提供了一组事务控制的接口,这组接口是在spring-tx-5.0.2.RELEASE.jar中
3.spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现

二.spring中事务控制的API介绍

PlatformTransactionManager

此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法,包含3个具体的操作

    1.获取事务状态信息
        TransactionStatus getTransaction(TransactionDefinition definition)
    2.提交事务
        void commit(TransactionStatus status)
    3.回滚事务
        void rollback(TransactionStatus status)
我们在开发中都是使用它的实现类

    1.org.springframework.jdbc.datasource.DataSourceTransactionManager
        使用Spring JDBC或iBatis进行持久化数据时使用
    2.org.springframework.orm.hibernate5.HibernateTransactionManager
        使用Hibernate版本进行持久化数据时使用

TransactionDefinition
它是事务的定义信息对象,包含的方法如下

String getName()

获取事务对象名称

int getIsolationLevel()

获取事务隔离级别

事务的隔离级别反映事务提交并发访问时的处理态度
    -ISOLATION_DEFAULT
        默认级别,归属于下列某一种
    -ISOLATION_READ_UNCOMMITTED
        可以读取未提交数据
    -ISOLATION_READ_COMMITEED
        只能读取已提交数据,解决脏读问题(Oracle默认级别)
    -ISOLATION_REPEATABLE_READ
        是否读取其他事务提交修改后的数据,解决不可重复读问题(MySQL默认级别)
    -ISOLATION_SERIALIZABLE
        是否读取其他事务提交添加后的数据,解决幻影读问题

int getPropagationBehavior()

获取事务传播行为

事务的传播行为
    REQUIRED:
        如果当前没有事务,就新建一个事务,如果已经存在于一个事务中,则加入到这个事务中。一般的选择(默认值)
    SUPPORTS:
        支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
    MANDATORY:
        使用当前的事务,如果当前没有事务,就抛出异常
    REQUERS_NEW:
        新建事务,如果当前在事务中,把当前事务挂起
    NOT_SUPPORTED:
        以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
    NEVER:
        以非事务方式运行,如果当前存在事务,抛出异常
    NESTED:
        如果当前存在事务,则在嵌套事务内执行,如果当前没有事务,则执行REQUIRED类似的操作

int getTimeout()

获取事务超时时间

默认值是-1,就是没有超时限制,如果有,以秒为单位进行设置

boolean isReadOnly()

获取事务是否只读

读写型事务:增加、删除、修改开启事务
只读型事务:查询时开启事务,建议查询时设置为只读

TransactionStatus
此接口提供的是事务具体的运行状态,包含了6个具体的操作

void flush()

刷新事务

boolean hasSavepoint()

获取是否存在存储点

boolean isCompleted()

获取事务是否完成

boolean isNewTransaction()

获取事务是否为新的事务

boolean isRollbackOnly()

获取事务是否回滚

void setRollbackOnly()

设置事务回滚

三.spring中基于xml的声明式事务控制

沿用上篇文章中的使用spring的JdbcDaoSupport的工程文件,然后做以下修改

导入依赖

别忘了导入spring-jdbc和spring-tx的坐标,其他所需要的按照之前项目导

<?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: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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

</beans>
配置事务管理器
<!--    配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
配置事务的通知

配置事务的通知
<tx:advice></tx:advice>

属性:
    id:给事务通知起一个唯一标识
    transaction-manager:给事务通知提供一个事务管理器引用

配置事务的属性
<tx:attributes></tx:attributes>
在这个标签里面用<tx:method/>标签指定切入点和属性

    属性:
        name="":用于指定切入点名称
        isolation="":用于指定事务的隔离级别,默认值是DEFAULT,表示使用数据库的隔离级别
        propagation=""用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择,查询方法可以选择SUPPORTS
        read-only=""用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写
        timeout=""用于指定事务的超时时间,默认值是-1,表示永不超时,如果指定了数值,以秒为单位
        no-rollback-for=""用于指定一个异常,表示当该异常发生时事务不回滚,产生其他异常时都回滚,没有默认值,表示任何异常都回滚
        rollback-for=""用于指定一个异常,表示当该异常发生时事务回滚,产生其他异常时事务不回滚,没有默认值,表示任何异常都回滚

name属性可以使用**通配符**来表示多个连接点,所以我们一般地将查询方法都用find开头,这样在设置`<tx:method/>`属性时,可以直接指定name = "find*",因为事务的原因,我们一般都按照下面这种写法,为什么这样设置,可以查看最上面对这些属性的值的说明
<!-- 配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--    配置事务的属性-->
    <tx:attributes>
        //增删改方法,命名按自己的来,propagation="REQUIRED",read-only设置为读写
        <tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
        //所有查询方法都用find打头,这里用find*指定所有查询方法,然后设置propagation="SUPPORTS",read-only设置为只读
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>
配置aop中的通用切入点表达式并和事务通知建立关系
<!--    配置aop中的通用切入点表达式-->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* com.cbw.service.impl.*.*(..))"/>
        <!--    建立事务通知和切入点表达式的关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>

完整的配置文件如下

<?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: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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.cbw"></context:component-scan>

<!--    配置账户的持久层-->
    <bean id="accountDao" class="com.cbw.dao.impl.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"></property>
    </bean>
    <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/myspringspace"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
<!--    1.配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<!--    2.配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!--5.配置事务的属性
-->
    <tx:attributes>
        <tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
        <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>
<!--    3.配置aop中的通用切入点表达式-->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* com.cbw.service.impl.*.*(..))"/>
        <!--    4.建立事务通知和切入点表达式的关系-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
    </aop:config>
</beans>

测试
我们运行带int i=1/0错误的转账方法

可以看到转账钱这么多钱,然后报错了,我们再去表中看下,是否转账人转了帐,收账人没收到

可以看到,既没有转账也没有收账,说明事务配置成功了

四.spring中基于注解的事务控制

使用纯注解的方式实现

创建配置类

1.springConfiguration

主配置文件

import org.springframework.context.annotation.*;
import org.springframework.transaction.annotation.EnableTransactionManagement;

/**
 * spring的配置类,相当于applicationContext.xml
 */
//表示这是一个配置类
@Configuration
//设置要扫描的包
@ComponentScan("com.cbw")
//导入另外两个配置类
@Import({JdbcConfig.class,TransactionConfig.class})
//导入外部的properties文件,里面写了数据库连接所需参数
@PropertySource("jdbcConfig.properties")
//开启事务支持
@EnableTransactionManagement
public class SpringConfiguration {

}

2.JdbcConfig

和数据库相关的配置类

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

import javax.sql.DataSource;

/**
 * 和连接数据库相关的类
 */
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String classDriver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    /**
     *
     * 创建JdbcTemplate对象
     * @param dataSource
     * @return
     */
    @Bean(name = "jdbcTemplate")
    public JdbcTemplate createJdbcTemplate(DataSource dataSource){
        return new JdbcTemplate(dataSource);
    }

    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setDriverClassName(classDriver);
        driverManagerDataSource.setUrl(url);
        driverManagerDataSource.setUsername(username);
        driverManagerDataSource.setPassword(password);

        return driverManagerDataSource;
    }
}

3.TransactionConfig

和事务相关的配置类

import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;

/**
 * 和事务相关的配置类
 */
public class TransactionConfig {
    /**
     * 用于创建事务管理器对象的
     * @param dataSource
     * @return
     */
    @Bean("TransactionManager")
    public PlatformTransactionManager createTransactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }
}
jdbcConfig.properties文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/myspringspace
jdbc.username=root
jdbc.password=123456
业务层开启事务

@Transactional注解在service层中的service实现类,开启事务

import com.cbw.dao.IAccountDao;
import com.cbw.domain.Account;
import com.cbw.service.IAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService {
    @Autowired
    private IAccountDao accountDao;

    public Account findAccountByID(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void transfer(String sourceName, String targetName, Float money) {
        //1.查询出转账人
        Account source = accountDao.findAccountByName(sourceName);
        //2.查询出收账人
        Account target = accountDao.findAccountByName(targetName);
        //3.转账人减钱
        source.setMoney(source.getMoney() - money);
        //4.收账人加钱
        target.setMoney(target.getMoney() + money);
        //5.更新两个账户
        accountDao.updateAccount(source);
        accountDao.updateAccount(target);

    }
}

当然这里也可以给方法设置事务属性,还是@Transactional标签

    @Transactional(propagation = Propagation.REQUIRED,readOnly = true)
    public Account findAccountByID(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }
测试

之前的@ContextConfiguration标签里面的location参数改成classes,指明配置类

import com.cbw.config.SpringConfiguration;
import com.cbw.domain.Account;
import com.cbw.service.IAccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class TestTx {

    @Autowired
    private IAccountService as;

    @Test
    public void testTransfer(){
        //转账前
        System.out.println("转账前");
        Account source = as.findAccountByID(1);
        Account target = as.findAccountByID(2);
        System.out.println("转账人:" + source);
        System.out.println("收账人:" + target);

        //转账
        as.transfer(source.getName(),target.getName(),200f);

        //转账后
        System.out.println("转账后");
        source = as.findAccountByID(1);
        target = as.findAccountByID(2);
        System.out.println("转账人:" + source);
        System.out.println("收账人:" + target);
    }
}

老样子,在转账的方法中,加入int i=1/0;

查看表

一样没变


 上一篇
事务控制,动态代理,动态代理实现事务控制 事务控制,动态代理,动态代理实现事务控制
事务控制,动态代理,动态代理实现事务控制 环境:Idea:2019.3.1系统:windows10 家庭版Jdk: 8spring:5.0.3 releasespring文档项目代码 一、事务控制背景操作账户的项目结构如下正常流程是首先创
2020-04-10
下一篇 
spring中的JdbcTemplate spring中的JdbcTemplate
spring中的JdbcTemplate 环境:Idea:2019.3.1系统:windows10 家庭版Jdk: 8spring:5.0.3 releasespring文档项目代码 1.概述它是spring框架中提供的一个对象,是对原始
2020-04-10
  目录