暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

AntDB 数据库驱动使用说明-基于JDBC4

tocata 2024-09-02
217

子事务处理

在数据库事务中,顶层事务控制着各个层次的事务,而嵌套在顶层事务下的事务被称为子事务。子事务控制着每个局部的变化,它可以提交也可以回滚,但其提交操作并不马上生效,只有在其父事务提交后才真正提交。同样,任意一个事务的回滚也会引起它的所有子事务一同回滚。JDBC 支持子事务功能,通过两个设置 autosave 和 cleanupSavepoints 来限制子事务的回滚和提交。autosave 参数控制子事务保存点的自动设置与自动回滚,cleanupSavepoints 参数控制子事务保存点的自动清理。

参数配置

参数组合
  • 不指定 autosave 和 cleanupSavepoints,默认 autosave 为 never,cleanupSavepoints 为 false。

  • 当 autosave 参数设置为 never ,cleanupSavepoints 参数可以为 ture 和 false 任意状态。

  • 当 autosave 参数设置为 always 或 conservative 时,cleanupSavepoints 参数只能设置为 true ,否则通过JDBC与数据库连接建立失败,出现报错:

    net.antdb.util.PsOLException: Invalid parameter value, when autosave is enabled, cleanupsavepoints must be true .
    
  • 子事务处理参数配置 url 连接串示例:

    不指定autosave和cleanupSavepoints,默认autosave为never,cleanupSavepoints为false
    jdbc:antdb://${ip}:${port}/${dbname}
    指定autosave和cleanupSavepoints(示例)
    jdbc:antdb://${ip}:${port}/${dbname}?autosave=always&cleanupSavepoints=true
    

参数说明

参数定义

autosave (string) default never:指定驱动程序在查询失败时应该采取的行为,不指定该参数时默认autosave = never。

  • autosave = always:每次执行查询之前都会设置一个保存点,并在查询出现异常时自动回滚到该保存点。

  • autosave = never:不设置保存点。执行出现异常时不执行回滚操作。

  • autosave=conservative:仅在特定情况下每次执行查询之前都会设置一个保存点,执行自动回滚。例如,当发生像"缓存的语句不能更改返回类型"或"语句 XXX 无效"这样的错误时才会执行回滚操作。

cleanupSavepoints (boolean) default false:清理保存点,决定在自动保存模式下创建的 SAVEPOINT 是否在语句之前释放。不指定该参数时默认 cleanupSavepoints = false。

  • cleanupSavepoints = true:在事务提交或回滚后,自动清理先前设置的保存点。

  • cleanupSavepoints = false:不自动清理保存点,保存点将保留在事务结束后。

参数设置建议
  • autosave 参数:建议用户设置为 always。

  • cleanupSavepoints 参数:建议设置为 true。

子事务相关参数使用示例

下述示例验证了autosave = always,cleanupSavepoints = true 时查询失败能够回滚到自动创建的保存点并能继续执行接下来操作,父事务不中断,只回滚该条失败的子事务,并且不影响前面已提交的子事务的场景。

测试程序代码:

import java.sql.*;

