spring事务管理有两种方式:一种是传统的编程式事务管理,即通过编写代码实现事务的管理,包括定义事务的开始、程序正常执行后事务提交、异常时进行事务回滚。
另一种是基于AOP技术实现的声明式事务,其主要思想是将事务管理作为一个“方面”代码单独编写,程序员只关心核心业务逻辑代码,然后通过AOP技术将事务管理的“方面”代码织入到业务类中。
下面通过一个银行转账的例子说明事务:
首先先配置好ssh框架见spring笔记(6)。
- 数据库实体类和映射文件
实体类:
package com.test.bean;
public class Account {
private int id;
private String accountNo;
private double balance;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccountNo() {
return accountNo;
}
public void setAccountNo(String accountNo) {
this.accountNo = accountNo;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
2、DAO层
package com.test.dao;
import java.util.List;
import com.test.bean.Account;
public interface AccountDAO {
// 根据账号获取账户对象
public List getAccountByAccountNo(String accountNo);
// 转账
public void transfer(Account a1, Account a2);
}
package com.test.dao.impl;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import com.test.bean.Account;
import com.test.dao.AccountDAO;
public class AccountDAOImpl extends HibernateDaoSupport implements AccountDAO {
//转账
public void transfer(Account a1, Account a2) {
this.getHibernateTemplate().update(a1);
this.getHibernateTemplate().update(a2);
}
//根据账号获取账户对象
public List getAccountByAccountNo(final String accountNo) {
// TODO Auto-generated method stub
String hql ="from Account account where account.accountNo=?";
return this.getHibernateTemplate().find(hql,accountNo);
}
}
3、service层
package com.test.service;
import java.util.List;
import com.test.bean.Account;
public interface AccountService {
// 根据账号获取账户对象
public List getAccountByAccountNo(String accountNo);
// 转账
public void transfer(Account a1, Account a2);
}
package com.test.service.impl;
import java.util.List;
import com.test.bean.Account;
import com.test.dao.AccountDAO;
import com.test.service.AccountService;
public class AccountServiceImpl implements AccountService{
//用AccountDAO接口声明对象,并添加set方法用于依赖注入
AccountDAO accountDAO;
public void setAccountDAO(AccountDAO accountDAO) {
this.accountDAO = accountDAO;
}
//转账
//使用@Transactional注解实现transfer方法的事务管理
public void transfer(Account a1, Account a2) {
accountDAO.transfer(a1, a2);
}
//根据账号获取账户对象
public List getAccountByAccountNo(String accountNo) {
// TODO Auto-generated method stub
return accountDAO.getAccountByAccountNo(accountNo);
}
}
4、action层
package com.test.action.account;
import java.util.List;
import com.opensymphony.xwork2.ActionSupport;
import com.test.bean.Account;
import com.test.service.AccountService;
public class AccountManagerAction extends ActionSupport {
// 定义属性,用于封装表单数据
private String ac1;
private String ac2;
private String amount;
public String getAc1() {
return ac1;
}
public void setAc1(String ac1) {
this.ac1 = ac1;
}
public String getAc2() {
return ac2;
}
public void setAc2(String ac2) {
this.ac2 = ac2;
}
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
// 使用AccountBiz接口声明对象,并添加set方法用于依赖注入
private AccountService service;
public void setAccountService(AccountService service) {
this.service = service;
}
@Override
// 执行请求
public String execute() throws Exception {
System.out.println(ac1);
System.out.println(ac2);
Account a1 = null;
Account a2 = null;
// 获取账号ac1的账户对象,并更新对象中的账户余额属性
List list1 = service.getAccountByAccountNo(ac1);
if (list1.size() > 0) {
a1 = (Account) list1.get(0);
System.out.println("ac1");
a1.setBalance((a1.getBalance() - Double.parseDouble(amount)));
}
// 获取账号ac2的账户对象,并更新对象中的账户余额属性
List list2 = service.getAccountByAccountNo(ac2);
if (list2.size() > 0) {
a2 = (Account) list2.get(0);
System.out.println("ac2");
a2.setBalance((a2.getBalance() + Double.parseDouble(amount)));
}
try {
// 执行转账操作
service.transfer(a1, a2);
System.out.println("transfer");
} catch (Exception e) {
// 转账失败
System.out.println(e);
return "error1";
}
// 转账成功
return "success1";
}
}
5、spring配置文件applicationContext.xml中配置相关类
<!--
定义AccountDAOImpl类实例,并将已经创建LocalSessionFactoryBean的实例
sessionFactory依赖注入到UserDAOImpl类所继承的 HibernateDaoSupport
的sessionFactory属性
-->
<bean id="accountDAO" class="com.test.dao.impl.AccountDAOImpl">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="accountService" class="com.test.service.impl.AccountServiceImpl">
<property name="accountDAO" ref="accountDAO" />
</bean>
<!--
部署Struts 2的负责账户管理的控制器AccountManager
-->
<bean id="amAction" class="com.test.action.account.AccountManagerAction">
<property name="accountService" ref="accountService" />
</bean>
6、struts.xml配置
<action name="doTransfer" class="amAction">
<!-- 定义处理结果和资源之间的映射关系 -->
<result name="error1">error1.jsp</result>
<result name="success1">success1.jsp</result>
</action>
7、配置声明事务
(1)、在spring配置文件中增加事务管理所需的常用命名空间声明
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
(2)、声明事务管理器,在声明事务管理器时需要注入sessionFactory属性。定义事务通知,定义事务通知时需要指定一个事务管理器,然后在其属性中声明事务规则。
<!-- 声明事务管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- 定义事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<!-- 指定事务传播规则 -->
<tx:attributes>
<!-- 对get打头的方法应用SUPPORTS事务规则 -->
<tx:method name="get*" propagation="SUPPORTS" />
<!-- 对其他方法应用REQUIRED事务规则 -->
<tx:method name="*" propagation="REQUIRED"></tx:method>
</tx:attributes>
</tx:advice>
<!-- 定义切面,并将事务通知和切面组合(定义哪些方法应用事务规则) -->
<aop:config>
<aop:pointcut id="serviceMethods" expression="execution(* com.test.service.*.*(..))" />
<!-- 将事务通知和切面组合 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" />
</aop:config>
8、编写测试页面
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>转账页面</title>
</head>
<body>
<s:form action="doTransfer.action">
<s:textfield name="ac1" label="账户1" />
<s:textfield name="ac2" label="账户2" />
<s:textfield name="amount" label="转账金额" />
<s:submit value="转账" />
</s:form>
</body>
</html>
第一次测试:
数据库:
第二次测试:
数据库:
账户输入错误,两个账户余额均不变化。
第三次测试:
将spring配置文件中一下代码删去,及删除声明式事务。重启工程后。
在执行第二次的操作: