Spring事务(2):声明式事务管理案例-转账(xml、注解)

1 编写转账案例,引出事务管理问题

需求:账号转账,Tom账号取出1000元,存放到Jack账号上

1.1 建表脚本(MySQL)

 CREATE TABLE t_account  (
 id INT(11) NOT NULL AUTO_INCREMENT,
 name VARCHAR(20) NOT NULL,
 money DOUBLE DEFAULT NULL,
 PRIMARY KEY (id)
)  

INSERT INTO `t_account` (`id`, `name`, `money`) VALUES ('1', 'tom', '1000');
INSERT INTO `t_account` (`id`, `name`, `money`) VALUES ('2', 'jack', '1100');
INSERT INTO `t_account` (`id`, `name`, `money`) VALUES ('3', 'rose', '2000');

1.2 新建工程

第一步:新建一个maven项目

第二步:引入依赖和applicationContext.xml配置文件和log4j.properties文件和db.properties文件:

pom.xml:

    <dependencies>
        <!-- junit测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>

        <!-- spring核心包 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.2.8.RELEASE</version>
        </dependency>
        <!-- spring集成测试 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>4.2.8.RELEASE</version>
        </dependency>

        <!-- spring事物管理 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>4.2.8.RELEASE</version>
        </dependency>


        <!-- c3p0数据源 -->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

        <!-- 数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>

        <!-- 注解开发切面包 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.1</version>
        </dependency>

    </dependencies>

applicationContext.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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">
</beans>

log4j.properties:

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
log4j.rootLogger=info, stdout

db.properties:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.222.156:3306/spring?characterEncoding=utf8&useSSL=false
jdbc.user=root
jdbc.password=123456

第三步:创建IAccountDao接口

创建AccounDaoImpl实现类,实现了IAccountDao接口

账户操作持久层

技术方案:jdbctempate

package com.example.demo.dao;

public interface IAccountDao {
    // 转出
    public void out(String name, Double money);

    // 转入
    public void in(String name, Double money);
}

第四步:建立service层,创建IAccountService接口,编写转账的业务代码:

package com.example.demo.service;

public interface IAccountService {
    //转账业务:
    public void transfer(String outName,String inName,Double money);
}

package com.example.demo.service.impl;

import com.example.demo.dao.IAccountDao;
import com.example.demo.service.IAccountService;

public class AccountServiceImpl implements IAccountService {
    // 注入dao
    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    // 转账业务
    public void transfer(String outName, String inName, Double money) {

        // 先转出
        accountDao.out(outName, money);

        // 再转入
        accountDao.in(inName, money);

    }
}

第五步: 将对象配置到spring工厂

applicationContext.xml文件添加配置

    <!-- 引入配置文件 -->
    <context:property-placeholder location="classpath:db.properties" />
    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>


    <!-- 管理dao和service -->
    <bean id="accountDao" class="com.example.demo.dao.impl.AccountDaoImpl">
        <!-- 注入数据源 -->
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="accountService" class="com.example.demo.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

第六步:使用SpringTest进行测试

package com.example.demo.service.impl;

import com.example.demo.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;

//集成spring测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class AccountServiceImplTest {

    //注入测试对象
    @Autowired
    private IAccountService accountService;


    @Test
    public void testTransfer() {
        accountService.transfer("tom", "jack", 1000d);
    }

}

但是发现问题:

事务管理问题:在Service层没有事务的情况下,如果出现异常,则会转账不成功,数据异常。

在转账方法中添加如下异常:

运行前:

运行后:

事务未生效。

注意:如果不配置事务,那么每一个数据库的操作都是单独的一个事务。

2 XML配置方式添加事务管理(tx、aop元素)

【操作思路】:aop三步走

  1. 确定目标:需要对AccountService 的 transfer方法,配置切入点
  2. 需要Advice (环绕通知),方法前开启事务,方法后提交关闭事务
  3. 配置切面和切入点

配置Advice通知:

