五、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)的更多相关文章

  1. Spring的事务控制-基于注解的方式

    模拟转账操作,即Jone减少500,tom增加500 如果有疑问请访问spring事务控制-基于xml方式 1.创建数据表 2.创建Account实体类 public class Account { ...

  2. 代码中添加事务控制 VS(数据库存储过程+事务) 保证数据的完整性与一致性

    做人事档案的系统考虑到数据的安全性与一致性,毕竟是要对外上线.真正投入使用的项目,数据库的可靠性与安全性上我们开发人员要考虑的就很多了,记得做机房收费系统时注册新卡是自己为了简单,写成了一个存储过程( ...

  3. Spring中的事务控制

    Spring中事务控制的API介绍 PlatformTransactionManager 此接口是spring的事务管理器,它里面提供了我们常用的操作事务的方法 我们在开发中都是使用它的实现类: 真正 ...

  4. SmartSql使用教程(3)——SmartSql中的事务,及AOP的使用

    一.引言 经过两章的铺垫,我们现在对SmartSql已经有了一定的了解,那么今天我们的主题是事务处理.事务处理是常用的一种特性,而SmartSql至少提供了两种使用事务的方法.一种是通过Reposit ...

  5. Spring的事务控制-基于xml方式

    介绍:该程序模拟了转账操作,即Jone减少500元,tom增加500元 1.导入坐标 <dependency> <groupId>junit</groupId> & ...

  6. SQL Server 中的事务与事务隔离级别以及如何理解脏读, 未提交读,不可重复读和幻读产生的过程和原因

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

  7. SQL Server中的事务与其隔离级别之脏读, 未提交读,不可重复读和幻读

    原本打算写有关 SSIS Package 中的事务控制过程的,但是发现很多基本的概念还是需要有 SQL Server 事务和事务的隔离级别做基础铺垫.所以花了点时间,把 SQL Server 数据库中 ...

  8. Django中的事务(Transaction)管理

    Django默认的事务行为 默认情况下,在Django中事务是自动提交的.当我们运行Django内置的模板修改函数时,例如调用model.save()或model.delete()时,事务将被立即提交 ...

  9. lightning mdb 源代码分析(5)-事务控制

    本博文系列前面已经探讨了LMDB的系统架构.MMAP映射.B-Tree操作等部分,本文将尝试描述LMDB中的事务控制的实现. 事务的基本特征: 事务是恢复和并发控制的基本单位.它是一个操作序列,这些操 ...

随机推荐

  1. C++学习笔记(十七):RTTI

    这个知识点被遗漏了,可以结合之前的这篇文章看类型转换这个知识点. RTTI(Run-Time Type Information,运行时类型信息)即程序能够使用基类的指针或引用来检查这些指针或引用所指的 ...

  2. 多年的.NET开发,也只学会了这么几招

    折腾了这么多年的.NET开发,也只学会了这么几招 软件开发不是生活的全部,但是好的生活全靠它了   随着工作年龄逐渐增加,身边的重担也越来越多.以前可以在公司加班到晚上10点,现在不行了.以前可以通宵 ...

  3. ECshop数据库的访问统计和管理员日志的清空

    ECshop是个不错的系统,但是它有一定漏洞,若是访问量巨大的话,大量的访问统计代码会存入数据库的ecs_stats表中,甚至几天就可以达到几百兆,严重的网站直接就崩溃了.数据备份的时候也有很多不便, ...

  4. HTML中标签和元素的区别

    作为一个前端,相信大家最先接触应该都是HTML吧?在HTML中很多人可能都没有把什么叫标签,什么叫元素这个概念搞清楚,为了把这个大家不曾留意的易混淆的搞清楚,特作此一文彻底解决掉这个问题! 首先我们来 ...

  5. spring jdbcTemplate源码剖析

    本文浅析 spring jdbcTemplate 源码,主要是学习其设计精髓.模板模式.巧妙的回调 一.jdbcTemplate 类结构 ①.JdbcOperations : 接口定义了方法,如 &l ...

  6. HDU4279(2012年天津网络赛---数论分析题)

    题目:Number 题意: 给出一个f(x),表示不大于x的正整数里,不整除x且跟x有大于1的公约数的数的个数.定义F(x),为不大于x的正整数里,满足f(x)的值为奇数的数的个数.题目就是求这个F( ...

  7. cocos2d-x 判断点击命中坐标的几种方法

    转自:http://www.cnblogs.com/jiackyan/archive/2013/04/14/3019893.html //重载 virtual bool ccTouchBegan(CC ...

  8. ILSpy反编译工具的使用

    以前一直使用reflector来查看.net类库的一些信息,不过,自2011年2月份开始,reflector就开始转向收费软件了,所以爱好免费软件的开发者们转而开发自己的反编译软件.于是ILspy就因 ...

  9. Codeforces Round #200 (Div. 1) B. Alternating Current 栈

    B. Alternating Current Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/343 ...

  10. iOS开发——swift精讲&MVC应用实战

    MVC应用实战 iOS开发中设计模式是很重要的,其中,使用最多的就是MVC模式,今天就简单介绍一下在Swift中这么使用MVC模式来实现我们想要的功能: 模型-视图-控制器(Model-View-Co ...