• 事务模板


    为什么提供事务模板

    截至到现在为止,除非你使用 dao.execute(Sql ...) ,一次执行多个 SQL,是事务安全的,其他的情况 均是事务不安全的,比如如下代码:

    Pet pet1 = dao.fetch(Pet.class,"XiaoBai");
    Pet pet2 = dao.fetch(Pet.class,"XiaoHei");
    
    pet1.setNickname("BaiBai");
    pet2.setNickname("HeiHei");
    
    dao.update(pet1);
    dao.update(pet2);
    

    尤其是请关注最后两句:

    dao.update(pet1);
    dao.update(pet2);
    

    当第二句话抛出异常的时候,第一句话不能被回滚。这两条调用就是不事务安全的。如果我想让 pet1 和 pet2 的更新操作是原子性的,它们必须一同成功,一同失败,怎么办呢?

     

    使用事务模板

    Nutz.Dao 提供了简单的解决办法: 事务模板

     

    一段示例代码

    上一节的例子可以修改为:

    final Pet pet1 = dao.fetch(Pet.class,"XiaoBai");
    final Pet pet2 = dao.fetch(Pet.class,"XiaoHei");
    
    pet1.setNickname("BaiBai");
    pet2.setNickname("HeiHei");
    // Begin transaction        
    Trans.exec(new Atom(){
        public void run() {
            dao.update(pet1);
            dao.update(pet2);
        }
    });
    // End transaction
    

    提供一个 org.nutz.trans.Atom 接口的匿名实现,在里面执行的所有的 Dao 操作都是原子性的,因为它们在 同一个 “原子 (Atom)” 里。

     

    事务的关键就是原子的界定

    事务最核心的是原子的界定,在 Nutz.Dao中,界定原子的方法出奇的简单,借助匿名类,你可以随时将一段 代码用你的原子实现包裹住。而 Trans.exec() 方法接受数目可变的原子,每个原子都是事务性的。

    Trans.exec 的函数声明

    public static void exec(Atom... atoms);
    

    被原子实现包裹住的代码就是事务安全的,无论它同时操作了多少个 DataSource。 Nutz.Dao 提供的原子接口非常简单,实际上它就是 java.lang.Runnable 的一个别名,下面就是这个接口的 全部代码:

    package com.zzh.trans;
    public interface Atom extends Runnable {}
    

    这几乎是我写过的最简单的 Java 类了,正是因为它简单,所以才有无限的威力。你如果查看过 Nutz 的源代 码包,在和数据库操作的地方,你总会和 Atom 不期而遇。很多朋友曾经很不适应匿名类的写法,是的,我在 早期写 Java 的时候也比较讨厌匿名类,但是熟悉了以后,你会真正喜欢上这个东西,就像你写 Javascript 的 一段时候以后,多数人都会喜欢上“闭包”一样。你可以把匿名类当作 Java 给你的闭包。

    采用事物模板的来界定事物有一个缺点,这是 Java 语言带来的限制:你有可能需要将一些相关的变量声明成 final 的。 并且在 run 函数中,你只能向外抛 RuntimeException 或其子类。

     

    设置事务的级别

    在 JDBC 的 java.sql.Connection 接口中定义的 setTransactionIsolation 函数可以设置事务的级别 Nutz.Dao 也提供另外一个静态函数,允许你设置事务的级别:

    Trans.exec 的函数声明

    public static void exec(int level, Atom... atoms);
    

    这里的第一个参数 level 和 java.sql.Connection 接口中的 setTransactionIsolation 规定的 level 是一样的。下面 是在 java.sql.Connection 里面关于 level 参数的 JDoc 说明:

    它可以是下列常量中的任意一个值:

    • Connection.TRANSACTION_READ_UNCOMMITTED
    • Connection.TRANSACTION_READ_COMMITTED
    • Connection.TRANSACTION_REPEATABLE_READ
    • Connection.TRANSACTION_SERIALIZABLE

    注意: 不能使用常量 Connection.TRANSACTION_NONE,因为它的意思是“不支持事务”

    关于 level 参数的更多说明,请参看 java.sql.Connection 的文档

    不同的数据库,对于 JDBC 事务级别的规范,支持的力度不同。请参看相应数据库的文档,已 确定你设置的数据库事务级别 是否被支持。

     

    事务的嵌套

    Nutz 的事务模板可以嵌套吗? 答案是肯定的。事实上,Nutz 支持事务模板的无限层级嵌套。

    这里,如果每一层嵌套,指定的事务级别有所不同,不同的数据库,可能引发不可预知的错误。所以, 嵌套的事务模板的事务,将以最顶层的事务为级别为标准。就是说,如果最顶层的事务级别为 'TRANSACTION_READ_COMMITTED',那么下面所 包含的所有事务,无论你指定什么样的事务级别,都是 'TRANSACTION_READ_COMMITTED', 这一点,由抽象类 Transaction 来保证。 其 setLevel 当被设置了一个大于 0 的整数以后,将不再 接受任何其他的值。

    你可以通过继承 Transaction 来修改这个默认的行为,当然,这个行为修改一般是没有必要的。

    另外,你还可能需要知道,通过 Trans.setup 方法,能让整个虚拟机的 Nutz 事务操作都使用你的 Transaction 实现

    下面我给出两个例子:

    最外层模板决定了整个事务的级别:

    Trans.exec(Connection.TRANSACTION_READ_COMMITTED, new Atom(){
        public void run(){
            dao.update(xxx);
            dao.update(bbb);
    
            // 在下层模板,虽然你指定了新的事务级别,但是这里的事务级别还是
            // 'TRANSACTION_READ_COMMITTED'。在一个事务中,级别一旦设定就不可更改
            Trans.exec(Connection.TRANSACTION_SERIALIZABLE, new Atom(){
                public void run(){
                    dao.update(CCC);
                    dao.update(EEE);
                }
            });
        }
    });
    

    让整个函数都是事务的:

    public void updatePet(final Pet pet){
        Trans.exec(new Atom(){
            public void run(){
                dao.update(pet);
                dao.update(pet.getMaster());
            }
        });
    }
    
    // 在另外一个函数里,可以这么使用
    public void updateDogAndCat(final Pet dog, final Pet cat){
        Trans.exec(new Atom(){
            public void run(){
                updatePet(dog);
                updatePet(cat);
            }
        });
    }
    
     

    扩展实现

    org.nutz.trans.Trans 类的 exec()方法,接受数目可变的 Atom 实例,足够方便了吧。 但是它默认只能支 持在一台机器上保证事务性,就是在一个 JVM 里保证代码的事务性。如果跨越多个JVM一起组合的 Service, 如何保证事务性呢,很抱歉,Nutz.Dao 的第一版的实现里不包括跨越多个JVM依然保证事务性的功能,但是 你如果真的需要这个功能也没关系,你可以自己写一个 org.nutz.trans.Transaction 的实现,然后在你的应 用启动时,通过

    org.nutz.trans.Trans.setup(你的实现类)
    

    替换 Nutz.Dao 的默认实现。

     

    总结一下 Nutz.Dao 事务

    • org.nutz.trans.Trans 类提供了两个函数 exec
      • 一个接受数目可变的 Atom 对象
      • 一个接受一个整型值用以界定本事务的级别,以及一个数目可变的 Atom 对象
    • Atom 类就是 java.lang.Runnable 的一个别名
    • 在一个 Atom 里,无论同时操作多少 DataSource,都是事务安全的(由于不是使用XADataSource,无法100%保证)
    • 你可以通过实现自己的 Transaction 实现类,扩展 Nutz.Dao 对于事务的支持
  • 相关阅读:
    C++栈(stack)、队列(queue)、链表(list)的常用函数
    C++中cin>>a原理
    C++中vector和堆的常用使用方法&例题:数据流中的中位数
    使用centos8搭建僵尸毁灭工程(PZ)服务器
    【从C#走进Python】四、装饰器
    【从C#走进Python】三、变量声明
    【从C#走进Python】二、迭代器
    【从C#走进Python】一、上下文管理器
    【C#基础】拥抱Lambda(2):表达式树与LINQ
    【机器学习笔记】Python机器学习基本语法
  • 原文地址:https://www.cnblogs.com/telwanggs/p/7729519.html
Copyright © 2020-2023  润新知