Spring的事务管理

一、概述
  Spring的事务管理不需要与任何特定的事务API耦合,声明式事务基于Spring AOP实现。JavaEE应用的传统事务有两种策略:全局事务和局部事务,全局事务由应用服务器管理,需要底层服务器的JTA(java事务API)支持,局部事务和底层所采用的持久化技术有关,当采用JDBC持久化技术时,需要使用Connection对象来操作事务,而采用Hibernate持久化技术时,需要使用Session对象来操作事务。
  Spring事务策略是通过PlatformTransactionManager接口实现,该接口是Spring事务策略的核心。它有许多不同的实现类,应用程序面向与平台无关的接口编程,当底层采用不同的持久层技术时,系统只需使用不同的PlatformTransactionManager实现类即可---而这种切换通常由Spring容器负责管理,应用程序既无需与具体的API耦合,也无须与特定实现类耦合,从而将应用和持久化技术、事务API彻底分离开来。故Spring的事务管理机制是一种典型的策略模式。Spring完全支持像事务跨多个数据库资源这种跨多个事务性资源的全局事务。

二、使用TransactionProxyFactoryBean创建事务代理(Spring1.x)
2.1 DAO接口
package test.dao;
public interface NewsDao{
    public void insert(String title, String content);
}

2.2 DAO实现
package test.dao.impl;
import javax.sql.DataSource;
import java.sql.Connection;
import org.springframework.jdbc.core.JdbcTemplate;
import org.crazyit.app.dao.*;
public class NewsDaoImpl implements NewsDao{
    private DataSource ds;
    public void setDs(DataSource ds){
        this.ds = ds;
    }
    public void insert(String title, String content){
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.update("insert into news_inf values(null , ? , ?)", title , content);
        //两次插入的数据违反唯一键约束
        jt.update("insert into news_inf values(null , ? , ?)", title , content);
        //如果增加事务控制,我们发现第一条记录也插不进去。
        //如果没有事务控制,则第一条记录可以被插入
    }
}

2.3 Spring配置文件 bean.xml
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
    <!-- 定义数据源Bean,使用C3P0数据源实现 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定连接数据库的驱动 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定连接数据库的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定连接数据库的用户名 -->
        <property name="user" value="root"/>
        <!-- 指定连接数据库的密码 -->
        <property name="password" value="32147"/>
        <!-- 指定连接数据库连接池的最大连接数 -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定连接数据库连接池的最小连接数 -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定连接数据库连接池的初始化连接数 -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定连接数据库连接池的连接的最大空闲时间 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager 类 -->
    <!-- 该类实现PlatformTransactionManager接口,是针对采用数据源连接的特定实现-->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager时需要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置一个业务逻辑Bean -->
    <bean id="newsDao" class="org.crazyit.app.dao.impl.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    <!-- 为业务逻辑Bean配置事务代理 -->
    <bean id="newsDaoTrans" class=
    "org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <!-- 为事务代理工厂Bean注入事务管理器 -->
        <property name="transactionManager" ref="transactionManager"/>
        <property name="target" ref="newsDao"/>
        <!-- 指定事务属性 -->
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRED</prop>
            </props>
        </property>
    </bean>
</beans>

2.4 测试
package test;
import org.springframework.context.support.*;
import org.springframework.context.*;
import org.crazyit.app.dao.*;
public class SpringTest{
    public static void main(String[] args){
        //创建Spring容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        //获取事务代理Bean
        NewsDao dao = (NewsDao)ctx.getBean("newsDaoTrans" , NewsDao.class);
        //执行插入操作
        dao.insert("疯狂Java" , "轻量级Java EE企业应用实战");
    }
}

三、Spring2.x提供了tx命名空间来配置事务管理
3.1 DAO接口
package org.crazyit.app.dao;
public interface NewsDao{
    public void insert(String title, String content);
}

3.2 DAO实现
package org.crazyit.app.dao.impl;
import javax.sql.DataSource;
import java.sql.Connection;
import org.springframework.jdbc.core.JdbcTemplate;
import org.crazyit.app.dao.*;
public class NewsDaoImpl implements NewsDao{
    private DataSource ds;
    public void setDs(DataSource ds){
        this.ds = ds;
    }
    public void insert(String title, String content){
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.update("insert into news_inf values(null , ? , ?)", title , content);
        //两次插入的数据违反唯一键约束
        jt.update("insert into news_inf values(null , ? , ?)", title , content);
        //如果增加事务控制,我们发现第一条记录也插不进去。
        //如果没有事务控制,则第一条记录可以被插入
    }
}

