JAVA学习笔记——数据库事务

写在开头:本文是学习尚硅谷JavaWeb的个人笔记,便于自己后期复习,也供各位参考评论,指出错误共同进步。

1 什么叫数据库事务?

事务:一组逻辑单元操作单元,使数据从一种状态转换到另一种状态。(将AA的账户余额转100到B的账户上。这样要经过两个update操作,使得balances由原来的1000、1000转变为900、1100,这就是数据库事务。)

一组逻辑操作单元:一个或多个DML(数据的增删改查)

在这里插入图片描述
事务处理原则
1. 保证所有事务都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时 ,要么所有的事务都被提交(commit),那么这些修改就永久地保存下来;要么数据库管理系统将放弃所作的所有修改,整个事务回滚(**rollback)**到最初状态。

和上面例子一样,要么两人(AA、BB)数字都改变,如果出现异常就回滚,两者都不改变。

2.数据一旦提交,就不可回滚。(我们要做的操作就是不能让数据提交,后面再提交)
3.哪些操作会导致数据的自动提交?

(1)DDL操作一旦执行,都会自动提交。(DLL操作是对数据表的创建create、修改alter、删除drop等操作),set autocommit = false 对DDL操作失效(反正他就是要自动提交)。
(2)DML默认情况下,一旦执行,就会自动提交。(数据的增删改查),(这里解决办法是我们可以通过set autocommit = false的方式取消DML操作的自动提交。)
(3)默认在关闭连接时,会自动的提交数据。(这里解决办法就是:让两个事务用一个连接conn,整个转账操作做完之后在关闭连接)

为了让多个 SQL 语句作为一个事务执行:

  • 1、调用 Connection 对象的 setAutoCommit(false); 以取消自动提交事务
  • 2、在所有的 SQL 语句都成功执行后,调用 commit(); 方法提交事务
  • 3、在出现异常时,调用 rollback(); 方法回滚事务
  • 4、若此时 Connection 没有被关闭, 则需要恢复其自动提交状态

实例一未考虑数据库事务的转账操作
(问题所在:如果程序现故障可能得到的结果就是AA转账成功但是BB没有收到)

public class TransactionTest {
	/**********************未考虑数据库事务的转账操作***************
	 * 针对数据表user_table来说,
	 * AA用户给BB用户转账100
	 */
	@Test
	public void testUpdate() {
		String sql1 = "update user_table set balance=balance-100 where user=?";
		update(sql1,"AA");
		//网络异常
		System.out.println(10/0);//问题所在:如果程序在这里出现故障那得到的结果就是AA转账成功但是BB没有收到
		String sql2 = "update user_table set balance=balance+100 where user=?";
		update(sql2,"BB");
		System.out.println("转账成功");
	}
	

	public int update(String sql,Object ...args){//sql中占位符的个数与可变形参的长度相同!
		Connection conn = null;
		PreparedStatement ps = null;
		try {
			//1.获取数据库的连接
			conn = JDBCUtils.getConnection();
			//2.预编译sql语句,返回PreparedStatement的实例
			ps = conn.prepareStatement(sql);
			//3.填充占位符
			for(int i = 0;i < args.length;i++){
				ps.setObject(i + 1, args[i]);//小心参数声明错误!!
			}
			//4.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			//5.资源的关闭
			JDBCUtils.closeResource(conn, ps);
			
		}
		
		return 0;
		
	}

实例二考虑数据库事务的转账操作

//***************考虑事务的转账操作***********************
	@Test
	public void testUpdatewidthTx() {
		Connection conn=null;//两步操作用一个连接串联起来了
		try {
			conn = JDBCUtils.getConnection();
			System.out.println(conn.getAutoCommit());
			//1.取消数据的自动提交
			conn.setAutoCommit(false);
			String sql1 = "update user_table set balance=balance-100 where user=?";
			update(conn,sql1,"AA");//两者公用一个conn
			//网络异常
			//System.out.println(10/0);
			String sql2 = "update user_table set balance=balance+100 where user=?";
			update(sql2,"BB");
			System.out.println("转账成功");
			conn.commit();//2.提交数据
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			try {
				conn.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}//3.产生异常我们回滚数据
		}finally {
			JDBCUtils.closeResource(conn, null);//最后我们采取关闭连接
		}
		
	}
	
