spring-事务管理-07

spring 事务管理


spring 的事务基于AOP实现,而spring的AOP以方法为单位,所以spring的事务属性是对事务应用的方法的策略的描述。

  • 事务管理是数据库操作中一个非常重要的概念
  • Spring框架使用AOP机制,降低了进行事务管理时的复杂性
  • spring 事务管理,使程序员将精力集中在对数据库的业务逻辑操作上,而不必去关心连接的建立和关闭、异常的捕获、日志的记录、资源的释放等。

总结

Spring框架的事务管理是基于动态的AOP机制实现的,它把所有注入连接数据库、捕获异常、清理资源等操作封装到一些已经定义好的类和接口当中,程序员只需要在Spring配置文件中将它们载入,并且在使用类中实现那些处理业务逻辑的接口即可。


Spring的事务管理方式有两种:

  • 编程式事务管理
  • 声明式事务管理

编程式事务管理


编程式事务管理要求程序员在自己编写的类中,重写特定的方法去完成业务操作。

  • 使用TransactionTemplate

  • 使用接口PlatformTransactionManager的事务管理器

添加jar包spring-tx-4.3.17.RELEASE.jar

spring推荐使用TransactionTemplate


利用TransactionTemplate实现Spring编程式事务管理


在spring的配置文件中,声明事务管理器和TransactionTemplate模板

<!-- 定义TransactionTemplate模板 -->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager">
        <ref bean="transactionManager"/>
    </property>
    <property name="propagationBehaviorName">
    <!-- 限定事务的传播行为,规定当前的方法必须在事务中,若没有事务,则创建一个。
 	一个新的事务和方法一同开始,随方法的返回或抛出异常而终止。-->
        <value>PROPAGATION_REQUIRED</value>
    </property>
</bean>
<!-- 定义事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
</bean>

创建类TransactionExample,定义添加数据的方法,在方法中执行两次添加数据库操作,并用事务保护操作。

以匿名类的方式定义TransactionCallback接口的实现,来处理事务管理。

package transaction;

import java.sql.Connection;
import java.sql.Statement;

import javax.sql.DataSource;
//添加spring-tx-4.3.17.RELEASE.jar包
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

public class TransactionExample {
    DataSource dataSource;//注入数据源
    PlatformTransactionManager transactionManager;//注入事务管理器
    TransactionTemplate transactionTemplate;//注入TransactionTemplate模板
    
    //上面三个属性的set方法,set方法是注入需要
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    } 
    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    } 
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    //操作数据库的方法
    @SuppressWarnings({ "unchecked", "rawtypes" })
	public void transactionOperation() {
        transactionTemplate.execute(new TransactionCallback() {
            public Object doInTransaction(TransactionStatus status) {
                Connection conn = DataSourceUtils.getConnection(dataSource);//获得数据库连接
                try {
                    Statement stmt = conn.createStatement();
                    //执行两次添加方法
                    stmt.execute("insert into tb_user(name,age,sex) values('小强','26','男')");
                    int a=0;//制造异常测试事务是否配置成功
                    //a=9/a;
                    stmt.execute("insert into tb_user(name,age,sex) values('小红','22','女')");
                    System.out.println("操作执行成功!");
                } catch (Exception e) {
                    transactionManager.rollback(status);//事务回滚
                    System.out.println("操作执行失败,事务回滚!");
                    System.out.println("原因:"+e.getMessage());
                }
                return null;
            }
        });
    }
}

在spring的配置文件中,注入TransactionExample

<!-- 为TransactionExample注入数据源、事务管理器、TransactionTemplate模板-->
<bean id="transactionExample"
    class="transaction.TransactionExample">
    <property name="dataSource">
        <ref bean="dataSource" />
    </property>
    <property name="transactionManager">
        <ref bean="transactionManager" />
    </property>
    <property name="transactionTemplate">
        <ref bean="transactionTemplate"/>
    </property>
</bean>

创建Manger类

package transaction;

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



public class Manager {

    public static void main(String[] args) {
        //装载配置文件
        ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取transactionExample
        TransactionExample transactionExample = (TransactionExample) factory.getBean("transactionExample");
        transactionExample.transactionOperation();//执行添加方法
    }
}

