spring 中的事物管理问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 中的事物管理问题相关的知识,希望对你有一定的参考价值。

<aop:config>
<aop:pointcut id="bussinessService" expression="execution(public * com.wjs.registration.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="bussinessService"/>
</aop:config>

这句话哪位高手告诉下啥意思哦?特别是execution(public * com.wjs.registration.service.*.*(..))" 我就不知道 *.*(..)这个有啥用哦?

---------------------------------------------------------

Spring提供的事务管理可以分为两类:编程式的和声明式的。

编程式的,比较灵活,但是代码量大,存在重复的代码比较多;声明式的比编程式的更灵活方便。

1、传统使用JDBC的事务管理

以往使用JDBC进行数据操作,使用DataSource,从数据源中得到Connection,我们知道数据源是线程安全的,而连接不是线程安全的,所以对每个请求都是从数据源中重新取出一个连接。一般的数据源由容器进行管理,包括连接池。例如TOMCAT,WEBSPHERE,WEBLOGIC等这些J2EE商业容器都提供了这个功能。

以往的我们使用JDBC在写代码时,事务管理可能会是这样:

Connection conn = null;
try
conn = DBConnectionFactory.getConnection;
conn.setAutoCommit(false);
//do something
conn.commit(); //commit transcation
catch(Exception e)
conn.rollback();

finally
try
conn.close();
catch(SQLException se)
//close ResultSet,PreparedStatement,Connection
//notice:Maybe ocurr Exception when u close rs,pstmt,conn


按照以往的思路来写代码,代码量比较长,而且容易疏忽,忘掉一些try/catch,引发一些异常无法catch,虽然有时候我们会写DBTool类,来关闭这些资源,并且保证在关闭这些资源时,不向外抛异常,但是这样做会导致额外的麻烦。

2、Spring提供的编程式的事务处理

Spring提供了几个关于事务处理的类:TransactionDefinition //事务属性定义

TranscationStatus //代表了当前的事务,可以提交,回滚。

PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。

我们使用编程式的事务管理流程可能如下:

(1) 声明数据源。

(2) 声明一个事务管理类,例如:DataSourceTransactionManager,HibernateTransactionManger,JTATransactionManager等

(3) 在我们的代码中加入事务处理代码:

TransactionDefinition td = new TransactionDefinition();
TransactionStatus ts = transactionManager.getTransaction(td);
try
//do sth
transactionManager.commit(ts);
catch(Exception e)

使用Spring提供的事务模板TransactionTemplate:

void add()

