sping编程式事务和声明式事务

1 编程式事务

1.1 编程式和声明式事务的区别

Spring提供了对编程式事务和声明式事务的支持,编程式事务允许用户在代码中精确定义事务的边界,而声明式事务(基于AOP)有助于用户将操作与事务规则进行解耦。简单地说,编程式事务侵入到了业务代码里面,但是提供了更加详细的事务管理;而声明式事务由于基于AOP,所以既能起到事务管理的作用,又可以不影响业务代码的具体实现。

1.2 如何实现编程式事务?

Spring提供两种方式的编程式事务管理,分别是:使用TransactionTemplate和直接使用PlatformTransactionManager。

1.2.1 使用TransactionTemplate

采用TransactionTemplate和采用其他Spring模板,如JdbcTempalte和HibernateTemplate是一样的方法。它使用回调方法,把应用程序从处理取得和释放资源中解脱出来。如同其他模板,TransactionTemplate是线程安全的。代码片段:

TransactionTemplate tt = new TransactionTemplate(); // 新建一个TransactionTemplate
Object result = tt.execute(
    new TransactionCallback(){  
        public Object doTransaction(TransactionStatus status){  
            updateOperation();  
            return resultOfUpdateOperation();  
        }  
}); // 执行execute方法进行事务管理

使用TransactionCallback()可以返回一个值。如果使用TransactionCallbackWithoutResult则没有返回值。

1.2.2 使用PlatformTransactionManager

示例代码如下:

DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); //定义一个某个框架平台的TransactionManager,如JDBC、Hibernate
dataSourceTransactionManager.setDataSource(this.getJdbcTemplate().getDataSource()); // 设置数据源
DefaultTransactionDefinition transDef = new DefaultTransactionDefinition(); // 定义事务属性
transDef.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); // 设置传播行为属性
TransactionStatus status = dataSourceTransactionManager.getTransaction(transDef); // 获得事务状态
try {
    // 数据库操作
    dataSourceTransactionManager.commit(status);// 提交
} catch (Exception e) {
    dataSourceTransactionManager.rollback(status);// 回滚
}

2 声明式事务

2.1 配置方式

注:以下配置代码参考自Spring事务配置的五种方式

根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:

(1)每个Bean都有一个代理

<?xml version="1.0" encoding="UTF-8"?>

<bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 

<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDao" 
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> 
       <!-- 配置事务管理器 --> 
       <property name="transactionManager" ref="transactionManager" />    
    <property name="target" ref="userDaoTarget" /> 
     <property name="proxyInterfaces" value="com.bluesky.spring.dao.GeneratorDao" />
    <!-- 配置事务属性 --> 
    <property name="transactionAttributes"> 
        <props> 
            <prop key="*">PROPAGATION_REQUIRED</prop>
        </props> 
    </property> 
</bean> 
(2)所有Bean共享一个代理基类 <?xml version="1.0" encoding="UTF-8"?>

<bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 

<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="transactionBase" 
        class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" 
        lazy-init="true" abstract="true"> 
    <!-- 配置事务管理器 --> 
    <property name="transactionManager" ref="transactionManager" /> 
    <!-- 配置事务属性 --> 
    <property name="transactionAttributes"> 
        <props> 
            <prop key="*">PROPAGATION_REQUIRED</prop> 
        </props> 
    </property> 
</bean>   

<!-- 配置DAO -->
<bean id="userDaoTarget" class="com.bluesky.spring.dao.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<bean id="userDao" parent="transactionBase" > 
    <property name="target" ref="userDaoTarget" />  
</bean>
(3)使用拦截器 <?xml version="1.0" encoding="UTF-8"?>

<bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 

<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean> 

<bean id="transactionInterceptor" 
    class="org.springframework.transaction.interceptor.TransactionInterceptor"> 
    <property name="transactionManager" ref="transactionManager" /> 
    <!-- 配置事务属性 --> 
    <property name="transactionAttributes"> 
        <props> 
            <prop key="*">PROPAGATION_REQUIRED</prop> 
        </props> 
    </property> 