public class test {
	//定义连接参数。
    public static void main(String[] args) throws SQLException {
        String url1 = "jdbc:antdb://10.20.16.227:32003/postgres?autosave=always&cleanupSavepoints=true";
        //不指定 autosave 和 cleanupSavepoints
        String url2 = "jdbc:antdb://10.20.16.227:32003/postgres;
		/**
    	*验证 autosave=always,cleanupSavepoints=true 的情况下,插入数据异常(主键冲突),子事务能回滚到自动创建的保存点并能继续执行子事务操作,事务不中断。
    	*/
		test001(url1);
	}
	//插入数据异常(主键冲突),子事务执行失败,回滚到查询前自动创建的保存点。
	//创建数据库连接。
	public static void test001(String url) throws SQLException {
        String user = "mengzp";
        String password = "xxx";
        Connection conn = null;
        try {
            System.out.println("connect start");
            //创建数据库连接。
            conn = DriverManager.getConnection(url, user, password);
            //关闭自动提交,方便进行子事务测试。
            conn.setAutoCommit(false);
            System.out.println("connect ok");
            /**
            * 创建表test(c1字段设置为主键)
            * 向表test中插入10条数据
            */
            PreparedStatement psmt = conn.prepareStatement("drop table if exists test cascade");
            psmt.execute();
            System.out.println("drop ok");
            psmt = conn.prepareStatement("create table test(c1 int not null primary key, c2 text)");
            psmt.execute();
            System.out.println("create ok");
            psmt = conn.prepareStatement("insert into test values (1, 'a'),(2,'你好'),(3,'c'),(4,'测试'),(5,'e'),(6,'10'),(7,'g'),(8,'写作'),(9,'i'),(10,'j')");
            psmt.execute();
            System.out.println("insert ok");
            //数据插入成功
            psmt = conn.prepareStatement("select * from test order by c1");
            ResultSet rs = psmt.executeQuery();
            while (rs.next()) {
                System.out.println("c1="+rs.getInt(1)+";c2="+rs.getString(2));
            }
            System.out.println("select ok");
            try {
            	//模拟插入数据异常(主键冲突),子事务执行失败,回滚到查询前自动创建的保存点。
                psmt = conn.prepareStatement("insert into test values (1, 'a'),(11,'你好'),(12,'c')");
                psmt.executeQuery();
            } catch (SQLException ex) {
                ex.printStackTrace();
                System.out.println(ex.getStackTrace());
                System.out.println(ex.getMessage());
                System.out.println("insert fail");
            }
            psmt.close();
            //提交事务。因为在服务端可以连接数据库,可以查询到test表,test表包含十条数据。
            conn.commit();
            conn.close();
            System.out.println("close ok");
        } catch (SQLException ex) {
            ex.printStackTrace();
            System.out.println(ex.getStackTrace());
            System.out.println(ex.getMessage());
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
    }
}
import java.sql.*;

public class test {
	//定义连接参数。
    public static void main(String[] args) throws SQLException {
        String url2 = "jdbc:antdb://10.20.16.227:32003/postgres;
		//对比验证:不设置 autosave,cleanupSavepoints 参数默认关闭情况下,插入数据异常(主键冲突),事务中止,提交事务后父事务回滚。
		test002(url2);
	}
    //插入数据异常(主键冲突)导致事务中断,子事务执行失败,回滚整个事务。
    //创建数据库连接。
    public static void test002(String url) throws SQLException {
        String user = "mengzp";
        String password = "xxx";
        Connection conn = null;
        try {
            System.out.println("connect start");
            //创建数据库连接。
            conn = DriverManager.getConnection(url, user, password);
            //关闭自动提交,方便进行子事务测试。
            conn.setAutoCommit(false);
            System.out.println("connect ok");
            /**
            * 创建表test(c1字段设置为主键)
            * 向表test中插入10条数据
            */
            PreparedStatement psmt = conn.prepareStatement("drop table if exists t2 cascade");
            psmt.execute();
            System.out.println("drop ok");
            psmt = conn.prepareStatement("create table t2(c1 int not null primary key, c2 text)");
            psmt.execute();
            System.out.println("create ok");
            psmt = conn.prepareStatement("insert into t2 values (1, 'a'),(2,'你好'),(3,'c'),(4,'测试'),(5,'e'),(6,'10'),(7,'g'),(8,'写作'),(9,'i'),(10,'j')");
            psmt.execute();
            System.out.println("insert ok");
            //数据插入成功
            psmt = conn.prepareStatement("select * from t2 order by c1");
            ResultSet rs = psmt.executeQuery();
            while (rs.next()) {
                System.out.println("c1="+rs.getInt(1)+";c2="+rs.getString(2));
            }
            System.out.println("select ok");
            try {
            	//模拟插入数据异常(主键冲突),子事务执行失败,事务中止。
                psmt = conn.prepareStatement("insert into t2 values (1, 'a'),(11,'你好'),(12,'c')");
                psmt.executeQuery();
            } catch (SQLException ex) {
                ex.printStackTrace();
                System.out.println(ex.getStackTrace());
                System.out.println(ex.getMessage());
                System.out.println("insert fail");
            }
            psmt.close();
            //提交事务。由于主键冲突导致事务中断,未创建保存点,整个事务回滚,表t2未创建成功,在服务端连接数据库查询不到t2表。
            conn.commit();
            conn.close();
            System.out.println("close ok");
        } catch (SQLException ex) {
            ex.printStackTrace();
            System.out.println(ex.getStackTrace());
            System.out.println(ex.getMessage());
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
    }
}

验证程序运行结果:

test001:test 表创建成功,并成功插入十条数据。
test002:t2 表创建失败。
[mengzp@host227 ~]$ adb -d postgres -p 32003
psql (6.3.20 based on PG 13.3)
Type "help" for help.

antdb=# \dt
       List of relations
 Schema | Name | Type  | Owner
--------+------+-------+--------
 public | test | table | mengzp
(1 row)

antdb=# select * from test order by c1;
 c1 |  c2
----+------
  1 | a
  2 | 你好
  3 | c
  4 | 测试
  5 | e
  6 | 10
  7 | g
  8 | 写作
  9 | i
 10 | j
(10 rows)


「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论