Spring系列教程——15Spring事务管理器

Spring系列教程——15Spring事务管理器

上一篇:Spring系列教程——14Spring数据库操作讲解
下一篇:Spring系列教程——16Aop事务配置与注解事务配置

一.事务概念与特性讲解

1.概念

事务是属于数据库操作的概念,一个事务可有多个数据库操作组成,它是一个操作单元,组成它的操作作为一个整体,要么同时执行成功,要么同时失败

2.特性(ACID)

1.原子性

即整体性,是指事务包含的所有操作要么全部成功,要么全部失败.

2.一致性

即数据一致,一个事务执行之前和执行之后都必须处于一致性状态,比如当在执行一个事务的时候,数据库系统发生了异常,这时事务执行了一般,导致只有执行了的那一部分操作才被写入数据库,而剩下的未被写入,这就是一个不一致的状态。

3.隔离性

也即并发性,一个事务的执行应该与其并发执行的其他事务互不干扰,互相隔离

4.持久性

也即永久性,一个事务一旦提交了,那么他对数据库的修改将是永久的

3.隔离级别

事务的隔离级别一共有四种,由低到高为Read uncommitted、Read committed、Repeatable read 、Serializable。但是会产生脏读不可重复复读虚读(幻读)的问题。具体的例子可以参见这篇文章事务的四种隔离级别

二.事务管理器介绍

1.准备讲解

首先我们需要讲解下面的红框里面的PlatformTransactionManager,TransactionDefinition,TransactionStatus三个接口。
在这里插入图片描述
PlatformTransactionManager:平台事务管理器,spring要管理事务,必须使用事务管理器,进行事务配置时,必须配置事务管理器。
TransactionDefinition:事务详情(事务定义、事务属性),spring用于确定事务具体详情。例如:隔离级别、是否只读、超时时间等。进行事务配置时,必须配置详情。spring将配置项封装到该对象实例。
在这里插入图片描述
红框里面的内容我们理解,上面的内容介绍如下(关于传播行为详细演示可以参见Spring事务传播行为详解,在后面讲事务代理工厂也会涉及到):
在这里插入图片描述
TransactionStatus:事务状态,spring用于记录当前事务运行状态。例如:是否有保存点,事务是否完成。spring底层根据状态进行相应操作。
在这里插入图片描述
接下来我们来看一个案例更好的理解.
1.准备表数据:

CREATE TABLE account(
  id INT PRIMARY KEY AUTO_INCREMENT,
  username VARCHAR(50),
  money INT
);
INSERT INTO account(username,money) VALUES('jack','10000');
INSERT INTO account(username,money) VALUES('rose','10000');

在这里插入图片描述
2.dao层:
IAccountDao接口

package dao;
@SuppressWarnings("all")
public interface IAccountDao {
    //进账
    public void inner(String inner,Integer money);

    //扣钱
    public void out(String outer,Integer money);
}

AccountDaoImpl实现类

package dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    @Override
    public void inner(String inner, Integer money) {
        getJdbcTemplate().update("update account set money = money + ? where username = ?",money,inner);
    }

    @Override
    public void out(String outer, Integer money) {
        getJdbcTemplate().update("update account set money = money - ? where username = ?",money,outer);
    }
}

3.Service层
IAccountService接口

package service;
@SuppressWarnings("all")
public interface IAccountService {
    public void transfer(String outter,String inner,Integer money);
}

AccountServiceImpl实现类

package service;
import dao.IAccountDao;

public class AccountServiceImpl implements IAccountService {
    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }
    private IAccountDao accountDao;
    @Override
    public void transfer(String outter, String inner, Integer money) {
        accountDao.inner(inner,money);
        accountDao.out(outter,money);
    }
}

4.配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">
    <context:property-placeholder location="db.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="password" value="${password}"></property>
        <property name="user" value="${user}"></property>
        <property name="driverClass" value="${driverClass}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
    </bean>
    <bean id="accountDao" class="dao.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <bean id="accountService" class="service.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
</beans>

5.测试

ApplicationContext context = new  ClassPathXmlApplicationContext("beans.xml");
IAccountService service = (IAccountService) context.getBean("accountService");
service.transfer("jack","rose",1000);

在这里插入图片描述
接下来我们才正式开始事务管理的内容。

2.手动事务管理配置

这一部分我们将讲解Spring底层的事务管理的实现,但是我们对该部分只要了解就够了,因为后面都会通过Aop来实现。

spring底层使用 TransactionTemplate 事务模板进行操作。
1.service 需要获得 TransactionTemplate
2.spring 配置模板,并注入给service
3.模板需要注入事务管理器
4.配置事务管理器:DataSourceTransactionManager ,需要注入DataSource