Spring为简化事务的配置,提供了**<tx:advice>**来配置事务管理,也可以理解为该标签是spring为你实现好了的事务的通知增强方案。

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

	<!-- 配置事物通知 -->
	<!-- transaction-manager: 指定事物管理器的id,如果事物管理器的id为transactionManager的话 该属性可以省略(缺省值) -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 配置事物管理细则(事物定义信息) -->
		<tx:attributes>
			<!-- 需要被增强(事物 管理)的方法 -->
			<tx:method name="transfer" isolation="DEFAULT" propagation="REQUIRED"
				read-only="false" timeout="-1" />
		</tx:attributes>
	</tx:advice>

	<!-- 配置切入点和切面 -->
	<aop:config>
		<aop:pointcut expression="bean(*Service)" id="mycut" />
		<aop:advisor advice-ref="txAdvice" pointcut-ref="mycut" />
	</aop:config>

使用AccountServiceImplTest.java测试:数据正常!

事物添加的前后对比

没有添加事务:

两个方法分属不同事务。

添加事务后:

分属同一事务

【注意】如果不配置,则走默认的事务(默认事务是每个数据库操作都是一个事务,相当于没事务),所以我们开发时需要配置事务。

3 注解配置方式添加事务管理 @Transactional

步骤:

  1. 在需要管理事务的方法或者类上面 添加@Transactional 注解
  2. 配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)

创建项目spring_transaction_anntx:

替换applicationContext.xml中的<bean> 配置为注解

改造dao:

package com.example.demo.dao.impl;

import com.example.demo.dao.IAccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;

import javax.sql.DataSource;

@Repository("accountDao")
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    //将数据源注入给父类,父类中需要通过数据源创建jdbctemplate
    @Autowired
    public void setSuperDataSource(DataSource dataSource){
        super.setDataSource(dataSource);
    }

    public void out(String name, Double money) {
        String sql = "update t_account set money = money-? where name = ?";
        super.getJdbcTemplate().update(sql, money, name);

    }

    public void in(String name, Double money) {
        String sql = "update t_account set money = money+? where name = ?";
        super.getJdbcTemplate().update(sql, money, name);
    }
}

改造service:

package com.example.demo.service.impl;

import com.example.demo.dao.IAccountDao;
import com.example.demo.service.IAccountService;
import org.springframework.stereotype.Service;

@Service("accountService")
public class AccountServiceImpl implements IAccountService {
    // 注入dao
    @Autowired
    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    // 转账业务
    public void transfer(String outName, String inName, Double money) {

        // 先转出
        accountDao.out(outName, money);

        // 再转入
        accountDao.in(inName, money);

    }
}

在applicationContext.xml中配置注解扫描:

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.example.demo" />

测试方法是否能正常运行

以上步骤全部没问题后,开始配置注解方式的事物管理

第一步:配置 事物管理器:

在applicationContext.xml中,根据选用的持久层框架配置事物管理器:

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

第二步: 在需要管理事物的方法上添加@Transactional注解,表示对该方法进行事物管理

第三步:在applicationContext.xml中开启事物注解驱动,让@Transactional注解生效

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd">
    <!-- 引入配置文件 -->
    <context:property-placeholder location="classpath:db.properties" />
    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClass}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.user}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.example.demo" />

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

    <!--配置事务注解驱动-->
    <tx:annotation-driven transaction-manager="transactionManager" />

</beans>

第四步:测试事物是否正常

提示:

如果 @Transactional 标注在 Class 上面, 那么将会对这个 Class 里面所有的 public 方法都包装事务方法。等同于该类的每个公有方法都放上了@Transactional。

如果某方法需要单独的事务定义,则需要在方法上加@Transactional来覆盖类上的标注声明。记住:方法级别的事务覆盖类级别的事务(就近原则)

package com.example.demo.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.demo.dao.IAccountDao;
import com.example.demo.service.IAccountService;

@Service("accountService")
@Transactional
// 放置在类上表示对该类中所有的方法都进行事物管理
public class AccountServiceImpl implements IAccountService {

   // 注入dao
   @Autowired
   private IAccountDao accountDao;

   // 转账业务
   @Transactional
   public void transfer(String outName, String inName, Double money) {

   	// 先转出
   	accountDao.out(outName, money);

   	// 发生异常
   	int i = 1 / 0;

   	// 再转入
   	accountDao.in(inName, money);

   }

   @Transactional(readOnly = true)
   // 当方法上的事物定义信息和类上的冲突时,就近原则使用方法上的配置
   public Double queryMoney(String name) {
   	// TODO Auto-generated method stub
   	return null;
   }

}

