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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://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 {// 注入daoprivate 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 {//注入测试对象@Autowiredprivate IAccountService accountService;@Testpublic void testTransfer() {accountService.transfer("tom", "jack", 1000d);}}
但是发现问题:
事务管理问题:在Service层没有事务的情况下,如果出现异常,则会转账不成功,数据异常。
在转账方法中添加如下异常:
运行前:
运行后:
事务未生效。
注意:如果不配置事务,那么每一个数据库的操作都是单独的一个事务。
2 XML配置方式添加事务管理(tx、aop元素)
【操作思路】:aop三步走
- 确定目标:需要对AccountService 的 transfer方法,配置切入点
- 需要Advice (环绕通知),方法前开启事务,方法后提交关闭事务
- 配置切面和切入点
配置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
步骤:
- 在需要管理事务的方法或者类上面 添加@Transactional 注解
- 配置注解驱动事务管理(事务管理注解生效的作用)(需要配置对特定持久层框架使用的事务管理器)
创建项目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@Autowiredpublic 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@Autowiredprivate 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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://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@Autowiredprivate IAccountDao accountDao;// 转账业务@Transactionalpublic 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 stubreturn null;}}
4 小结-xml和注解的选择
XML配置方式和注解配置方式进行事务管理 哪种用的多?
XML方式,集中式维护,统一放置到applicationContext.xml文件中,缺点在于配置文件中的内容太多。
使用@Transactional注解进行事务管理,配置太分散,使用XML进行事务管理,属性集中配置,便于管理和维护
注意:以后的service的方法名字的命名,必须是上面规则,否则,不能被spring事务管理。!!!!
即以save开头的方法,update开头的方法,delete开头的方法,表示增删改的操作,故事务为可写
以find开头的方法,表示查询,故事务为只读
相关文章:

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,m…...

NACHI机器人模拟示教器如何切换中文
前言 现在开始学习机器人的编程语言,那么要学习会用首先得用模拟示教器来学习,但是全是英文确实比较难受一些些,没有中文来的直观。所以摸透一下如何给示教器更换语言。 具体步骤 步骤一:将中文的汉化包下载下来。具体的下载链…...

用通俗易懂的方式讲解:使用 Mistral-7B 和 Langchain 搭建基于PDF文件的聊天机器人
在本文中,使用LangChain、HuggingFaceEmbeddings和HuggingFace的Mistral-7B LLM创建一个简单的Python程序,可以从任何pdf文件中回答问题。 一、LangChain简介 LangChain是一个在语言模型之上开发上下文感知应用程序的框架。LangChain使用带prompt和few…...

综合智慧能源监测管理平台,实现能源管理“透明”化
能源问题是全球面临的最大问题,在提高经济增长的同时,也引发了能源供应危机及环境严重等问题,降低能源管理、低碳环保是我们未来发展的必经之路。 为了解决这一问题,智慧能源管理平台应运而生。平台采用微服务架构,整…...

【大数据进阶第三阶段之Datax学习笔记】使用阿里云开源离线同步工具DataX 实现数据同步
【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax概述 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax类图 【大数据进阶第三阶段之Datax学习笔记】使用…...
kotlin chunked 和 windowed
kotlin chunked的作用 将集合按照指定的数量分割成多个结合 val numbers listOf(0,1,2,3,4,5,6,7,8,9) //把集合按照一个结合3个元素分割 Log.d("chunked", numbers.chunked(3).toString()) // 打印结果 [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]] kotlin windowed…...

C语言光速入门笔记
C语言是一门面向过程的编译型语言,它的运行速度极快,仅次于汇编语言。C语言是计算机产业的核心语言,操作系统、硬件驱动、关键组件、数据库等都离不开C语言;不学习C语言,就不能了解计算机底层。 目录 C语言介绍C语言特…...

Flutter+Go_Router+Fluent_Ui仿阿里网盘桌面软件开发跨平台实战-买就送仿小米app开发
Flutter是谷歌公司开发的一款开源、免费的UI框架,可以让我们快速的在Android和iOS上构建高质量App。它最大的特点就是跨平台、以及高性能。 目前 Flutter 已经支持 iOS、Android、Web、Windows、macOS、Linux 的跨平台开发。 Flutter官方介绍,目前Flutte…...

内联函数的作用
目的 主要为了提升程序运行速度。 分析 当程序调用一个函数时,程序暂停执行当前指令,跳到函数体处执行,在函数执行完后,返回原来的位置继续执行。如果该函数为内联函数,则不需跳,是因为该内联函数直接插…...