test:

  • 当抛出异常时,数据库没有插入数据。transactionManager.rollback(status);事务回滚成功!

    即事务配置成功

  • 容易看出以上代码中,没有编写关闭连接之类的事务管理代码(对比spring dao中操作数据库的代码)

a=9/a;

声明式事务管理


声明式事务管理不像编程式事务管理,靠重写接口去实现业务逻辑和事务控制相分离,它是通过拦截器机制来实现的。

  • 在使用声明式事务时不需编写任何代码即可通过实现基于容器的事务管理。
  • 在spring中常用TransactionalProxyFactoryBean完成声明式事务管理。
  • 在处理业务逻辑之前,先靠拦截器去完成连接数据库、创建事务等;在处理业务逻辑之后,逆向调用拦截器实现事务提交或回滚、清理资源等操作。
  • 用户就不需要在自己编写的类中去重写接口,而仅需要在自命名的方法中处理业务逻辑,然后在配置文件中为该方法配置拦截器即可。

使用TransactionalProxyFactoryBean需要注入所依赖的事务管理器,并设置代理的目标对象,代理对象的生成方式和事务属性。

代理对象是在目标对象上生成的包含事务和AOP切面的新对象,它可以赋给目标的引用来代替目标对象以支持事务或AOP提供的切面功能。

两种常用的方式:

  • 基于tx和aop名字空间的xml配置文件
  • 基于@Transactional注解。

在spring配置文件中定义数据源DataSource和事务管理器,该管理器被注入到TransactionProxyFactoryBean中,设置代理对象和事务属性。

<!-- 定义事务管理器 :使用前面的:transactionManager-->
<!-- 定义TransactionProxy -->
<bean id="transactionProxy"
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager">
        <ref local="transactionManager" />
    </property>
    <property name="target">
        <!-- 以内部类的形式指定代理对象 -->
        <bean id="addDAO" class="state_transaction.AddDAO">
            <property name="dataSource">
                <ref local="dataSource" />
            </property>
        </bean>
    </property>
    <!-- 设置创建基于类的代理 -->
    <property name="proxyTargetClass" value="true" />
    <!-- 配置事务传播:所有以add开头的事务必须运行在事务中(若没有则创建) -->
    <property name="transactionAttributes">
        <props>
            <prop key="add*">PROPAGATION_REQUIRED</prop>
        </props>
    </property>
</bean>

编写操作数据库的AddDAO类,在该类的addUser()方法中,执行两次数据插入操作。

这个方法在配置TransactionProxyFactoryBean时被定义为事务性方法,并指定了事务属性,故方法中的所有数据库操作都被当做一个事务处理。

package state_transaction;

import org.springframework.jdbc.core.support.JdbcDaoSupport;
//使用spring dao
public class AddDAO extends JdbcDaoSupport {
    //添加用户的方法
	public void addUser(User user){
        //执行添加方法的sql语句
    	 String sql="insert into tb_user (name,age,sex) values('" + 
                 user.getName() + "','" + user.getAge()+ "','" + user.getSex()+ "')";
    	
        //执行两次添加方法
    	 getJdbcTemplate().execute(sql);
    	 int a=0;//制造异常测试事务是否配置成功
        //a=9/a;
        getJdbcTemplate().execute(sql);
        System.out.print("数据插入成功");
    }
}

注意到没有手动地添加事务管理代码,事务由spring管理。


编写User类并添加set/get方法

package state_transaction;

public class User {
    private Integer id;//唯一性标识
    private String name;//姓名
    private String password;
    private Integer age;//年龄
    private String sex;//性别
}

编写Manager类

package state_transaction;

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

public class Manager {
    public static void main(String[] args) {
		@SuppressWarnings("resource")
        //装载配置文件
		ApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");
        AddDAO addDAO = (AddDAO)factory.getBean("transactionProxy");//获取AddDAO
        User user = new User();//实例化User实体对象
        user.setName("声明式事务");//设置姓名
        user.setAge(18);//设置年龄
        user.setSex("男");//设置性别   
        addDAO.addUser(user);//执行数据库添加方法
    }
}

test:

  • 当抛出异常时,数据库没有插入数据。事务回滚成功!

    即事务配置成功

a=9/a;

posted @ 2020-12-13 09:23  jt_coder  阅读(89)  评论(0)    收藏  举报