</bean>

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> 
    <property name="beanNames"> 
        <list> 
            <value>*Dao</value>
        </list> 
    </property> 
    <property name="interceptorNames"> 
        <list> 
            <value>transactionInterceptor</value> 
        </list> 
    </property> 
</bean> 

<!-- 配置DAO -->
<bean id="userDao" class="com.bluesky.spring.dao.UserDaoImpl">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
(4)使用tx标签配置的拦截器 <?xml version="1.0" encoding="UTF-8"?>

<context:annotation-config />
<context:component-scan base-package="com.bluesky" />

<bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 

<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="*" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut id="interceptorPointCuts"
        expression="execution(* com.bluesky.spring.dao.*.*(..))" />
    <aop:advisor advice-ref="txAdvice"
        pointcut-ref="interceptorPointCuts" />       
</aop:config>     
(5)全注解 <?xml version="1.0" encoding="UTF-8"?>

<context:annotation-config />
<context:component-scan base-package="com.bluesky" />

<tx:annotation-driven transaction-manager="transactionManager"/>

<bean id="sessionFactory" 
        class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
    <property name="configLocation" value="classpath:hibernate.cfg.xml" /> 
    <property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean> 

<!-- 定义事务管理器(声明式的事务) --> 
<bean id="transactionManager"
    class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>
此时在DAO上需加上@Transactional注解,如下:

package com.bluesky.spring.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Component;

import com.bluesky.spring.domain.User;

@Transactional
@Component(“userDao”)
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {

public List<User> listUsers() {
    return this.getSession().createQuery("from User").list();
}  

}
4.2 一个声明式事务的实例
注:该实例参考自Spring中的事务管理实例详解

首先是数据库表
book(isbn, book_name, price)
account(username, balance)
book_stock(isbn, stock)

然后是XML配置

<import resource="applicationContext-db.xml" />

<context:component-scan
    base-package="com.springinaction.transaction">
</context:component-scan>

<tx:annotation-driven transaction-manager="txManager"/>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>
使用的类 BookShopDao

package com.springinaction.transaction;

public interface BookShopDao {
// 根据书号获取书的单价
public int findBookPriceByIsbn(String isbn);
// 更新书的库存,使书号对应的库存-1
public void updateBookStock(String isbn);
// 更新用户的账户余额:account的balance-price
public void updateUserAccount(String username, int price);
}
BookShopDaoImpl

package com.springinaction.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository(“bookShopDao”)
public class BookShopDaoImpl implements BookShopDao {

@Autowired
private JdbcTemplate JdbcTemplate;

@Override
public int findBookPriceByIsbn(String isbn) {
    String sql = "SELECT price FROM book WHERE isbn = ?";

    return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}

@Override
public void updateBookStock(String isbn) {
    //检查书的库存是否足够,若不够,则抛出异常
    String sql2 = "SELECT stock FROM book_stock WHERE isbn = ?";
    int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
    if (stock == 0) {
        throw new BookStockException("库存不足!");
    }
    String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn = ?";
    JdbcTemplate.update(sql, isbn);
}

@Override
public void updateUserAccount(String username, int price) {
    //检查余额是否不足,若不足,则抛出异常
    String sql2 = "SELECT balance FROM account WHERE username = ?";
    int balance = JdbcTemplate.queryForObject(sql2, Integer.class, username);
    if (balance < price) {
        throw new UserAccountException("余额不足!");
    }       
    String sql = "UPDATE account SET balance = balance - ? WHERE username = ?";
    JdbcTemplate.update(sql, price, username);
}

}
BookShopService

package com.springinaction.transaction;
public interface BookShopService {
public void purchase(String username, String isbn);
}
BookShopServiceImpl

