Spring 事务管理的使用

Spring提供了2种事务管理

  • 编程式的
  • 声明式的(重点):包括xml方式、注解方式(推荐)

基于转账的demo

dao层

新建包com.chy.dao,包下新建接口AccountDao、实现类AccountDaoImpl:

public interface AccountDao {
    //查询用户账户上的余额
    public double queryMoney(int id);
    //减少用户账户上的余额
    public void reduceMoney(int id, double amount);
    //增加用户账户上的余额
    public void addMoney(int id, double amount);
}
@Repository
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
    @Override
    public double queryMoney(int id) {
        String sql = "select money from account_tb where id=?";
        JdbcTemplate jdbcTemplate = super.getJdbcTemplate();
        double money = jdbcTemplate.queryForObject(sql, double.class,id);
        return money;
    }

    @Override
    public void reduceMoney(int id, double account) {
        double money = queryMoney(id);
        money -= account;
        if (money>=0){
            String sql = "update account_tb set money=? where id=?";
            JdbcTemplate jdbcTemplate = super.getJdbcTemplate();
            jdbcTemplate.update(sql, money, id);
        }
        //此处省略余额不足时的处理
    }

    @Override
    public void addMoney(int id, double account) {
        double money = queryMoney(id);
        money += account;
        String sql = "update account_tb set money=? where id=?";
        JdbcTemplate jdbcTemplate = super.getJdbcTemplate();
        jdbcTemplate.update(sql, money, id);
    }
}

service层

新建包com.chy.service,包下新建接口TransferService、实现类TransferServiceImpl:

public interface TransferService {
    public void transfer(int from,int to,double account);
}
@Service
public class TransferServiceImpl implements TransferService {
    private AccountDao accountDao;

    @Autowired
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public void transfer(int from, int to, double account) {
        accountDao.reduceMoney(from,account);
        // System.out.println(1/0);
        accountDao.addMoney(to,account);
    }
}

数据库连接信息

src下新建db.properties:

#mysql数据源配置
url=jdbc:mysql://localhost:3306/my_db?serverTimezone=GMT
user=chy
password=abcd

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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 引入数据库配置信息 -->
    <context:property-placeholder location="db.properties" />

    <!-- 使用包扫描-->
    <context:component-scan base-package="com.chy.dao,com.chy.service" />

    <!-- 配置数据源,此处使用jdbc数据源 -->
    <bean name="jdbcDataSource" class="com.mysql.cj.jdbc.MysqlDataSource">
        <property name="url" value="${url}" />
        <property name="user" value="${user}" />
        <property name="password" value="${password}" />
    </bean>

    <!-- 配置AccountDaoImpl,注入数据源 -->
    <bean name="accountDaoImpl" class="com.chy.dao.AccountDaoImpl">
        <!--注入数据源-->
        <property name="dataSource" ref="jdbcDataSource" />
    </bean>
</beans>

测试

新建包com.chy.test,包下新建主类Test:

        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
        TransferServiceImpl transferServiceImpl = applicationContext.getBean("transferServiceImpl", TransferServiceImpl.class);
        transferServiceImpl.transfer(1,2,1000);

以上是未使用事务管理的,将service层的这句代码取消注释,会出现钱转丢的情况(对方收不到钱)。

// System.out.println(1/0);

编程式事务管理

编程式事务管理,顾名思义需要自己写代码。

自己写代码很麻烦,spring把代码封装在了TransactionTemplate类中,方便了很多。

(1)事务需要添加到业务层(service),修改TransferServiceImpl类如下:

@Service
public class TransferServiceImpl implements TransferService {
    private AccountDao accountDao;
    private TransactionTemplate transactionTemplate;

    @Autowired
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

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

    @Override
    public void transfer(int from, int to, double account) {
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
                accountDao.reduceMoney(from, account);
                // System.out.println(1 / 0);
                accountDao.addMoney(to, account);
            }
        });
    }
}
  • 注入事务管理模板TransactionTemplate(成员变量+setter方法)。
  • 在处理业务的方法中写:
transactionTemplate.execute(new TransactionCallbackWithoutResult() {  });

使用匿名内部类传入一个TransactionCallbackWithoutResult接口类型的变量,只需实现一个方法:把原来处理业务的代理都放到这个方法中。


(2)在xml中配置事务管理、事务管理模板

    <!-- 配置事务管理器,此处使用JDBC的事务管理器-->
    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="jdbcDataSource" />
    </bean>

    <!-- 配置事务管理模板-->
    <bean class="org.springframework.transaction.support.TransactionTemplate">
        <!-- 注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager" />
    </bean>

把下面这句代码取消注释,运行,不会出现钱转丢的情况(执行失败,自动回滚):

// System.out.println(1/0);

声明式事务管理(此节必看)

