• Java JDBC学习实战(三): 事务管理


    一、 数据库的事务特性

    事务是一步或多步组成操作序列组成的逻辑执行单元,这个序列要么全部执行,要么则全部放弃执行。
    事务的四个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(IsoIation)和持续性(Durability)
    原子性(Atomicity):事务应用最小的执行单元,不可再分。是事务中不可再分的最小逻辑执行体。

    一致性(Consistency):事务的执行结果,必须使数据库的从一个一致性的状态变到另一个一致性的状态。

    隔离线(IsoIation):各个事务的执行互不干扰,任意一个事务的内部操作对其他并发的事务,都是隔离的。也就是:并发执行的事务之间不能看到对方的中间状态,并发执行的事务之间不能互相影响。

    持续性(Durability):持续性也称为持久性(Persistence),指事务一旦提交,对数据所做的任何改变,都要记录到永久存储器中,通常就是保存在物理数据库中。


    通常数据库的事务涉及到的语句有:
    一组DML(Data Munipulation Language,数据操作语言)语句,这组DML语句修改后数据将保持较好的一致性;
        操作表的语句,如插入、修改、删除等;
    一个DDL(Data Definition Language,数据定义语言)语句,操作数据对象的语言,有create、alter、drop。
    一个DCL(Data Control Language,数据控制语言)语句,主要有grant、revoke语句。
    DDL和DCL语句最多只能有一个,因为它们都会导致事务的立即提交。

    当事务所包含的全部数据库操作都成功执行后,应该提交事务,使这些修改永久生效。


    事务提交有两种方式:显示提交和自动提交。
    显示提交:使用commit提交
    自动提交:执行DLL或DCL,或者程序正常退出

     
    当事务包含的任意一个数据库操作执行失败后,应该回滚(rollback)事务,使该事务中所作的修改全部失效。
    事务的回滚方式有两种:显示回滚和自动回滚。
    显示回滚:使用rollback
    自动回滚:系统错误或强行退出

     

    二、 JDBC的事务的支持

    JDBC的Connection也支持事务,Connection默认打开自动提交,即关闭事务。
    也就是说,每条SQL语句执行就会立即提交到数据库,永久生效,无法对其进行操作。
    关闭Connection的自动提交,开启事务。Connection的setAutoCommit方法即可:connection.setAutoCommit(false);
    通过connection.getAutoCommit()来获取事务的模式。
    当我们开启事物后,在当前Connection中完成的数据库操作,都不会立即提交到数据库,需要调用Connection的commit方法才行。
    如果有语句执行失败,可以调用rollback来回滚。
    注意:如果Connection遇到未处理的SQLException异常时,系统将非正常退出,系统会自动回滚该事务。
    如果程序捕捉了该异常,则需要在异常处理中显示回滚事务。
     
    Connection提供了设置事务中间保存点的方法:setSavepoint,有2个方法可以设置中间点:
    Savepoint setSavepoint():在当前事务中创建一个未命名的中间点,并返回该中间点的Savepoint对象。
    Savepoint setSavepoint(String name):当前事务中创建一个具有指定名称的中间点,并返回该中间点的Savepoint对象
    通常setSavepoint(String name)设置中间点的名称,事务回滚并不是通过中间点的名称进行回滚的,而是根据中间点对象进行回滚的。
    设置名称只是更好的区分中间点对象,用Connection的rollback(Savepoint savepoint)方法即可完成回滚到指定中间点。
    public class TransactionTest
    {
    	private String driver;
    	private String url;
    	private String user;
    	private String pass;
    	public void initParam(String paramFile)throws Exception
    	{
    		// 使用Properties类来加载属性文件
    		Properties props = new Properties();
    		props.load(new FileInputStream(paramFile));
    		driver = props.getProperty("driver");
    		url = props.getProperty("url");
    		user = props.getProperty("user");
    		pass = props.getProperty("pass");
    	}
    	public void insertInTransaction(String[] sqls) throws Exception
    	{
    		// 加载驱动
    		Class.forName(driver);
    		try(
    			Connection conn = DriverManager.getConnection(url
    				, user , pass))
    		{
    			// 关闭自动提交,开启事务
    			conn.setAutoCommit(false);
    			try(
    				// 使用Connection来创建一个Statment对象
    				Statement stmt = conn.createStatement())
    			{
    				// 循环多次执行SQL语句
    				for (String sql : sqls)
    				{
    					stmt.executeUpdate(sql);
    				}
    			}
    			// 提交事务
    			conn.commit();
    		}
    	}
    	public static void main(String[] args) throws Exception
    	{
    		TransactionTest tt = new TransactionTest();
    		tt.initParam("mysql.ini");
    		String[] sqls = new String[]{
    			"insert into student_table values(null , 'aaa' ,1)",
    			"insert into student_table values(null , 'bbb' ,1)",
    			"insert into student_table values(null , 'ccc' ,1)",
    			// 下面这条SQL语句将会违反外键约束,
    			// 因为teacher_table中没有ID为5的记录。
    			"insert into student_table values(null , 'ccc' ,5)" //①
    		};
    		tt.insertInTransaction(sqls);
    	}
    }


    三、 JDBC的批量更新


    批量更新就是可以同时进行多条SQL语句,将会被作为一批操作被同时执行、同时提交。
    批量更新需要得到数据底层的支持,可以通过调研DataBaseMetaData的supportsBatchUpdates方法来查看底层数据库是否支持批量更新。
    批量更新也需要创建一个Statement对象,然后通过该对象的addBatch方法将多条SQL语句同时收集在一起,
    然后通过Statement对象的executeBatch同时执行这些SQL语句,如下代码:
    Statement sm = conn.createStatement();
    sm.addBatch(sql);
    sm.addBatch(sql2);
    sm.addBatch(sql3);

    //同时执行多条SQL语句
    sm.executeBatch();
    执行executeBatch将返回一个int[]的数组,因为使用Statement执行DDL、DML都将返回一个int的值,
    而执行多条DDL、DML也将返回一个int数组。批量更新中不允许出现select查询语句,一旦出现程序将出现异常。
    如果要批量更新正确、批量完成,需要用单个事务,如果批量更新过程中有失败,则需要用事务回滚到原始状态。
    如果要达到这样的效果,需要关闭事务的自动提交,当批量更新完成再提交事务,如果出现异常将回滚事务。
    然后将连接恢复成自动提交模式。
    public int[] executeBatch(String[] sql) throws SQLException {
        int[] result = null;
        conn = DBHelper.getConnection();
        try {
            //获得当前Connection的提交模式
            boolean autoCommit = conn.getAutoCommit();
            //关闭自动提交模式
            conn.setAutoCommit(false);
            sm = conn.createStatement();
            for (String s : sql) {
                sm.addBatch(s);
            }
            //执行批量更新
            result = sm.executeBatch();
            //提交事务
            conn.commit();
            //还原提交模式
            conn.setAutoCommit(autoCommit);
        } catch (Exception e) {
            e.printStackTrace();
            conn.rollback();
        } finally {
            if (sm != null) {
                sm.close();
            }
            DBHelper.close();
        }
        return result;
    }

    想了解JDBC的总结,可查看这篇文章:http://www.cnblogs.com/hoojo/archive/2011/06/10/2077643.html

  • 相关阅读:
    朋友圈、浏览器分享实现
    Cannot read property 'appendChild' of null
    Nginx报错:Sorry, the page you are looking for is currently unavailable. Please try again later.
    RGBA(0,0,0,0)调色
    windows下 python中报错ImportError: No module named 'requests'
    一起谈.NET技术,C# 基础概念之延迟加载 狼人:
    一起谈.NET技术,为类提供软件约定 狼人:
    一起谈.NET技术,asp.net控件开发基础(6) 狼人:
    一起谈.NET技术,详解.NET程序集的加载规则 狼人:
    一起谈.NET技术,Linq学习笔记 狼人:
  • 原文地址:https://www.cnblogs.com/hehe520/p/6330015.html
Copyright © 2020-2023  润新知