transactionTemplate.execute( new TransactionCallback()
pulic Object doInTransaction(TransactionStatus ts)




TransactionTemplate也是为我们省去了部分事务提交、回滚代码;定义事务模板时,需注入事务管理对象。

3、Spring声明式事务处理

Spring声明式事务处理也主要使用了IoC,AOP思想,提供了TransactionInterceptor拦截器和常用的代理类TransactionProxyFactoryBean,可以直接对组件进行事务代理。

使用TransactionInterceptor的步骤:

(1)定义数据源,事务管理类

(2)定义事务拦截器,例如:

<bean id = "transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributeSource">
<value>
com.test.UserManager.*r=PROPAGATION_REQUIRED
</value>
</property>
</bean>

(3)为组件声明一个代理类:ProxyFactoryBean

<bean id="userManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>com.test.UserManager</value></property>
<property name="interceptorNames">
<list>
<idref local="transactionInterceptor"/>
</list>
</property>
</bean>

使用TransactionProxyFactoryBean:

<bean id="userManager"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local="userManagerTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

TransactionProxyFactoryBean只是为组件的事务代理,如果我们要给组件添加一些业务方面的验证等,可以使用TransactionTemplate加拦截器方式,为组件添加多个拦截器,spring AOP中提供了三类Advice,即前增强,后增强,抛出异常时的增强,可以灵活使用。

----------------------------------------------------------
参考技术A *.*(..)第一个*是类 这里放* 就是说com.wjs.registration.service这个路径下面所有的类
点后面的*就是该类里面的方法 * 就是说所有的方法
(..)这里是方法的参数 两个..就是说任意类型,个数的参数
还有不懂的在问我本回答被提问者采纳
参考技术B *.*(..)通配符,就是扫描com.wjs.registration.service包下的所有类的所有方法

Spring中的事物管理,用 @Transactional 注解声明式地管理事务

事物:

  事务管理是企业级应用程序开发中必不可少的技术,  用来确保数据的 完整性和 一致性.

  事务就是一系列的动作, 它们被当做一个单独的工作单元. 这些动作要么全部完成, 要么全部不起作用

事务的四个关键属性:

  原子性:事务是一个原子操作, 由一系列动作组成. 事务的原子性确保动作要么全部完成要么完全不起作用.

  一致性:一旦所有事务动作完成, 事务就被提交. 数据和资源就处于一种满足业务规则的一致性状态中.

  隔离性:可能有许多事务会同时处理相同的数据, 因此每个事物都应该与其他事务隔离开来, 防止数据损坏.

  持久性:一旦事务完成, 无论发生什么系统错误, 它的结果都不应该受到影响. 通常情况下, 事务的结果被写到持久化存储器中.

-------------------------------------------------------------------

用 @Transactional 注解声明式地管理事务的例子:

在mysql中建立三个数据表分别为:

account:属性:用户名:username(varchar),该用户的账户余额:balance(int);

book:书的编号:isbn(int),书名:book_name(varchar),价钱:price(int);

book_stock:书的编号:isbn(int),库存:stock(int);

导包。。。。

建立存放连接数据库的file文件:jdbc.properties;

jdbc.user=root
jdbc.password=lxn123
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///spring2

jdbc.initPoolSize=5
jdbc.maxPoolSize=10

 

建立spring bean configuration file的xml文件,导入资源文件和配置c3p0数据源,这是基于注解的声明管理事物,所以也将加特定注解的包进行扫描;

    <!-- 基于注解的bean配置,扫描这个包及其自包 -->
    <context:component-scan base-package="com.atguigu.spring.jdbc"></context:component-scan>
    
    <!-- 导入资源文件 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    
    <!-- 配置c3p0数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${jdbc.user}"></property>
        <property name="password" value="${jdbc.password}"></property>
        <property name="driverClass" value="${jdbc.driverClass}"></property>
        <property name="jdbcUrl" value="${jdbc.jdbcUrl}"></property>
        
        <property name="initialPoolSize" value="${jdbc.initPoolSize}"></property>
        <property name="maxPoolSize" value="${jdbc.maxPoolSize}"></property>
    </bean>
    
    <!-- 配置spring 的 JdbcTemplate ,里面有一些jdbc的方法,实现对数据库数据的增删改查-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

 

建立一个接口:让继承他的子类去实例化一些方法

package com.atguigu.spring.jdbc;

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

 

建立BookShopDaoImpl 类并继承接口: BookShopDao,实例化其方法,并且在类名前 加注解:@Repository("bookShopDao"),和在属性private JdbcTemplate jdbcTemplae;的上边加注解:@Autowired

package com.atguigu.spring.jdbc;

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 jdbcTemplae;
    
    //通过书名获取书的价格
    @Override
    public int findBookPriceByIsbn(int isbn) {
        String sql="select price from book where isbn=?";
        return jdbcTemplae.queryForObject(sql, Integer.class, isbn);
    }
    
    
    @Override
    public void updateBookStock(int isbn) {
        String sql="select stock from book_stock where isbn=?";
        //stock,指数的库存
        //通过书名检查书的库存
        int stock=jdbcTemplae.queryForObject(sql, Integer.class, isbn);
        if(stock==0){
            System.out.println("库存不足!!!");
            throw new BookStockException("库存不足!!!");
        }
        
        String sql2="update book_stock set stock=stock-1 where isbn=?";
        jdbcTemplae.update(sql2, isbn);
    }
    
    //用户的余额减去要购买书的价钱
    @Override
    public void updateUserAccount(String username, int price) {
        //验证余额是否足够, 若不足, 则抛出异常
        String sql2="select balance from account where username=?";
        int balance=jdbcTemplae.queryForObject(sql2, Integer.class, username);
        if(balance<price){
            System.out.println("余额不足!!!");
            throw new BookStockException("余额不足!!!");
        }
        
        String sql="update account set balance=balance-? where username=?";
        jdbcTemplae.update(sql, price, username);
    }

}

 