声明式事务管理,不管是基于xml,还是基于注解,都有以下3个点要注意:

  • 底层是使用AOP实现的(在Spring中使用AspectJ),需要把AspectJ相关的jar包添加进来。
  • 因为我们的service层一般写成接口——实现类的形式,既然实现了接口,基于动态代理的AspectJ代理的自然是接口,所以只能使用接口来声明:
TransferService transferServiceImpl = applicationContext.getBean("transferServiceImpl", TransferService.class);

红色标出的2处只能使用接口。

  • 在配置xml时有一些新手必遇到的坑,如果在配置xml文件时遇到了问题,可滑到最后面查看我写的解决方式。

基于xml的声明式事务管理

xml配置:

    <!-- 配置事务管理器,此处使用JDBC的事务管理器-->
    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="jdbcDataSource" />
    </bean>

    <!-- 配置增强-->
    <!-- 此处只能用id,不能用name。transaction-manager指定要引用的事务管理器 -->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <!-- 配置要添加事务的方法,直接写方法名即可。可以有多个method元素,可以使用通配符* -->
            <tx:method name="transfer" />
        </tx:attributes>
    </tx:advice>

    <!-- 配置aop-->
    <aop:config>
        <!-- 配置切入点-->
        <aop:pointcut id="transferPointCut" expression="execution(* com.chy.service.TransferServiceImpl.transfer(..))"/>
        <!--指定要引用的<tx:advice>,指定切入点。切入点可以point-ref引用,也可以pointcut现配。可以有多个advisor元素。 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="transferPointCut"/>
    </aop:config>

说明

<tx:method name="transfer" propagation="REQUIRED" isolation="DEFAULT" read-only="false" />
  • propagation 指定事务的传播行为,默认为REQUIRED
  • isolation 指定事务的隔离级别
  • read-only 是否只读,读=>查询,写=>增删改。默认为false——读写。
  • timeout  事务的超时时间,-1表示永不超时。

这4个属性一般都不用设置,使用默认值即可。当然,只查的时候,可以设置read-only="true"。

常见的3种配置方式:

            <tx:method name="transfer" />
            <tx:method name="*" />
            <tx:method name="save*" />
            <tx:method name="find*" read-only="false"/>
            <tx:method name="update*" />
            <tx:method name="delete*" />     
  • 指定具体的方法名
  • 用*指定所有方法
  • 指定以特定字符串开头的方法(我们写的dao层方法一般都以这些词开头)

因为要配合aop使用,筛选范围是切入点指定的方法,不是项目中所有的方法。

比如

<tx:method name="save*" />
<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.chy.service.TransferServiceImpl.*(..))" />

切入点是TransferServiceImpl类中的所有方法,是在TransferServiceImpl类所有方法中找到以save开头的所有方法,给其添加事务。


基于注解的声明式事务管理(推荐)

(1)xml配置

    <!-- 配置事务管理器,此处使用JDBC的事务管理器-->
    <bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="jdbcDataSource" />
    </bean>

    <!--启用事务管理的注解,并指定要使用的事务管理器 -->
    <tx:annotation-driven transaction-manager="transactionManager" />

(2)使用@Transactional标注

@Service
// @Transactional
public class TransferServiceImpl implements TransferService {
    private AccountDao accountDao;

    @Autowired
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    @Transactional
    public void transfer(int from, int to, double account) {
        accountDao.reduceMoney(from, account);
        // System.out.println(1 / 0);
        accountDao.addMoney(to, account);
    }
}

可以标注在业务层的实现类上,也可以标注在处理业务的方法上。

标注在类上,会给这个所有的业务方法都添加事务;标注在业务方法上,则只给这个方法添加事务。

同样可以设置传播行为、隔离级别、是否只读:

@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT,readOnly = false)

基于注解的声明式事务管理是最简单的,推荐使用。


声明式事务管理,配置xml时的坑

  • <tx:advice>、<tx:annotation-driven>的代码提示不对、配置没错但仍然显式红色

原因:IDEA自动引入的约束不对。

IDEA会自动引入所需的约束,但spring事务管理所需的约束,IDEA引入的不对。

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

看我红色标出的部分(需拖动滚动条查看最右边),这是IDEA自动引入的tx的约束,这是旧版本的约束,已经无效了。

新版本的约束:

xmlns:tx="http://www.springframework.org/schema/tx"

xsi里对应的部分也要修改:

http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd

可以在错误约束的基础上改,也可以添加。

如果没有修改xsi中对应的部分,会报下面的错:

通配符的匹配很全面, 但无法找到元素 ‘tx:advice‘ 的声明。
通配符的匹配很全面, 但无法找到元素 ‘tx:annotation-driven‘ 的声明。

上面提供的约束是目前版本的约束,以后可能会变。最好在官方文档中找,有2种方式:

(1)http://www.springframework.org/schema/tx/spring-tx.xsd     可修改此url查看其它约束

(2)如果下载了spring的压缩包,可在schema文件夹下查看约束。