4 小结-xml和注解的选择

XML配置方式和注解配置方式进行事务管理 哪种用的多?

XML方式,集中式维护,统一放置到applicationContext.xml文件中,缺点在于配置文件中的内容太多。

使用@Transactional注解进行事务管理,配置太分散,使用XML进行事务管理,属性集中配置,便于管理和维护

注意:以后的service的方法名字的命名,必须是上面规则,否则,不能被spring事务管理。!!!!

即以save开头的方法,update开头的方法,delete开头的方法,表示增删改的操作,故事务为可写

以find开头的方法,表示查询,故事务为只读

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/294008.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mysql 添加用户并分配select权限

1.root用户先登录或者在可执行界面 1.1 选择mysql 点击mysql 或者在命令行 use mysql 1.2创建用户 CREATE USER username% IDENTIFIED BY password; 备注1&#xff1a;%替换为可访问数据库的ip&#xff0c;例如“127.0.0.1”“192.168.1.1”&#xff0c;使用“%”表示不限制…

Python办公自动化 – 操控远程桌面和文件版本控制

Python办公自动化 – 操控远程桌面和文件版本控制 以下是往期的文章目录&#xff0c;需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处理 Python办公自动化 –…

vue项目 Network: unavailable的解决办法

vue项目npm run serve 后&#xff0c;只有localhost访问&#xff0c;network不能访。 看到网上说有三种情况&#xff1a; 多个网卡原因&#xff1a;打开网络共享中心&#xff0c;把多余的网络禁用掉&#xff0c;只留一个 在中配置host及public 系统环境变量问题…

小学副科老师轻松吗

在小学里&#xff0c;除了语文、数学和英语这些主科&#xff0c;还有许多副科老师&#xff0c;他们的工作日常是什么样的呢&#xff1f;今天&#xff0c;让我们一起来揭秘小学副科老师的一天。 备课&#xff1a;在忙碌中寻找创意的火花 副科老师同样需要花费大量时间进行备课…

XTU OJ 1525瓷片

题意 给定一个2n的地面&#xff0c;用11和1*2的瓷片铺满&#xff0c;问有多少种方案 数据范围 n<30 输入 3 1 2 30 输出 2 7 1084493574452273 代码 #include<stdio.h>int main() {int t;scanf("%d",&t);long long a[40];a[0]1,a[1]2,a[2]7;fo…

2023APMCM亚太数学建模C题 - 中国新能源汽车的发展趋势(3)

六、问题三的模型建立和求解 6.1问题分析 问题3.收集数据&#xff0c;建立数学模型分析新能源电动汽车对全球传统能源汽车行业的影响。 本题要求建立模型分析新能源电动汽车对全球传统能源汽车行业的影响。由于数据集可能略大&#xff0c;而在处理复杂问题、大量特征和大规模…

ubuntu 安装 anaconda

ubuntu 安装 anaconda 下载 wget https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh安装 bash Anaconda3-2023.09-0-Linux-x86_64.sh2.1 回车继续 2.2 许可协议 输入 q 退出阅读许可协议 2.3 输入 yes 接受 许可协议 2.4 设置 anaconda 安装位置 如不需…

CSS3新增文本样式-text-shadow属性

文本样式 概念:在CSS3中&#xff0c;增加了丰富的文本修饰效果&#xff0c;使得页面更加美观舒服。 常用的文本样式属性 属性说明text-shadow文本阴影text-stroke文本描边text-overflow文本溢出word-wrap强制换行font-face嵌入字体 W3C坐标系 我们日常生活使用最多的是数学坐…

U盘如何设置密码?U盘数据该怎么加密?

U盘等移动储存设备可以存储很多重要文件&#xff0c;方便我们随时使用。为了避免数据泄露&#xff0c;我们需要加密保护U盘数据。那么&#xff0c;U盘数据该怎么加密呢&#xff1f;下面我们就来了解一下。 U盘数据加密保护的必要性 目前&#xff0c;大多数的U盘并不具备数据加…

记一次http换成https的过程