package com.springinaction.transaction;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service(“bookShopService”)
public class BookShopServiceImpl implements BookShopService {

@Autowired
private BookShopDao bookShopDao;

/**
 * 1.添加事务注解
 * 使用propagation 指定事务的传播行为,即当前的事务方法被另外一个事务方法调用时如何使用事务。
 * 默认取值为REQUIRED,即使用调用方法的事务
 * REQUIRES_NEW:使用自己的事务,调用的事务方法的事务被挂起。
 *
 * 2.使用isolation 指定事务的隔离级别,最常用的取值为READ_COMMITTED
 * 3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚,也可以通过对应的属性进行设置。通常情况下,默认值即可。
 * 4.使用readOnly 指定事务是否为只读。 表示这个事务只读取数据但不更新数据,这样可以帮助数据库引擎优化事务。若真的是一个只读取数据库值得方法,应设置readOnly=true
 * 5.使用timeOut 指定强制回滚之前事务可以占用的时间。
 */
@Transactional(propagation=Propagation.REQUIRES_NEW,
        isolation=Isolation.READ_COMMITTED,
        noRollbackFor={UserAccountException.class},
        readOnly=true, timeout=3)
@Override
public void purchase(String username, String isbn) {
    //1.获取书的单价
    int price = bookShopDao.findBookPriceByIsbn(isbn);
    //2.更新书的库存
    bookShopDao.updateBookStock(isbn);
    //3.更新用户余额
    bookShopDao.updateUserAccount(username, price);
}

}
Cashier

package com.springinaction.transaction;
import java.util.List;
public interface Cashier {
public void checkout(String username, Listisbns);
}
CashierImpl:CashierImpl.checkout和bookShopService.purchase联合测试了事务的传播行为

package com.springinaction.transaction;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service(“cashier”)
public class CashierImpl implements Cashier {
@Autowired
private BookShopService bookShopService;

@Transactional
@Override
public void checkout(String username, List<String> isbns) {
    for(String isbn : isbns) {
        bookShopService.purchase(username, isbn);
    }
}

}
BookStockException

package com.springinaction.transaction;
public class BookStockException extends RuntimeException {

private static final long serialVersionUID = 1L;

public BookStockException() {
    super();
    // TODO Auto-generated constructor stub
}

public BookStockException(String arg0, Throwable arg1, boolean arg2,
        boolean arg3) {
    super(arg0, arg1, arg2, arg3);
    // TODO Auto-generated constructor stub
}

public BookStockException(String arg0, Throwable arg1) {
    super(arg0, arg1);
    // TODO Auto-generated constructor stub
}

public BookStockException(String arg0) {
    super(arg0);
    // TODO Auto-generated constructor stub
}

public BookStockException(Throwable arg0) {
    super(arg0);
    // TODO Auto-generated constructor stub
}

}
UserAccountException

package com.springinaction.transaction;
public class UserAccountException extends RuntimeException {

private static final long serialVersionUID = 1L;

public UserAccountException() {
    super();
    // TODO Auto-generated constructor stub
}

public UserAccountException(String arg0, Throwable arg1, boolean arg2,
        boolean arg3) {
    super(arg0, arg1, arg2, arg3);
    // TODO Auto-generated constructor stub
}

public UserAccountException(String arg0, Throwable arg1) {
    super(arg0, arg1);
    // TODO Auto-generated constructor stub
}

public UserAccountException(String arg0) {
    super(arg0);
    // TODO Auto-generated constructor stub
}

public UserAccountException(Throwable arg0) {
    super(arg0);
    // TODO Auto-generated constructor stub
}

}
测试类

package com.springinaction.transaction;

import java.util.Arrays;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTransitionTest {

private ApplicationContext ctx = null;
private BookShopDao bookShopDao = null;
private BookShopService bookShopService = null;
private Cashier cashier = null;
{
    ctx = new ClassPathXmlApplicationContext("config/transaction.xml");
    bookShopDao = ctx.getBean(BookShopDao.class);
    bookShopService = ctx.getBean(BookShopService.class);
    cashier = ctx.getBean(Cashier.class);
}

@Test
public void testBookShopDaoFindPriceByIsbn() {
    System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}

@Test
public void testBookShopDaoUpdateBookStock(){
    bookShopDao.updateBookStock("1001");
}

@Test
public void testBookShopDaoUpdateUserAccount(){
    bookShopDao.updateUserAccount("AA", 100);
}
@Test
public void testBookShopService(){
    bookShopService.purchase("AA", "1001");
}

@Test
public void testTransactionPropagation(){
    cashier.checkout("AA", Arrays.asList("1001", "1002"));
	 }
}

OVER
参考此篇编写

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值