Simpy简介:python仿真模拟库-02/5
一、说明 关于python下的仿真库,本篇为第二部分,是更进一步的物理模型讲解,由于这部分内容强依赖于第一部分的符号介绍,因此,有以下建议: 此文为第二部分,若看第一部分。建议查看本系列的第一部…...
Kafka高级应用:如何配置处理MQ百万级消息队列?
在大数据时代,Apache Kafka作为一款高性能的分布式消息队列系统,广泛应用于处理大规模数据流。本文将深入探讨在Kafka环境中处理百万级消息队列的高级应用技巧。 本文,已收录于,我的技术网站 ddkk.com,有大厂完整面经…...

LIN总线学习笔记(1)-总线传输规范
关注菲益科公众号—>对话窗口发送 “CANoe ”或“INCA”,即可获得canoe入门到精通电子书和INCA软件安装包(不带授权码)下载地址。 接触LIN是从最近负责项目中开始的。项目已经快要量产了,因为中间遇到的大大小小的问题…...

Qt界面篇:Qt停靠控件QDockWidget、树控件QTreeWidget及属性控件QtTreePropertyBrowser的使用
1、功能介绍 本篇主要使用Qt停靠控件QDockWidget、树控件QTreeWidget及Qt属性控件QtTreePropertyBrowser来搭建一个简单实用的主界面布局。效果如下所示。 2、控件使用详解 2.1 停靠控件QDockWidget QDockWidget可以停靠在 QMainWindow 内或作为桌面上的顶级窗口浮动。默认值…...

H266/VVC网络适配层概述
视频编码标准的分层结构 视频数据分层的必要性:网络类型的多样性、不同的应用场景对视频有不同的需求。 编码标准的分层结构:为了适应不同网络和应用需求,视频编码数据根据其内容特性被分成若干NAL单元(NAL Unit,NALU…...

new FormData 同时发送表单 json 以及文件二进制流
需要新增时同时发送表单 json 以及对应的文件即可使用以下方法传参 let formDataParams new FormData(); 首先通过 new FormData() 创建你需要最后发送的表单 接着将你的对象 json 存储,注意使用 new Blob 创建大表单转换成 json 格式。以…...

计算机环境安全
操作系统安全----比如windows,linux 安全标识--实体唯一性 windows---主体:账户,计算机,服务 安全标识符SID-Security Identifier 普通用户SID是1000,管理用SID是500 linux---主体:用户,用户组…...
Activiti7工作流引擎:多租户
一:多租户 表示每个租户之间数据隔离互不影响,互不可见。通常一个租户表示一个系统应用(类似于appid的作用)或者一家公司。 通过数据库级别进行隔离,每个租户对应一个数据库;通过表记录级别进行隔离&…...

Postman实现压力测试
从事软件开发对于压力测试并不陌生,常见的一些压测软件有Apache JMeter LoadRunner Gatling Tsung 等,这些都是一些比较专业的测试软件,对于我的工作来说一般情况下用不到这么专业的测试,有时候需要对一些接口进行压力测试又不想再安装新软件,那么可以使用Postman来实现对…...

爬虫工具(tkinter+scrapy+pyinstaller)
需求介绍输入:关键字文件,每一行数据为一爬取单元。若一行存在多个and关系的关键字 ,则用|隔开处理:爬取访问6个网站的推送,获取推送内容的标题,发布时间,来源,正文第一段࿰…...
MySQL常用sql语句记录
1,创建用户及赋权 -- 创建用户 CREATE USER usernamelocalhost IDENTIFIED BY password;-- 赋予所有权限 GRANT ALL PRIVILEGES ON database_name.* TO usernamelocalhost;-- 赋予特定表的某些权限 GRANT SELECT, INSERT ON table_name TO usernamelocalhost;-- 更…...

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?
编辑:陈萍萍的公主一点人工一点智能 未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战,在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)
HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...
React Native 导航系统实战(React Navigation)
导航系统实战(React Navigation) React Navigation 是 React Native 应用中最常用的导航库之一,它提供了多种导航模式,如堆栈导航(Stack Navigator)、标签导航(Tab Navigator)和抽屉…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...

苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...

ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

如何在最短时间内提升打ctf(web)的水平?
刚刚刷完2遍 bugku 的 web 题,前来答题。 每个人对刷题理解是不同,有的人是看了writeup就等于刷了,有的人是收藏了writeup就等于刷了,有的人是跟着writeup做了一遍就等于刷了,还有的人是独立思考做了一遍就等于刷了。…...