• iBatis第五章:事务管理


    ---------------------------- 1、什么是事务 ------------------------------

    什么是事务?


    需要注意的是,事务的概念不是针对某个特定的数据库的,而是针对所有数据库都需要遵循的一项原则。
    我们先来看一个经典的示例,将设有两个银行账户,他们之间实现转账业务,在这个时候就需要有事务控制,否则将可能出现不可预期的错误。
    初始余额信息如下:


    1、假设A要给B转账 $1000 ,正常情况下应该是下面介过样子滴:


    2、可是,情况可能会出现下面这样不尽人意滴:

    出现这种情况的原因是,A用户去银行给B转账1000,A操作成功了,但是在B银行正在准备将A转过来的钱存入B的账户时,这个时候发生了异常,比如突然断电了,或者由于程序错误原因,导致钱没存入到B账户中,就发生了这样的一幕。

    所以,可见我们通过事务来控制是多么的重重要,它能保证我们的数据处于一致的状态,达到如下的效果:


    这主要是用到的事务的特性,将A转账和向B存款放在同一个事务中,如果其中某一步发生了错误,事务将进行回滚(即一旦B存款发生的错误,A的转账也失效,将A的钱回退回去),达到操作前得一个正确状态。

    ---------------------------- 2、事务的特性 ------------------------------
    既然我们需要使用事务,那么我们首先的搞清楚事务具有哪些特性,只有熟悉了这些特性,我们才能更好的根据需要来使用事务管理我们的应用。

     

    1、原子性:保证事务中的所有步骤属于同一个整体,要么全部成功,要么全部失败。
    2、一致性:指数据需要处于一致的状态。
    3、隔离性:由于数据库中的资源处于一个共享的状态,同一时刻可能有多个用户操作,可能造成数据紊乱的状态,所以需要通过隔离特性来控制,在某一时刻只能有一个用户操作,当这个用户操作完成后再让其他用户操作,以此保证数据的准确性。
    4、持久性:数据库主要提供了数据持久化的功能,一旦某个操作成功提交之后,这些数据就处于一个持久化的状态了,处于一个安全的状态。

    ---------------------------- 3、自动事务、局部事务和全局事务 ------------------------------
    3.1、自动事务:
    对于我们执行一条SQL语句,这个时候一般不需要通过我们显示的管理事务,单其实事务依然是存在的,是数据库提供的一项隐式管理功能,它不需要我们显示的进行开始事务、提交事务等操作。这样事务称为自动事务管理。
    需要注意的是:如果我们没显示的对事务进行管理,数据库就会为我们提供自动事务处理机制,无论我们的SQL语句是怎么样的。

    3.2、局部事务

    其实局部事务和全局事务主要从业务的范围上存在不同,例如我们进行本行转账,就可以通过局部事务控制,因为整个流程只涉及到一个应用程序和一个数据库,没有痛其他应用程序交互,在业务范围上相对较小。

    3.3、全局事务
    全局事务处理的是一个比较宽泛的业务逻辑,它可能跨越多个应用系统以及应用程序,使用逻辑与局部事务管理雷同,只是范围更加宽而已。

    通过全局事务将A银行转账和B银行进账控制在同一个事务中,如果A和B都成功了才提交,只要有一方发生异常即回滚事务,整个流程失败,以此来控制数据的一致性。

    ---------------------------- 4、自定义事务 ------------------------------
    自定义事务可以显示的控制事务,下面以一个银行转账的例子来说明:
    1、定义一个账户实体
    /**
    * @author Administrator
    * 账户实体类
    */
    public class Account {

    private int id;

    private String account;

    private int money;

    public int getId() {
    return id;
    }

    public void setId(int id) {
    this.id = id;
    }

    public String getAccount() {
    return account;
    }

    public void setAccount(String account) {
    this.account = account;
    }

    public int getMoney() {
    return money;
    }

    public void setMoney(int money) {
    this.money = money;
    }
    }

    2、编写SQL配置文件
    <sqlMap>

    <!-- 给类定义别名,简化代码 -->
    <typeAlias alias="account" type="com.test.bean.Account"/>

    <!-- 查询 -->
    <select id="account_select" parameterClass="java.lang.String" resultClass="account">
    select account,money from account where account=#account#
    </select>

    <!-- 更新 -->
    <update id="account_update" parameterClass="account">
    update account set money=#money# where account=#account#
    </update>

    </sqlMap>

    3、测试事务控制
    package com.test.dbutil;

    import java.sql.SQLException;

    import com.ibatis.sqlmap.client.SqlMapClient;
    import com.test.bean.Account;

    public class TestAccount {

    /**
    * 测试事务控制转账
    * @param args
    */
    public static void main(String[] args) {
    // TODO Auto-generated method stub

    TestAccount t = new TestAccount();
    try {
    t.exchange1("A", "C", 500);
    } catch (SQLException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }
    }

    /**
    * 实现转账功能【已经事务控制】
    * @param aname
    * @param bname
    * @param money
    * @throws SQLException
    */
    private void exchange(String aname,String bname, int money) throws SQLException{

    SqlMapClient sqlMapClient = BaseDAO.getInstance();

    try {
    //1、开始事务
    sqlMapClient.startTransaction();

    /** 具体的事处理 **/
    //a、到数据库查询出两个账户
    Account a = (Account) sqlMapClient.queryForObject("account_select", aname);
    Account b = (Account) sqlMapClient.queryForObject("account_select", bname);

    if(a != null && b != null){
    //b、设置账户的金额
    a.setMoney(a.getMoney() - money); //a账户减少金额
    b.setMoney(b.getMoney() + money); //b账户增加金额

    //c、执行转账功能
    int ak = sqlMapClient.update("account_update", a);
    int bk = sqlMapClient.update("account_update", b);
    if(ak != 1 || bk != 1){
    throw new SQLException("转账失败");
    }

    }else{
    throw new SQLException("输入的账户信息有误");
    }

    System.out.println("转账成功:账户"+aname+"已经成功向账户"+bname+"转账。 转账金额:"+money);
    //2、提交事务
    sqlMapClient.commitTransaction();

    }finally{
    //3、结束事务
    sqlMapClient.endTransaction();
    }
    }

    /**
    * 转行功能【未做事务控制】
    * @param aname
    * @param bname
    * @param money
    * @throws SQLException
    */
    private void exchange1(String aname,String bname, int money) throws SQLException{

    SqlMapClient sqlMapClient = BaseDAO.getInstance();

    try {
    //a、到数据库查询出两个账户
    Account a = (Account) sqlMapClient.queryForObject("account_select", aname);
    Account b = (Account) sqlMapClient.queryForObject("account_select", bname);

    //a账户执行转账
    a.setMoney(a.getMoney() - money);
    sqlMapClient.update("account_update", a);

    //b账户执行金额进账
    b.setMoney(b.getMoney() + money);
    sqlMapClient.update("account_update", b);


    System.out.println("转账成功:账户"+aname+"已经成功向账户"+bname+"转账。 转账金额:"+money);
    }catch(Exception e){
    throw new SQLException("转账失败");
    }
    }
    }


    通过测试我们可以发现,通过事务控制的,不会发生数据不一致的情况,二不通过事务控制的在发生异常的情况下不能保证数据的正确性。

  • 相关阅读:
    java代码连接数据库
    phpcms v9 读取地区联动菜单缓存文件
    PHPCMS V9二次开发便捷自定义后台入口文件夹
    phpcms v9中模板标签使用及联动菜单
    Phpcms v9系统类库与函数库调用方法
    phpcms v9 二次开发
    phpcms v9开源开发框架基础mvc解读
    phpcms插件开发初步规范
    phpcms v9二次开发之模型类的应用(1)
    phpcms v9二次开发之模型类的应用(2)
  • 原文地址:https://www.cnblogs.com/funnyboy0128/p/7647385.html
Copyright © 2020-2023  润新知