接下来我们按照上面的步骤来做。
1.在AccountServiceImpl里面配置模板

private TransactionTemplate template;
public void setTemplate(TransactionTemplate template) {
    this.template = template;
}
@Override
public void transfer(String outter, String inner, Integer money) {
    final String out = outter;
    final  String in = inner;
    final  Integer mon = money;
    template.execute(new TransactionCallbackWithoutResult() {
        @Override
        protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) {
            accountDao.inner(in,mon);
            int i = 10/0;
            accountDao.out(out,mon);
        }
	});

2.beans.xml修改为下面内容

<context:property-placeholder location="db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="password" value="${password}"></property>
    <property name="user" value="${user}"></property>
    <property name="driverClass" value="${driverClass}"></property>
    <property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
<!--配置事务管理器bean对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"></bean>
<!--配置事务模板-->
<bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
    <!--配置事务管理器-->
    <property name="transactionManager" ref="transactionManager"></property>
</bean>
<bean id="accountDao" class="dao.AccountDaoImpl">
    <property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="service.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
    <property name="template" ref="transactionTemplate"></property>
</bean>

3.测试
仍然使用上面的代码
在执行到int i=10/0;时出现异常,但是表数据由于回滚而未出现隔离性问题。
在这里插入图片描述
在这里插入图片描述

3.事务代理工厂配置

Spring提供管理事务的代理工厂bean :TransactionProxyFactoryBean,这里我们采用的是AOP半自动的方式来讲解使用方式。(前面讲过,忘记了可以参考Spring系列教程-12配置文件实现AOP
这里我们不再需要TransactionTemplate了,将tranfer方法还原:

@Override
public void transfer(String outter, String inner, Integer money) {
    accountDao.inner(inner,money);
    int i = 10/0;
    accountDao.out(outter,money);
}

beans.xml配置的配置

<context:property-placeholder location="db.properties"></context:property-placeholder>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
   <property name="password" value="${password}"></property>
   <property name="user" value="${user}"></property>
   <property name="driverClass" value="${driverClass}"></property>
   <property name="jdbcUrl" value="${jdbcUrl}"></property>
</bean>
<bean id="accountDao" class="dao.AccountDaoImpl">
   <property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="accountService" class="service.AccountServiceImpl">
   <property name="accountDao" ref="accountDao"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
       <property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="proxyService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
   <!--接口-->
   <property name="proxyInterfaces" value="service.IAccountService"></property>
   <!--目标类-->
   <property name="target" ref="accountService"></property>
    <!--切面类由Spring来完成-->
   <!--配置事务管理器-->
   <property name="transactionManager" ref="transactionManager"></property>
   <!--事务属性、详情配置
        prop.key :确定哪些方法使用当前事务配置
        prop.text:用于配置事务详情
    格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
            传播行为  隔离级别	是否只读   异常回滚	 异常提交-->
   <property name="transactionAttributes">
       <props>
       </props>
   </property>
</bean>

注意上面这一段:

<!--事务属性、详情配置
    prop.key :确定哪些方法使用当前事务配置
    prop.text:用于配置事务详情
格式:PROPAGATION,ISOLATION,readOnly,-Exception,+Exception
        传播行为  隔离级别	是否只读   异常回滚	 异常提交-->
<property name="transactionAttributes">
   <props>
   </props>
</property>

我们还没有对transactionAttributes(事务属性)进行配置,我们先测试一下看可否保证数据安全。
在这里插入图片描述
这说明我们是事务管理工厂配置好后没有起到作用,原因在于我们没有配置transactionAttributes来就行限定。配置这一段内容:

<property name="transactionAttributes">
   <props>
       <prop key="transfer">PROPAGATION_REQUIRED,ISOLATION_DEFAULT</prop>
   </props>
</property>

接下来我们先将表数据修改回来。
在这里插入图片描述
测试发现此时发生异常后数据保持正常:
在这里插入图片描述
加入我们把配置改为:

PROPAGATION_REQUIRED,ISOLATION_DEFAULT,readOnly

再来测试看看报什么异常:
在这里插入图片描述
还有-Exception,+Exception没有演示,这里留给读者。(不过要注意不一定只可以写Exception,还可以类似这样+java.lang.ArithmeticException)
上面的方式不是我们推荐的事务管理的配置方式,我们更推荐Aop配置与注解的方式,请见下一篇。
上一篇:Spring系列教程——14Spring数据库操作讲解
下一篇:Spring系列教程——16Aop事务配置与注解事务配置

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值