Java实战之03Spring-05Spring中的事务控制(基于AOP)
五、Spring中的事务控制(基于AOP)
1、Spring中事务有关的接口
1.1、明确:
JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案
1.2、Spring事务管理主要包括3个接口
1.2.1、PlatformTransactionManager事务管理器
具体实现类:
1.2.2、TransactionDefinition事务定义信息
事务的隔离级别:
事务的传播行为:有时面试会问到
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)
MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常
REQUERS_NEW:新建事务,如果当前在事务中,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起
NEVER:以非事务方式运行,如果当前存在事务,抛出异常
NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。
超时时间:默认值是-1,没有超时限制。如果有,以秒为单位进行设置。
是否是只读事务:建议查询时设置为只读。
1.2.3、TransactionStatus事务具体运行状态
2、Spring中的事务控制
准备环境:
1.使用我们之前转账的案例。
2.使用Spring容器来管理业务层和持久层对象的注入
public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao ; public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
} public void transfer(String sourceName, String targetName, Float money) {
//1.根据名称获取账户信息
Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
//2.设置账户的金额
sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
//3.更新账户信息
accountDao.updateAccount(sourceAccount);
//手动模拟一个异常
int i=1/0;
accountDao.updateAccount(targetAccount);
} public Account findAccountById(Integer id) {
return accountDao.findAccountById(id);
} }
public interface IAccountService { /**
* 转账方法
* @param sourceAccountName 转出账户名称
* @param targetAccountName 转入账户名称
* @param money 转账金额
*/
void transfer(String sourceAccountName,String targetAccountName,Float money); /**
* 根据id获取账户信息
* @param id
* @return
*/
Account findAccountById(Integer id);
}
3.使用Spring的jdbc模板(JdbcDaoSupport)来编写持久层。
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao { public Account findAccountByName(String accountName) {
List<Account> list = getJdbcTemplate().query("select * from account where name = ?", new AccountRowMapper(),accountName);
if(list.size() == 1){
return list.get(0);
}
return null;
} public void updateAccount(Account account) {
getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
} public Account findAccountById(Integer id) {
List<Account> list = getJdbcTemplate().query("select * from account where id = ?", new AccountRowMapper(),id);
if(list.size() == 1){
return list.get(0);
}
return null;
}
}
public class AccountRowMapper implements RowMapper<Account> { public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}
import cn.itcast.domain.Account; /**
* 账户的持久层接口
* @author zhy
*
*/
public interface IAccountDao { /**
* 根据账户名称,查询账户信息
* @param accountName
* @return
*/
Account findAccountByName(String accountName); /**
* 更新账户信息
* @param account
*/
void updateAccount(Account account); /**
* 根据id查询账户信息
* @param id
* @return
*/
Account findAccountById(Integer id);
}
4.使用Spring的内置数据源。
<!-- 配置数据源 -->
<bean id="driverManagerDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ee0413_spring_day37"></property>
<property name="username" value="root"></property>
<property name="password" value="1234"></property>
</bean>
2.1、编程式事务控制(了解)
public class AccountServiceImpl implements IAccountService { private IAccountDao accountDao ;
//定义一个事务模板
private TransactionTemplate transactionTemplate; public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
this.transactionTemplate = transactionTemplate;
} public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
} public void transfer(final String sourceName,final String targetName,final Float money) {
//定义一个TransactionCallback
TransactionCallback action = new TransactionCallback<Account>() {
//此方法内部,写需要事务控制的代码
public Account doInTransaction(TransactionStatus status) {
//1.根据名称获取账户信息
Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
//2.设置账户的金额
sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
//3.更新账户信息
accountDao.updateAccount(sourceAccount);
//手动模拟一个异常
int i=1/0;
accountDao.updateAccount(targetAccount);
return null;
}
};
//事务控制的方法
transactionTemplate.execute(action);
} public Account findAccountById(final Integer id) {
TransactionCallback action = new TransactionCallback<Account>() {
//此方法内部,写需要事务控制的代码
public Account doInTransaction(TransactionStatus status) {
return accountDao.findAccountById(id);
}
};
//事务控制的方法
return transactionTemplate.execute(action);
}
}
弊端:我们发现,其实就是编写了一个环绕通知,但是当我们业务代码很多时,每个都得向这样编写,很繁琐。
<!-- 把业务层的创建交给spring容器 -->
<bean id="accountService" class="cn.itcast.service.impl.AccountServiceImpl">
<property name="accountDao" ref="accountDao"></property>
<!-- 注入事务管理模板 -->
<property name="transactionTemplate" ref="transactionTemplate"></property>
</bean> <!-- 配置一个事务模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<!-- 注入事务管理器 -->
<property name="transactionManager" ref="transactionManager"></property>
</bean> <!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="driverManagerDataSource"></property>
</bean>
2.2、声明式事务控制(重点)
编程式事务管理将数据层提交事务的代码加入到逻辑层,与Spring无侵入式编程的主思想有冲突,实际开发过程中,往往采用声明式事务管理形式
通过编程式事务管理的代码不难看出,在业务逻辑层对应的业务上添加某些代码即可完成整体事务管理的操作,使用SpringAOP的思想,将公共的代码加入后,即可完成对应的工作,这就是声明式事务管理的核心机制。
2.2.1、基于XML的声明式事务配置
a、导入spring事务支持的jar包
b、引入spring事务的名称空间
c、配置spring的事务支持
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="driverManagerDataSource"></property>
</bean>
<!-- 配置通知,通知中要引用事务管理器。因为事务管理器中有公共的代码:提交事务和回滚事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 事务的属性设置 -->
<tx:attributes>
<!-- 指定需要事务的方法
常用属性:
isolation:指定事务的隔离级别
propagation:指定事务的传播行为
read-only:是否是只读事务
timeout:事务的超时时间
no-rollback-for:当产生指定异常时,不回滚。其他异常回滚。没有默认值
rollback-for:当产生指定异常时回滚,产生其他异常时,不回滚。没有默认值 -->
<!-- 查询方法 -->
<tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
<tx:method name="*" read-only="false" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 指定通知和切入点表达式 -->
<aop:config>
<aop:advisor advice-ref="txAdvice" pointcut="execution(* cn.itcast.service.impl.*.*(..))"/>
</aop:config>
2.2.2、基于注解的声明式事务配置
a、把spring容器管理改为注解配置的方式
<!-- 配置一个JdbcTemplate的bean -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="driverManagerDataSource"></property>
</bean>
public class AccountDaoImpl implements IAccountDao { @Autowired
private JdbcTemplate jdbcTemplate; public Account findAccountByName(String accountName) {
List<Account> list = jdbcTemplate.query("select * from account where name = ?", new AccountRowMapper(),accountName);
if(list.size() == 1){
return list.get(0);
}
return null;
} public void updateAccount(Account account) {
jdbcTemplate.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
} public Account findAccountById(Integer id) {
List<Account> list = jdbcTemplate.query("select * from account where id = ?", new AccountRowMapper(),id);
if(list.size() == 1){
return list.get(0);
}
return null;
}
}
@Component("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService { @Autowired
private IAccountDao accountDao ; public void transfer(final String sourceName,final String targetName,final Float money) {
//1.根据名称获取账户信息
Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
//2.设置账户的金额
sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
//3.更新账户信息
accountDao.updateAccount(sourceAccount);
//手动模拟一个异常
int i=1/0;
accountDao.updateAccount(targetAccount);
} @Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public Account findAccountById(final Integer id) {
return accountDao.findAccountById(id);
} }
b、开启注解事务的支持
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源 -->
<property name="dataSource" ref="driverManagerDataSource"></property>
</bean>
c、在需要事务支持的地方加入@Transactional注解
@Component("accountService")
public class AccountServiceImpl implements IAccountService { @Autowired
private IAccountDao accountDao ; @Transactional
public void transfer(final String sourceName,final String targetName,final Float money) {
//1.根据名称获取账户信息
Account sourceAccount = accountDao.findAccountByName(sourceName);//转出账户
Account targetAccount = accountDao.findAccountByName(targetName);//转入账户
//2.设置账户的金额
sourceAccount.setMoney(sourceAccount.getMoney()-money);//转出账户减钱
targetAccount.setMoney(targetAccount.getMoney()+money);//转入账户加钱
//3.更新账户信息
accountDao.updateAccount(sourceAccount);
//手动模拟一个异常
int i=1/0;
accountDao.updateAccount(targetAccount);
} @Transactional(readOnly=true,propagation=Propagation.SUPPORTS)
public Account findAccountById(final Integer id) {
return accountDao.findAccountById(id);
} }
d、@Transactional注解配置的位置
l 用在业务实现类上:该类所有的方法都在事务的控制范围
l 用在业务接口上:该接口的所有实现类都起作用
l 通过注解指定事务的定义信息
Java实战之03Spring-05Spring中的事务控制(基于AOP)的更多相关文章
- Spring的事务控制-基于注解的方式
模拟转账操作,即Jone减少500,tom增加500 如果有疑问请访问spring事务控制-基于xml方式 1.创建数据表 2.创建Account实体类 public class Account { ...
- 代码中添加事务控制 VS(数据库存储过程+事务) 保证数据的完整性与一致性
做人事档案的系统考虑到数据的安全性与一致性,毕竟是要对外上线.真正投入使用的项目,数据库的可靠性与安全性上我们开发人员要考虑的就很多了,记得做机房收费系统时注册新卡是自己为了简单,写成了一个存储过程( ...
- Spring中的事务控制
Spring中事务控制的API介绍 PlatformTransactionManager 此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法 我们在开发中都是使用它的实现类: 真正 ...
- SmartSql使用教程(3)——SmartSql中的事务,及AOP的使用
一.引言 经过两章的铺垫,我们现在对SmartSql已经有了一定的了解,那么今天我们的主题是事务处理.事务处理是常用的一种特性,而SmartSql至少提供了两种使用事务的方法.一种是通过Reposit ...
- Spring的事务控制-基于xml方式
介绍:该程序模拟了转账操作,即Jone减少500元,tom增加500元 1.导入坐标 <dependency> <groupId>junit</groupId> & ...
- SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- SQL Server中的事务与其隔离级别之脏读, 未提交读,不可重复读和幻读
原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...
- Django中的事务(Transaction)管理
Django默认的事务行为 默认情况下,在Django中事务是自动提交的.当我们运行Django内置的模板修改函数时,例如调用model.save()或model.delete()时,事务将被立即提交 ...
- lightning mdb 源代码分析(5)-事务控制
本博文系列前面已经探讨了LMDB的系统架构.MMAP映射.B-Tree操作等部分,本文将尝试描述LMDB中的事务控制的实现. 事务的基本特征: 事务是恢复和并发控制的基本单位.它是一个操作序列,这些操 ...
随机推荐
- angular ui-route
http://www.cnblogs.com/freeliver54/p/4488141.html
- C# Winform 支持Hex与ASCII输入和切换的文本框
最近一直在做一个支持串口,TCP,UDP通讯调试的一体化工具(也就是C#串口调试工具 v2.0的第三版),其中涉及到16进制数据和ASCII码的输入,所以继承了TextBox的基础上,写了这个支持He ...
- Redis实战
大约一年多前,公司同事开始使用Redis,不清楚是配置,还是版本的问题,当时的Redis经常在使用一段时间后,连接爆满且不释放.印象中,Redis 2.4.8以下的版本由于设计上的主从库同步问题,就会 ...
- AS问题解决系列3—iCCP: Not recognizing known sRGB profile
http://my.oschina.net/1pei/blog/479162 摘要 本文解决了Android Studio 1.2.2下编译期间出现的libpng warning: iCCP: Not ...
- php empty()和isset()的区别<转载>
在使用 php 编写页面程序时,我经常使用变量处理函数判断 php 页面尾部参数的某个变量值是否为空,开始的时候我习惯了使用 empty() 函数,却发现了一些问题,因此改用 isset() 函数,问 ...
- iOS开发——UI篇OC篇&UIDynamic详解
iOS开发拓展篇—UIDynamic(简单介绍) 一.简单介绍 1.什么是UIDynamic UIDynamic是从iOS 7开始引入的一种新技术,隶属于UIKit框架 可以认为是一种物理引擎,能模拟 ...
- iOS7 设置隐藏状态栏(status bar)
在info.plist 添加 UIViewControllerBasedStatusBarAppearance(View controller-based status bar appearance) ...
- ADO.Net 之 数据库连接池(一)
1. 什么是连接池? 我们都知道,建立一个数据库连接是一件非常耗时(消耗时间)耗力(消耗资源)的事情.之所以会这样,是因为连接到数据库服务器需要经历几个漫长的过程:建立物理通道(例如套接字或命名管道) ...
- 常用CentOS 6/7 扩展源
1.系统自带 baseextrasupdates 2.epel yum install epel-release https://fedoraproject.org/wiki/EPEL 3.el(用于 ...
- 关于Comparator和Comparable
1.Comparable 2.Comparator >>>>>> Comparable & Comparator 都是用来实现集合中元素的比较.排序的,只 ...