3.3 Spring配置文件
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    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">
    <!-- 定义数据源Bean,使用C3P0数据源实现 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定连接数据库的驱动 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定连接数据库的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定连接数据库的用户名 -->
        <property name="user" value="root"/>
        <!-- 指定连接数据库的密码 -->
        <property name="password" value="32147"/>
        <!-- 指定连接数据库连接池的最大连接数 -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定连接数据库连接池的最小连接数 -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定连接数据库连接池的初始化连接数 -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定连接数据库连接池的连接的最大空闲时间 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager 类 -->
    <!-- 该类实现PlatformTransactionManager接口,是针对采用数据源连接的特定实现-->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager时需要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 配置一个业务逻辑Bean -->
    <bean id="newsDao" class="org.crazyit.app.dao.impl.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    <!-- 配置事务增强处理,指定事务管理器 -->
    <tx:advice id="txAdvice"
        transaction-manager="transactionManager">
        <!-- 用于配置详细的事务语义 -->
        <tx:attributes>
            <!-- 所有以'get'开头的方法是read-only的 -->
            <tx:method name="get*" read-only="true"/>
            <!-- 其他方法使用默认的事务设置 -->
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!-- AOP配置的元素 -->
    <aop:config>
        <!-- 配置一个切入点,匹配org.crazyit.app.dao.impl包下
            所有以Impl结尾的类里、所有方法的执行 -->
        <aop:pointcut id="myPointcut"
            expression="execution(* org.crazyit.app.dao.impl.*Impl.*(..))"/>
            <!-- 指定在txAdvice切入点应用txAdvice事务增强处理 -->
        <aop:advisor advice-ref="txAdvice"
            pointcut-ref="myPointcut"/>
    </aop:config>
</beans>

3.4 测试
package lee;
import org.springframework.context.support.*;
import org.springframework.context.*;
import org.crazyit.app.dao.*;
public class SpringTest{
    public static void main(String[] args){
        //创建Spring容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        //获取事务代理Bean
        NewsDao dao = (NewsDao)ctx.getBean("newsDao" , NewsDao.class);
        //执行插入操作
        dao.insert("疯狂Java" , "轻量级Java EE企业应用实战");
    }
}

四、使用@Transactional Annotation
4.1 DAO接口
package org.crazyit.app.dao;
public interface NewsDao{
    public void insert(String title, String content);
}

4.2 DAO实现
package org.crazyit.app.dao.impl;
import javax.sql.DataSource;
import java.sql.Connection;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.transaction.annotation.*;
import org.crazyit.app.dao.*;
public class NewsDaoImpl implements NewsDao{
    private DataSource ds;
    public void setDs(DataSource ds){
        this.ds = ds;
    }
    @Transactional(propagation=Propagation.REQUIRED)
    public void insert(String title, String content){
        JdbcTemplate jt = new JdbcTemplate(ds);
        jt.update("insert into news_inf values(null , ? , ?)", title , content);
        //两次插入的数据违反唯一键约束
        jt.update("insert into news_inf values(null , ? , ?)", title , content);
        //如果增加事务控制,我们发现第一条记录也插不进去。
        //如果没有事务控制,则第一条记录可以被插入
    }
}

4.3 Spring配置文件bean.xml
<?xml version="1.0" encoding="GBK"?>
<!-- Spring配置文件的根元素,使用spring-beans-3.0.xsd语义约束 -->
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    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">
    <!-- 定义数据源Bean,使用C3P0数据源实现 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <!-- 指定连接数据库的驱动 -->
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <!-- 指定连接数据库的URL -->
        <property name="jdbcUrl" value="jdbc:mysql://localhost/javaee"/>
        <!-- 指定连接数据库的用户名 -->
        <property name="user" value="root"/>
        <!-- 指定连接数据库的密码 -->
        <property name="password" value="32147"/>
        <!-- 指定连接数据库连接池的最大连接数 -->
        <property name="maxPoolSize" value="40"/>
        <!-- 指定连接数据库连接池的最小连接数 -->
        <property name="minPoolSize" value="1"/>
        <!-- 指定连接数据库连接池的初始化连接数 -->
        <property name="initialPoolSize" value="1"/>
        <!-- 指定连接数据库连接池的连接的最大空闲时间 -->
        <property name="maxIdleTime" value="20"/>
    </bean>
    <!-- 配置一个业务逻辑Bean -->
    <bean id="newsDao" class="org.crazyit.app.dao.impl.NewsDaoImpl">
        <property name="ds" ref="dataSource"/>
    </bean>
    <!-- 配置JDBC数据源的局部事务管理器,使用DataSourceTransactionManager 类 -->
    <!-- 该类实现PlatformTransactionManager接口,是针对采用数据源连接的特定实现-->
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!-- 配置DataSourceTransactionManager时需要依注入DataSource的引用 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 根据Annotation来生成事务代理 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
</beans>

4.4 测试
package lee;
import org.springframework.context.support.*;
import org.springframework.context.*;
import org.crazyit.app.dao.*;
public class SpringTest{
    public static void main(String[] args){
        //创建Spring容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext("bean.xml");
        //获取事务代理Bean
        NewsDao dao = (NewsDao)ctx.getBean("newsDao" , NewsDao.class);
        //执行插入操作
        dao.insert("疯狂Java" , "轻量级Java EE企业应用实战");
    }
}

你可能感兴趣的:(Spring的事务管理)