随笔录 之 spring 自学杂记(六) -- Transaction(TX)

Spring Transaction

事务,简称TX,简单说就是一系列的动作,被当作一个独立的单元,这个单元 要么成功,要么失败。通常是对数据库中数据的一系列读/写操作

在企业级应用程序开发中是必不可少的技术之一,用来确保数据的正确性和一致性。


ACID特性

事务的四大特性:

  1. 原子性(Atomicity):事务作为一个整体单元被执行,要么成功,要么就失败。
  2. 一致性(Consistency):数据一致性,如:转账业务中,转账前后总金额应该是一致的。
  3. 隔离性(Isolation):在多个事务并发执行时,一个事务的执行不应该影响其他的事物执行。
  4. 持久性(Durability):已提交的事务对于数据库的修改应该永久性的保存在数据库中。

Spring中的事务

1. 编程式的事务

2. 声明式的事务

编程式事务 ,可以自己控制事务的开启、提交、回滚等操作,但是不便于维护。必须在每个事务操作中包含另外的事务管理代码。因此要在每个操作中重复样板事务代码。此外还很难对不同的应用程序启用和禁用事务管理。

声明式事务,在大多数情况下要比编程式事务管理更好用。它将事务管理代码从业务中分离出来,以声明的方式来实现事务管理。将事务管理作为一种横切关注点,可以通过AOP方法模块化。但是,声明式事务管理不太灵活,因为无法通过代码精确的控制事务。


编程式事务此处不讲,主要是声明式事务。

先来一个简单的实例:转账业务

一个简单实例

1、数据库

-- Create table
create table DEMO_ACCOUNT
(
  NAME    VARCHAR2(20) not null, --账户名
  BALANCE NUMBER                 --账户下的余额
)

数据:
这里写图片描述

2、配置文件

配置数据源

<?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:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    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-4.0.xsd 
            http://www.springframework.org/schema/mvc 
            http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd 
            http://www.springframework.org/schema/context 
            http://www.springframework.org/schema/context/spring-context-4.0.xsd 
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop-4.0.xsd 
            http://www.springframework.org/schema/tx 
            http://www.springframework.org/schema/tx/spring-tx-4.0.xsd ">


    <!-- 自动扫描 -->
    <context:component-scan base-package="com.wm.spring.tx"></context:component-scan>

    <!-- 导入外部配置文件 -->
    <context:property-placeholder location="classpath:jdbc.properties" />

    <!-- c3p0  数据源 -->
    <bean id="dataSource" 
    class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="${jdbc.driverClassName}" />
        <property name="jdbcUrl" value="${jdbc.url}" />
        <property name="user" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />


        <property name="maxPoolSize" value="${c3p0.pool.size.max}"></property>
        <property name="minPoolSize" value="${c3p0.pool.size.min}"></property>
        <property name="initialPoolSize" value="${c3p0.pool.size.ini}"></property>  

    </bean>

    <!-- spring jdbcTemplate -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <!--  事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 添加 事务注解驱动 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>    

在配置文件中 要添加事务管理器 及其 事务注解驱动,然后才能去使用 事务注解@transactional。

在配置文件中,要添加事务transaction(TX)的命名空间及约束

xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/tx    http://www.springframework.org/schema/tx/spring-tx-4.0.xsd ">

jdbc.properties


jdbc.driverClassName = oracle.jdbc.OracleDriver
jdbc.url = jdbc:oracle:thin:@10.5.1.50:1521:cddev

jdbc.username = base_55demo
jdbc.password = crm_12345

c3p0.pool.size.max=20
c3p0.pool.size.min=5

c3p0.pool.size.ini=3

3、DAO

dao层使用的是jdbcTemplate,它的使用方法就不再讲解,如果有不懂之处,可以去参考 前篇文章

随笔录 之 spring 自学杂记(五) – JDBC

AccountDAO.java类:

package com.wm.spring.tx;

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

@Repository
public class AccountDAO {

    @Autowired
    private JdbcTemplate jdbcTemplate ;

    // 账户 收钱
    public void income(String name, int money){
        String sql = "update demo_account set balance = balance + ? where name = ?";

        jdbcTemplate.update(sql, money, name);

        System.out.println(name + ": 收入增加了  "+money);
    }

    // 账户 转钱
    public void spend(String name, int money) {

        String query = "select balance from demo_account where name = ?";
        Integer balance = jdbcTemplate.queryForObject(query, Integer.class, name);

        if (balance < money) {
            throw new RuntimeException("账户:" + name + "余额不足 ! " + balance + " < " + money);
        }

        String sql = "update demo_account set balance = balance - ? where name = ?";

        jdbcTemplate.update(sql, money, name);

        System.out.println(name + ": 余额减少了  "+money);
    }
}

dao中 实现的事务中的一些对数据的操作。

4、service

service中可以对 方法添加事务的注解@transactional ,至此就具备了事务的功能

AccountSVImpl.java类:

package com.wm.spring.tx;

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

@Service
public class AccountSVImpl {

    @Autowired
    private AccountDAO dao ;

    // 转账 // 账户accB 跟 账号 accA 转钱 money
    // 添加事务注解
    @Transactional
    public void transfer(String accA, String accB, int money){

        dao.income(accA, money); //A的余额增加

        dao.spend(accB, money);  //B的余额减少

    }
}

@transactional 注解是 使一个方法具有事务的特性。

5、测试

TransactionTest.java类:

package com.wm.spring.TEST;

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

import com.wm.spring.tx.AccountSVImpl;

public class TransactionTest {

    private ApplicationContext ac = null ;
    private AccountSVImpl sv = null ;

    @Before
    public void init(){
        ac = new ClassPathXmlApplicationContext("spring-transaction.xml");
        sv = ac.getBean(AccountSVImpl.class);
    }

    @Test
    public void transfer(){

        sv.transfer("JACK", "TOM", 500);  // TOM 向 JACK 转 500元钱

    }
}

测试结果: TOM向JACK转入500元,但是TOM余额只有400,所以 失败。整个事务都要回滚,数据库中的数据并没有改变。


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天涯共明月

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值