	//通用增删改操作version2.0(从外部传一个连接进来)
	public int update(Connection conn,String sql,Object ...args){//sql中占位符的个数与可变形参的长度相同!
		PreparedStatement ps = null;
		try {
			//2.预编译sql语句,返回PreparedStatement的实例
			ps = conn.prepareStatement(sql);
			//3.填充占位符
			for(int i = 0;i < args.length;i++){
				ps.setObject(i + 1, args[i]);//小心参数声明错误!!
			}
			//4.执行
			return ps.executeUpdate();
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
		// 5.恢复每次DML操作的自动提交功能
		try {
            conn.setAutoCommit(true);
        } catch (SQLException e) {
            e.printStackTrace();
        }
			//5.资源的关闭
			JDBCUtils.closeResource(null, ps);//连接不关,后面还要用
			
		}
		
		return 0;
		
	}

2 事务的ACID属性

2.1 事物的四个属性

  1. 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。
  2. 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另外一个一致性状态。
  3. 隔离性(Isolation) 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。(最重要
  4. 持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来的其他操作和数据库故障不应该对其有任何影响。

2.2 数据的并发问题

对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
    - 脏读: 对于两个事务 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段。之后, 若 T2 回滚, T1读取的内容就是临时且无效的。(一般解决脏读就好了,因为人家都没提交,你读了也没有用

T1读取了T2 更新但还没有被提交数据,这种没有提交被我们读到的数据就叫脏读,一旦T1回滚,T2再去读取,两次读取的数据就不一样。

- 不可重复读: 对于两个事务T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段。之后, T1再次读取同一个字段, 值就不同了。

比如在火车站买票,点击进去有200张票,过了一会儿我们并没有关闭这个事务,再刷新,票数就达到了210张。两次读到的数据不一样叫不可重复读。(一般来说这个可以不解决)

- 幻读: 对于两个事务T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行。之后, 如 果 T1 再次读取同一个表, 就会多出几行。

事务T再去查的时候发现表中多了几行,跟幻觉一样。

数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题
  一个事务与其他事务隔离的程度称为隔离级别。数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱。

2.3 四种隔离级别

数据库提供的4种事务隔离级别:
表格复制

隔离级别描述
READ UNCOMMITTED(读未提交数据)允许事务读取未被其他事务提交的变更;脏读,不可重复度和幻读的问题都会出现(三个问题都不能解决
READ COMMITED(读已提交数据)只允许事务读取已经被其他事务提交的变更;可以避免脏读,但不可重复读和幻读的问题仍然可能出现(解决脏读
REPEATABLE READ(可重复读)确保事务可以多次从一个字段中读取相同的值;在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但幻读的问题仍然存在(解决脏读、幻读
SERIALIZABLE(串行化)确保事务可以从一个表中读取相同的行;在这个事务持续期间,禁止其他事务对该表进行插入,更新和删除操作;所有并发问题都可以避免,但性能十分低下(三个问题都能解决

越往下并发性越差,一致越好。

可重复读解决的方法是:如果事务T1去查表结果是1,,现在事务T2改了表的值变成了2,并且提交了,但现在事务T1并没有断掉,T1再去查表仍然是1,只有当事务T1断了,查到的结果才是2.
串行化解决的方法是:同上,只要在同一个事务内,查到的东西都一样。只有关掉这个事务,重新打开新的事务(比如在一个网页上我们关掉网页重新打开)才能看到更新的行,列。

Oracle 支持的 2 种事务隔离级别:READ COMMITED,SERIALIZABLE; Oracle 默认的事务隔离级别为:READ COMMITED(读已经提交了的)
Mysql 支持 4 种事务隔离级别。Mysql 默认的事务隔离级别为: REPEATABLE READ(可以反复读的)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值