spring压缩包的下载可参考:https://www.cnblogs.com/chy18883701161/p/11108542.html

原文地址:https://www.cnblogs.com/chy18883701161/p/12239011.html

时间: 2024-10-09 17:53:25

Spring 事务管理的使用的相关文章

Spring事务管理(详解+实例)

写这篇博客之前我首先读了<Spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

Spring事务管理

写这篇博客之前我首先读了<spring in action>,之后在网上看了一些关于Spring事务管理的文章,感觉都没有讲全,这里就将书上的和网上关于事务的知识总结一下,参考的文章如下: Spring事务机制详解 Spring事务配置的五种方式 Spring中的事务管理实例详解 1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是要么都执行要么都

Spring事务管理接口PlatformTransactionManager的实现类DataSourceTransactionManager

package org.springframework.jdbc.datasource; import java.sql.Connection; import java.sql.SQLException; import javax.sql.DataSource; import org.apache.commons.logging.Log; import org.springframework.beans.factory.InitializingBean; import org.springfra

Spring 事务管理高级应用难点剖析--转

第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN&en=utf&hpp=20&dws=cndw&lo=zh 概述 Spring 最成功,最吸引人的地方莫过于轻量级的声明式事务管理,仅此一点,它就宣告了重量级 EJB 容器的覆灭.Spring 声明式事务管理将开发者从繁复的事务管理代码中解脱出来,专注于业务逻辑的开发上,这是一件

Spring事务管理--多个ORM框架在使用时的情况分析

公司的项目已经接近尾声了,总结一下项目中用到的技术,我发现项目中的有些东西还是挺模糊的,只是知道这么用就行了.并不清楚其中的原理.由于公司的项目比较老,是7年前的一个项目了,中间一直有人在维护,也是在这个过程中不断融入了新的东西,比如就项目的持久化这块来说,就用了ibatis.mybatis.hibernate.spring JDBC四种混合的框架.究其原因只能说是历史遗留问题,就不做过多的解释了.但是这么多持久化的框架如何协同工作的,尤其是事务的控制,一个系统中使用如此多的持久化框架是,他们是

详细介绍Spring事务管理

在学习spring事务管理时,我忍不住要问,spring为什么进行事务管理,spring怎么进行的事务管理?首先,为什么要进行事务,接下来说说spring是怎样进行事务管理的. 我们都知道spring提供两种管理事务的方式,一种是声明式事务,一种是编程式事务. Spring的声明式事务管理,基于Spring的AOP,不再需要不停地写commit,rollback,(但Spring仍然没有放弃编程式的事务管理策略). Spring的编程式事务管理,为我们提供了一个TransactionTempla

Spring事务管理----------整合学习版

作者:学无先后 达者为先 Spring提供了一流的事务管理.在Spring中可以支持声明式事务和编程式事务. 一  spring简介 1 Spring的事务       事务管理在应用程序中起着至关重要的作用:它是一系列任务的组成工作单元,在这个工作单元中,所有的任务必须同时执行.它们只有二种可能执行结果,要么所有任务全部执行成功,要么所有任务全部执行失败.     Spring中提供了丰富的事务管理功能,它们超过了EJB并且和EJB一样支持声明式事务,重要的是Spring提供了一致的事务管理,

spring事务管理——编程式事务、声明式事务

本教程将深入讲解 Spring 简单而强大的事务管理功能,包括编程式事务和声明式事务.通过对本教程的学习,您将能够理解 Spring 事务管理的本质,并灵活运用之. 先决条件 本教程假定您已经掌握了 Java 基础知识,并对 Spring 有一定了解.您还需要具备基本的事务管理的知识,比如:事务的定义,隔离级别的概念,等等.本文将直接使用这些概念而不做详细解释.另外,您最好掌握数据库的基础知识,虽然这不是必须. 系统需求 要试验这份教程中的工具和示例,硬件配置需求为:至少带有 512MB 内存(

Spring事务管理-使用注解配置事务

一.概述 事务管理对于企业应用来说是至关重要的,即使出现异常情况,它也可以保证数据的一致性.Spring Framework对事务管理提供了一致的抽象,其特点如下:为不同的事务API提供一致的编程模型,比如JTA(Java Transaction API), JDBC, Hibernate, JPA(Java Persistence API和JDO(Java Data Objects)支持声明式事务管理,特别是基于注解的声明式事务管理,简单易用 提供比其他事务API如JTA更简单的编程式事务管理

分布式事务系列(1.1)Spring事务管理器PlatformTransactionManager

1 系列目录 分布式事务系列(开篇)提出疑问和研究过程 分布式事务系列(1.1)Spring事务管理器PlatformTransactionManager源码分析 2 jdbc事务 2.1 例子 public void save(User user) throws SQLException{ Connection conn=jdbcDao.getConnection(); conn.setAutoCommit(false); try { PreparedStatement ps=conn.pre