五、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. android 数据存储操作之SQLite

    一. SQLite介绍 SQLite是android内置的一个很小的关系型数据库. 二. SQLiteOpenHelper的使用方法 ①SQLiteOpenHelper是一个辅助类来管理数据库的创建和 ...

  2. yii2 i18n学习

    举例说明常见的翻译:Yii::t('app','Login'):追踪源码:BaseYii.php 文件 ,Yii::t($category, $message, $params = [], $lang ...

  3. 总结一下ERP .NET程序员必须掌握的.NET技术

    总结一下ERP .NET程序员必须掌握的.NET技术,掌握了这些技术工作起来才得心应手   从毕业做.NET到现在,有好几年了,自认为只能是达到熟练的水平,谈不上精通.所以,总结一下,自己到底熟练掌握 ...

  4. view 与layer

      文章出处:http://blog.csdn.net/iukey 一.添加 Quartz Core 框架 要使用 Quartz Core 框架,你需要将其添加到你的工程中 . 然后  #import ...

  5. ubuntu下查看文件md5

    终端输入md5sum --help: md5sum --help用法:md5sum [选项]... [文件]...显示或检查 MD5(128-bit) 校验和.若没有文件选项,或者文件处为" ...

  6. IE下载打印文件的时候,下载打印闪一下就没有了

    这是因为我们的浏览器没有将文件下载的自动提示设为启用.点击IE菜单栏中的“工具”—“Internet选项”-安全—可信站点—自定义级别 1,添加信任站点 打开IE浏览器,输入需要下载文件的地址 选择[ ...

  7. SICP 习题 (1.14)解题总结

    SICP 习题 1.14要求计算出过程count-change的增长阶.count-change是书中1.2.2节讲解的用于计算零钱找换方案的过程. 要解答习题1.14,首先你需要理解count-ch ...

  8. 每天进步一点点——负载均衡之DNS域名解析

    转载请说明出处:http://blog.csdn.net/cywosp/article/details/38017027     在上一篇文章(http://blog.csdn.net/cywosp/ ...

  9. FactoryBean的使用--转

    一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean .在某些情况下,实例化bean过程比较复杂,如果按照传统的方式,则需要在<bean>中提供大量的 ...

  10. Asp.Net 之 缓存机制

    asp.net缓存有三种:页面缓存,数据源缓存,数据缓存. 一.页面缓存 原理:页面缓存是最常用的缓存方式,原理是用户第一次访问的时候asp.net服务器把动态生成的页面存到内存里,之后一段时间再有用 ...