建立一个接口:BookShopService,有一个没有实例化的方法;

package com.atguigu.spring.jdbc;

public interface BookShopService {
    public void purchase(String username,int isbn);
}

 

建立类: BookShopServiceImpl 继承于接口 BookShopService,有一个方法,合并了BookShopDaoImpl类中的三个方法,建立了一个“事物”;在该类上边加了注解@Service("bookShopService"),在属性private BookShopDao bookShopDao;加了注解@Autowired,在其方法上加了注解@Transactional(propagation=Propagation.REQUIRES_NEW),都是便于在spring的xml文件中bean的配置扫描识别;

package com.atguigu.spring.jdbc;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
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=false,
            timeout=3)
 * 
 */
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    @Override
    public void purchase(String username, int isbn) {
        
        //1. 获取书的单价
        int price=bookShopDao.findBookPriceByIsbn(isbn);
        
        //2. 更新书的库存
        bookShopDao.updateBookStock(isbn);
        
        //3. 更新用户余额
        bookShopDao.updateUserAccount(username, price);
    }

}

 

建立一个接口:Cashier

package com.atguigu.spring.jdbc;

import java.util.List;

public interface Cashier {
    //购买多本书
    public void checkout(String username,List<Integer> isbns);
}

 

建立类:CashierImpl 继承于上边的接口 Cashier,该中有private BookShopService bookShopService这个“事物”父类的属性,类中仅有的一个方法,可以实现多个“原子事物",该类是调用事物的事物类,仅有的方法为调用事物的事物方法;在该类上边加注解@Service("cashier"),在属性private BookShopService bookShopService;上边加注解@Autowired,在方法上边加事物的注解@Transactional

package com.atguigu.spring.jdbc;

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<Integer> isbns) {
        for(int isbn:isbns){
            bookShopService.purchase(username, isbn);
        }

    }

}

 

在spring的xml文件中 配置事物管理器和 启用事物管理器的注解

    <!-- 配置事物管理器 -->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <!-- 启用事物管理器的注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

 

最后建立测试类:JUnitTest

package com.atguigu.spring.jdbc;

import java.sql.SQLException;
import java.util.Arrays;

import javax.sql.DataSource;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;


public class JUnitTest {
    
    private ApplicationContext ctx=null;
    private BookShopService bookShopService;
    private Cashier cashier;
    {
        ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        bookShopService=(BookShopService) ctx.getBean("bookShopService");
        cashier=(Cashier) ctx.getBean("cashier");
    }
    
    @Test
    //调用事物的事物方法的测试,即有多个事物
    public void testCashier(){
        cashier.checkout("AA", Arrays.asList(1001,1002));
    }
    @Test
    //事物的测试方法
    public void testPurchase(){
        bookShopService.purchase("AA", 1001);
    }
    //测试数据库连接池是否连接成功
    public void testDataSource() throws SQLException {
        DataSource dataSource=(DataSource) ctx.getBean("dataSource");
        System.out.println(dataSource.getConnection());
        
    }

}

 

建立一个异常处理类:BookStockException 继承于异常处理父类 RuntimeException

package com.atguigu.spring.jdbc;

public class BookStockException extends RuntimeException {
    //异常类的建立:继承于RuntimeException这个父类,点击上边的Source,设置构造器,即可
    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
    }

    
    
}

 

以上是关于spring 中的事物管理问题的主要内容,如果未能解决你的问题,请参考以下文章

Spring中的事物管理,用 @Transactional 注解声明式地管理事务

spring详解——事物管理

Spring事物管理简介 (转)

spring中的事务管理和aop的关系

转:Spring中事物管理

SpringMVC中的@Transaction怎么使用,有啥作用