Day 38 学习分享 - JDBC批处理与事务

本文详细探讨了JDBC批处理的基本概念和代码实现,通过实例展示了如何进行批处理以提升性能。同时,文章深入讲解了事务管理,包括事务的概念、开启和回滚事务的方法,以及ACID特性。还分析了不考虑事务隔离级别可能导致的问题,如脏读、不可重复读和幻读,并介绍了数据库的事务隔离级别及其在MySQL中的应用。
摘要由CSDN通过智能技术生成
1. JDBC批处理
1.1 基本概念

批量处理允许将相关的SQL语句分组到批次中处理, 并通过对数据库的一次调用提交他们.

​ 当一次向数据库发送多个SQL语句时, 可以减少连接数据库的开销, 从而调高性能

​ 批处理实现方式:

PreparedStatement:
	1. 使用占位符创建SQL语句
	2. 使用preparedStatement() 方法创建PreparedStatement对象
	3. 使用setAutoCommit() 将auto-commit设置为false
	4. 使用addBatch() 方法在创建的语句对象上添加需求的SQL语句到批处理中
	5. 在创建的语句对象上使用executeBatch() 方法执行所有SQL语句
	6. 使用commit()方法提交所有更改
1.2 代码演示:
/*
 * PrepareStatement对象进行批处理
 */
public class Demo1 {

	@Test
	public void test1(){
		
		List<Employees> list=new ArrayList<Employees>();
		list.add(new Employees(206, "tom1", "123", 123));
		list.add(new Employees(207, "tom2", "123", 123));
		list.add(new Employees(208, "tom3", "123", 123));
		list.add(new Employees(209, "tom4", "123", 123));
		list.add(new Employees(210, "tom5", "123", 123));
		list.add(new Employees(211, "tom6", "123", 123));
		list.add(new Employees(212, "tom7", "123", 123));
		
		Connection conn=JdbcUtils.openConn();
		try {
			conn.setAutoCommit(false); //关闭自动提交
			String sql="INSERT INTO Employees (id, first, last, age) VALUES(?,?,?,?)";
			PreparedStatement ps = conn.prepareStatement(sql);
			
			for (Employees e : list) {
				ps.setObject(1, e.getId());
				ps.setObject(2, e.getFirst());
				ps.setObject(3, e.getLast());
				ps.setObject(4, e.getAge());
				ps.addBatch();
			}
			int[] arrays = ps.executeBatch(); //执行批处理
			conn.commit(); //手动提交
			System.out.println("执行完毕:"+Arrays.toString(arrays));
			
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}
1.3 总结
优点:
	发送的是预编译后的SQL语句,执行效率高
缺点:
	只能应用在SQL语句相同,但参数不同的批处理中.因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据
	
注意:
	JDBC的批处理不能加入select语句,否则会抛异常!
2. 事务
2.1 事务的概念
事务:
	逻辑上的一组操作, 组成这组操作的各个单元, 全部成功或者全部失败

例如:
	A-->B转账
		update from account set money=money+100 where name='B';
		update from account set money=money-100 where name='A';
	这两条SQL语句要么同时成功 要么同时失败, 这种组操作就是事务!
2.2 事务的语法
2.2.1 在MySQL数据库中开启事务
start transaction - 开启事务
commit - 提交事务
trollback - 回滚事务
2.2.2 在JDBC中开启事务
  • 模拟转账成功时的业务场景

    public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement st = null;
    
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			conn = DriverManager.getConnection("jdbc:mysql://192.168.132.128:3306/employeesdb?useUnicode=true&characterEncoding=utf-8", "root", "123");
    			
    			conn.setAutoCommit(false);//通知数据库开启事务(start transaction)
    			String sql1 = "update account set money=money-100 where name='A'";
    			st = conn.prepareStatement(sql1);
    			st.executeUpdate();
    			String sql2 = "update account set money=money+100 where name='B'";
    			st = conn.prepareStatement(sql2);
    			st.executeUpdate();
    			conn.commit();//上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
    			System.out.println("成功!!!");  //
    			
    		} catch (Exception e) {
    			try {
    				conn.rollback();
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    			e.printStackTrace();
    		} 
    	}
    
  • 模拟转账过程中出现异常导致有一部分SQL执行失败后让数据库自动回滚事务

    public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement st = null;
    
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			conn = DriverManager.getConnection("jdbc:mysql://192.168.132.128:3306/employeesdb?useUnicode=true&characterEncoding=utf-8", "root", "123");
    
