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
- 配置了事务的注解驱动<tx:annotation-driven transaction-manager="transactionManager" /> ,但一次都没有标注@Transactional,即一次都没有使用事务,会报错:
通配符的匹配很全面, 但无法找到元素 'tx:annotation-driven' 的声明。
Spring 事务管理的使用的更多相关文章
- 【Java EE 学习 52】【Spring学习第四天】【Spring与JDBC】【JdbcTemplate创建的三种方式】【Spring事务管理】【事务中使用dbutils则回滚失败!!!??】
一.JDBC编程特点 静态代码+动态变量=JDBC编程. 静态代码:比如所有的数据库连接池 都实现了DataSource接口,都实现了Connection接口. 动态变量:用户名.密码.连接的数据库. ...
- spring事务管理器设计思想(二)
上文见<spring事务管理器设计思想(一)> 对于第二个问题,涉及到事务的传播级别,定义如下: PROPAGATION_REQUIRED-- 如果当前没有事务,就新建一个事务.这是最常见 ...
- spring事务管理器设计思想(一)
在最近做的一个项目里面,涉及到多数据源的操作,比较特殊的是,这多个数据库的表结构完全相同,由于我们使用的ibatis框架作为持久化层,为了防止每一个数据源都配置一套规则,所以重新实现了数据源,根据线程 ...
- 事务管理(下) 配置spring事务管理的几种方式(声明式事务)
配置spring事务管理的几种方式(声明式事务) 概要: Spring对编程式事务的支持与EJB有很大的区别.不像EJB和Java事务API(Java Transaction API, JTA)耦合在 ...
- Spring事务管理器的应对
Spring抽象的DAO体系兼容多种数据访问技术,它们各有特色,各有千秋.像Hibernate是非常优秀的ORM实现方案,但对底层SQL的控制不太方便:而iBatis则通过模板化技术让你方便地控制SQ ...
- Spring事务管理(转)
1 初步理解 理解事务之前,先讲一个你日常生活中最常干的事:取钱. 比如你去ATM机取1000块钱,大体有两个步骤:首先输入密码金额,银行卡扣掉1000元钱:然后ATM出1000元钱.这两个步骤必须是 ...
- [Spring框架]Spring 事务管理基础入门总结.
前言:在之前的博客中已经说过了数据库的事务, 不过那里面更多的是说明事务的一些锁机制, 今天来说一下Spring管理事务的一些基础知识. 之前的文章: [数据库事务与锁]详解一: 彻底理解数据库事务一 ...
- Spring 事务管理 01 ——
目录: 参考: 1.Spring 事务管理高级应用难点剖析: 第 1 部分
- Spring 事务管理原理探究
此处先粘贴出Spring事务需要的配置内容: 1.Spring事务管理器的配置文件: 2.一个普通的JPA框架(此处是mybatis)的配置文件: <bean id="sqlSessi ...
- Spring 事务管理高级应用难点剖析--转
第 1 部分 http://www.ibm.com/search/csass/search/?q=%E4%BA%8B%E5%8A%A1&sn=dw&lang=zh&cc=CN& ...
随机推荐
- Oracle 11g安装 —— Oracle Database 11g Release2 for Windows(x64)
文章来自:https://blog.csdn.net/IT_xiao_guang_guang/article/details/104422421 下面是我的Oracle 11g安装过程,希望可以帮到正 ...
- 大数据-es(elasticsearch)
elasticsearch elasticsearch是lucene作为核心的实时分布式检索,底层使用倒排索引实现. 倒排索引原理 索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址.由于不 ...
- Git的基本使用 -- 远程仓库
SSH公钥 生成公钥 ssh-keygen -t rsa -C "xxx@xxx.com" 然后按三次回车 添加公钥 cat ~/.ssh/id_rsa.pub查看公钥 将生成的公 ...
- 解决VS2013报错fopen、sprintf等函数安全的问题
VS2013中使用fopen.sprintf等函数是会出现安全问题: error C4996: 'fopen': This function or variable may be unsafe. Co ...
- MVC5+EF6 入门完整教程2 :从前端UI开始
MVC分离的比较好,开发顺序没有特别要求,先开发哪一部分都可以,这次我们主要讲解前端UI的部分. ASP.NET MVC抛弃了WebForm的一些特有的习惯,例如服务器端控件,ViewState这些东 ...
- cc.Sprite 与 ccui.ImageView 改变图片
sprite.setTexture(fileName); imageView.loadTexture(fileName);
- keil密钥过期 新注册机 有效期至2032年
点击下载:keil注册机至2032年
- FastDFS :java.lang.Exception: getStoreStorage fail, errno code: 28
FastDFS 服务正常,突然报错:java.lang.Exception: getStoreStorage fail, errno code: 28 答:错误代码28表示 No space left ...
- Ubuntu安装程序报错:无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用)
问题描述:在使用Ubuntu系统时安装程序时出现下面的报错. E: 无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用)E: Unable to ...
- ABB工业机器人(条件执行数字信号判断,画方or画圆)
一.前戏 条件:从安全点,到工具区域夹取工具(笔),到工作区域,判断数字信号 Di1 =1 ,Ture :画方,False:画圆,回到工具区域放下工具(笔),回到安全点 二. 准备工作 校准tcp工具 ...