记一次http换成https的过程 http默认端口是80&#xff0c;https默认端口是443&#xff0c;此文章主要记录一次网站配置https的过程。 1. 下载申请下载ssl证书 SSL证书是由证书颁发机构审核验证后颁发的&#xff0c;这种颁发机构也叫CA机构&#xff0c;是一个受信任的数字证书…

添加 is_similar_dict 校验器,判断字典格式是否一致

目录 一、前置说明1、总体目录2、相关回顾3、本节目标 二、操作步骤1、项目目录2、代码实现3、测试代码4、日志输出 三、后置说明1、要点小结2、下节准备 一、前置说明 1、总体目录 《 pyparamvalidate 参数校验器&#xff0c;从编码到发布全过程》 2、相关回顾 param_vali…

vite中配置服务代理

前言 在vite中配置服务代理和webpack中大差不差,不过有些写法会有些不同 具体配置:配置 Vite {#configuring-vite} | Vite中文网 这里我写了一个demo,如下所示 开启node服务 我用express启动了一个服务,分别暴露两个接口 进行相关配置 在vite.config.ts文件中进行配置 e…

并行算法举例

谷歌的三大马车中的两辆分别是MapReduce GFS 这些我们在后边都会学到。 补充shuffle的作用 将map中的内容&#xff0c;具备相同内容的key被放到一组 问题1 矩阵乘法 第一轮j相同放在一起。 第二轮ik相同的放在一起。 问题2 求和问题 问题3 不重复元素

大数据可视化Web框架——飞致云Dataease在Windows端的安装指南(多图说明版)V2.2最新版

DataEase开源代码在Windows系统开发环境搭建与调试指南_怎么部署dataease 2.0-CSDN博客https://blog.csdn.net/tllhc01/article/details/135220598?spm1001.2014.3001.5502参考这一篇&#xff0c;基于dataease2.2源码进行构建 需要先下载三个文件&#xff0c;且版本一一对应均…

力扣题:字符串变换-1.5

力扣题-1.5 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;482. 密钥格式化 解题思想&#xff1a;首先先将破折号去除,并将所有字母转换为大写,然后计算第一组的长度,进行结果字符串的拼接,如果第一组的长度为0,则需要删除开头的’-符号 class S…

YOLOv5独家原创改进:新颖的Shape IoU结合 Inner-IoU,基于辅助边框的IoU损失的同时关注边界框本身的形状和尺度,小目标实现高效涨点

💡💡💡本文改进:一种新的Shape IoU方法结合 Inner-IoU,基于辅助边框的IoU损失的同时,更加关注边界框本身的形状和尺度来计算损失 💡💡💡对小目标检测涨点明显,在VisDrone2019、PASCAL VOC均有涨点 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/ca…

【React系列】受控非受控组件

本文来自#React系列教程&#xff1a;https://mp.weixin.qq.com/mp/appmsgalbum?__bizMzg5MDAzNzkwNA&actiongetalbum&album_id1566025152667107329) 一. refs 的使用 在React的开发模式中&#xff0c;通常情况下不需要、也不建议直接操作DOM原生&#xff0c;但是某些…

DDIA 第十二章:数据系统的未来

本文是《数据密集型应用系统设计》&#xff08;DDIA&#xff09;的读书笔记&#xff0c;一共十二章&#xff0c;我已经全部阅读并且整理完毕。 采用一问一答的形式&#xff0c;并且用列表形式整理了原文。 笔记的内容大概是原文的 1/5 ~ 1/3&#xff0c;所以你如果没有很多时间…

2014年多线冲刺1000万

1、盯重要的科技股票等待机会低价买入&#xff08;投入不超过20万&#xff0c;全亏也认了。&#xff09;目标20倍取出即400万。3次机会达1000万就收手。 2、公司&#xff0c;找机会。1000万。 3、创业&#xff0c;经商做企业。1000万。 4、直播&#xff0c;视频&#xff0c;课程…

CAPL函数——testxxx系列

文章目录 1、TestCaseTitle-设置测试用例的标题2、TestCaseDescription -对测试用例的具体描述3、TestStep 、testStepPass、testStepFail-打印步骤、结果4、TestInfoTable、TestInfoHeadingBegin、TestInfoHeadingEnd、TestInfoRow、TestInfoCell-创建表格5、testWaitForTeste…