五、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. Lambda表达式的由来

    1.lambada表达式的本质:一个匿名方法,或说是匿名委托.从C#3.0开始支持,C#2.0只支持匿名方法语法很简单 : (输入参数)=>expr   //当参数为一个是可以省略括号.lamb ...

  2. PowerDesigner实用技巧小结(4)

    下述十四个技巧,是许多人在大量的数据库分析与设计实践中,逐步总结出来的.对于这些经验的运用,读者不能生帮硬套,死记硬背,而要消化理解,实事求是,灵活掌握.并逐步做到:在应用中发展,在发展中应用. 1. ...

  3. 拼接json示例 json分页并显示所有页码

    <%@ page language="java" import="java.util.*" pageEncoding="gbk"%&g ...

  4. JSon实体类快速生成插件 GsonFormat 1.2.0

    写在前头:本插件只适用 android studio和 Intellij IDEA 工具,eclipse 的少年无视我吧!!! 这是一个根据JSONObject格式的字符串,自动生成实体类参数. gi ...

  5. Nape刚体body.align();

    (转载http://tomyail.com/blog/1065) Body的类型: BodyType.DYNAMIC(默认):用来模拟现实世界的刚体,拥有质量并且一旦加入Nape的空间(Space)里 ...

  6. 7 种流行 PHP IDE 的比较

    编写关于 PHP 的系列文章让我更加深刻地了解了 PHP 开发人员的世界.我和许多 PHP 程序员交谈过,最令我惊奇的是只有很少的人使用 IDE.大多数程序员使用文本编辑器,比如 Microsoft® ...

  7. 谈使用Eclipse与DDMS调试Android程序的方法

    在Eclipse开发工具中调试程序的方法很多,但是使用Eclipse调试Android程序时需要注意一些细节上的问题.许多刚接触 Android的开发者,在调试Android程序时总是不能迅速地找到程 ...

  8. (收藏)sokcet编程

    C# Socket网络编程精华篇 C#编写高性能网络服务器(源码) .net自动更新组件Ant 客户端服务器通信demo(附源码) 有了WCF,Socket是否已人老珠黄? Socket服务器整体架构 ...

  9. jQuery选择器模糊匹配

    <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8&quo ...

  10. 使用apache common-io 监控文件变化--转

    package common.io; import org.apache.commons.io.filefilter.FileFilterUtils; import org.apache.common ...