    			conn.setAutoCommit(false);// 通知数据库开启事务(start transaction)
    			String sql1 = "update account set money=money-100 where name='A'";
    			st = conn.prepareStatement(sql1);
    			st.executeUpdate();
    			// 用这句代码模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交,此时数据库会自动执行回滚操作
    			int x = 1 / 0;
    			String sql2 = "update account set money=money+100 where name='B'";
    			st = conn.prepareStatement(sql2);
    			st.executeUpdate();
    			conn.commit();// 上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
    			System.out.println("成功!!!");
    
    		} catch (Exception e) {
    			e.printStackTrace();
    		}
    	}
    
  • 模拟转账过程中出现异常导致有一部分SQL执行失败后让数据库手动回滚事务

    public static void main(String[] args) {
    		Connection conn = null;
    		PreparedStatement st = null;
    
    		try {
    			Class.forName("com.mysql.jdbc.Driver");
    			conn = DriverManager.getConnection("jdbc:mysql://192.168.132.128:3306/employeesdb?useUnicode=true&characterEncoding=utf-8", "root", "123");
    
    			conn.setAutoCommit(false);// 通知数据库开启事务(start transaction)
    			String sql1 = "update account set money=money-100 where name='A'";
    			st = conn.prepareStatement(sql1);
    			st.executeUpdate();
    			// 用这句代码模拟执行完SQL1之后程序出现了异常而导致后面的SQL无法正常执行,事务也无法正常提交,此时数据库会自动执行回滚操作
    			int x = 1 / 0;
    			String sql2 = "update account set money=money+100 where name='B'";
    			st = conn.prepareStatement(sql2);
    			st.executeUpdate();
    			conn.commit();// 上面的两条SQL执行Update语句成功之后就通知数据库提交事务(commit)
    			System.out.println("成功!!!");
    
    		} catch (Exception e) {
    			try {
                   //捕获到异常之后手动通知数据库执行回滚事务的操作
    				conn.rollback();
    				System.out.println("程序回滚啦");
    			} catch (SQLException e1) {
    				e1.printStackTrace();
    			}
    			e.printStackTrace();
    		}
    	}
    
2.3 设置事务回滚点
	在开发中,有时候可能需要手动设置事务的回滚点,在JDBC中使用如下的语句设置事务回滚点:
	Savepoint sp = conn.setSavepoint();
	Conn.rollback(sp);
	Conn.commit();//回滚后必须通知数据库提交事务
public static void main(String[] args) {

		Connection conn = null;
		PreparedStatement st = null;
		Savepoint sp = null;	

		try {
			Class.forName("com.mysql.jdbc.Driver");
			conn = DriverManager.getConnection("jdbc:mysql://192.168.132.128:3306/employeesdb?useUnicode=true&characterEncoding=utf-8", "root", "123");

			conn.setAutoCommit(false);// 通知数据库开启事务(start transaction)

			String sql1 = "update account set money=money-100 where name='A'";
			st = conn.prepareStatement(sql1);
			st.executeUpdate();

			// 设置事务回滚点
			sp = conn.setSavepoint();

			String sql2 = "update account set money=money+100 where name='B'";
			st = conn.prepareStatement(sql2);
			st.executeUpdate();

			// 程序执行到这里出现异常,后面的sql3语句执行将会中断
			int x = 1 / 0;

			String sql3 = "update account set money=money+100 where name='C'";
			st = conn.prepareStatement(sql3);
			st.executeUpdate();

			conn.commit();
		} catch (Exception e) {
			try {
                /**
                 * 我们在上面向数据库发送了3条update语句,
                 * sql3语句由于程序出现异常导致无法正常执行,数据库事务而已无法正常提交,
                 * 由于设置的事务回滚点是在sql1语句正常执行完成之后,sql2语句正常执行之前,
                 * 那么通知数据库回滚事务时,不会回滚sql1执行的update操作
                 * 只会回滚到sql2执行的update操作,也就是说,上面的三条update语句中,sql1这条语句的修改操作起作用了
                 * sql2的修改操作由于事务回滚没有起作用,sql3由于程序异常没有机会执行
                 */
                conn.rollback(sp);//回滚到设置的事务回滚点
                //回滚了要记得通知数据库提交事务
                conn.commit();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
		}
	}
2.4 事务的4大特性 ACID
原子性 Atomicity:
	原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败.
	比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败!
	
一致性 Consistency
	官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态.
	以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性
	
隔离性 Isolation
	事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离
	
持久性 Durability
	持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
2.5 不考虑隔离级别可能会引发的问题
2.5.1 脏读
	脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中
	这时,另外一个事务也访问这个数据,然后使用了这个数据

例如:
	A工资为1000,事务A中把他的工资减去100,再把B的工资加上100,但事务A尚未提交
	与此同时,事务B正在查询工资表,读取到A的工资为900,B的工资为1100
	随后,事务A发生异常,而回滚了事务.A的工资又回滚为1000,B的工资也回滚为1000
	最后,最终事务B读取到A的工资为900,B的工资为1100,事务B做了一次脏读
	
	脏读最严重的事情、读取数据是不真实的!
2.5.2 不可重复读
	一个事务在读取某一项数据的时候, 多次重复读取到的数据不同, 就叫做不可重复读
	
例如:
	事务A中有读取某项数据N次的命令, 事务B对事务A所读取的数据做出了修改操作, 导致事务A在本次事务中读取到的这项数据值发生了改变, 就出现了不可重复读错误.
2.5.3 虚读(幻读)
	一个事务A在读取某项数据的时候另一个事务B对数据的结构做出了修改, 导致A事务读取到的数据条数改变, 就是虚读
2.6 数据库的事务隔离级别
2.6.1 隔离级别类型

在这里插入图片描述

注意:
	事务的隔离级别和数据库并发性是成反比的,隔离级别越高,并发性越低
2.6.2 MySQL相关API
select @@tx_isolation  
	查询当前事务隔离级别
	
select @@global.tx_isolation 
	查看系统会话隔离级别
	
set session transaction isolatin level repeatable read 
	设置当前会话隔离级别
	
set global transaction isolatin level repeatable read 
	设置系统会话隔离级别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值