五、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. ulua学习笔记(二):官方资料及问题解决方案

    uLua&SimpleFramework入门视频教程网盘地址 视频教程地址 http://pan.baidu.com/s/1gd8fG4N游戏框架地址 https://github.com/j ...

  2. 网站WAF的检测

    [wafw00f]: 项目地址: https://github.com/sandrogauci/wafw00f WAFW00F是识别和指纹Web应用防火墙(WAF)产品,其工作原理是首先通过发送一个正 ...

  3. Android真机抓屏- Android Screen Monitor

    一般运行Android应用程序有两种方式一种是设置Android虚拟设备模拟器,通过Android  Virtual Manger进行管理,一种是插入USB数据线直接真机上进行调试,但是如果电脑配置比 ...

  4. 【转】linux中的sort命令

    转自:http://www.cnblogs.com/51linux/archive/2012/05/23/2515299.html sort是在Linux里非常常用的一个命令,管排序的,集中精力,五分 ...

  5. Linux性能及调优指南(翻译)之Linux内存架构

    http://blog.csdn.net/ljianhui/article/details/46734115

  6. kernel debuging

    http://blog.csdn.net/XscKernel/article/category/1276234

  7. mysql performance schema的即时诊断工具-邱伟胜

    https://github.com/noodba http://www.noodba.com

  8. Supervised Learning-Regression

    假设我们有一张房子属性及其价格之间的关系表(如下图所示) ,根据这些数据如何估计其他房子的价格?我们的第一个反应肯定是参考属性相似的房子的价格.在属性较少时这个方法还行得通,属性太复杂时就不那么简单了 ...

  9. How to center a div in bootstrap3

    There are two approaches to centering a column <div> in Bootstrap 3: Approach 1 (offsets): The ...

  10. 关于工作流之最后Assign给TeamLeader

    如果你的单子Buddy验证通过了.然后也进行了Integrate了,然后就可以把这个单子给TL(Team Leader)了. >>>>>>>>>& ...