事务

概念:
事务(Transaction):其实指的一组操作,里面包含许多单一的逻辑,只要有一个逻辑没有执行成功,那么都算失败。所有的数据都回归到最初的状态(回滚)。

为什么要有事务:
为了确保逻辑的成功。例如:银行转账

注意:事务只是针对连接连接对象,如果再开一个连接对象,那么那是默认的提交。

一.使用代码方式演示事务

1) 关闭自动提交的设置:conn.setAutoCommit(false); -----(写在事务开始前)
2) 提交事务:conn.commit(); -----(写在事务结束后)
3) 回滚事务:conn.rollback(); --------(写在捕获异常中)

例1:用数据库修改演示事务

第一步:导入连接包,并添加路径:

在这里插入图片描述

第二步:部署配置文件,并新建JDBC工具类

在这里插入图片描述

工具类:

package tool;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class JDBCUtils {
	
	private JDBCUtils(){};
	private static Connection con;
	private static String driverClass;
	private static String url;
	private static String username;
	private static String password;
	
	static{
		try{
			readConfig();
			Class.forName(driverClass);
			con = DriverManager.getConnection(url, username, password);
		}catch(Exception ex){
			throw new RuntimeException(ex+"数据库连接失败");
		}
	}
	
	private static void readConfig() throws Exception{
		InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("database.properties");
		Properties pro = new Properties();
		pro.load(in);
		driverClass = pro.getProperty("driverClass");
		url = pro.getProperty("url");
		username = pro.getProperty("username");

		password = pro.getProperty("password"); 
	}
	
	public static Connection getConnection(){
		return con;
	}
	
	public static void close(Connection con,Statement stat){
		if(stat != null){
			try{
				stat.close();
			}catch(SQLException ex){};
		}
		if(con != null){
			try{
				con.close();
			}catch(SQLException ex){};
		}
		
	}
	public static void close(Connection con,Statement stat,ResultSet rs){
		if(stat != null){
			try{
				stat.close();
			}catch(SQLException ex){};
		}
		if(con != null){
			try{
				con.close();
			}catch(SQLException ex){};
		}
		if(rs != null){
			try{
				rs.close();
			}catch(SQLException ex){};
		}
		
	}

}

第三步:数据库数据:

在这里插入图片描述

第四步:测试类

package com.test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.junit.Test;

import tool.JDBCUtils;

public class TestDemo {
	
	@Test
	public void testTransaction(){
		
		Connection conn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		
		try {
			conn = JDBCUtils.getConnection();
			
			//连接。事务默认就是自动提交的。关闭自动提交
			conn.setAutoCommit(false);
			
			String sql = "update  account set money = money- ? where id = ?";
			ps = conn.prepareStatement(sql);
			
			//给id为1的人员-100
			ps.setInt(1, 100);
			ps.setInt(2, 1);
			ps.executeUpdate();
			
			int a = 10/0;  (插入的异常数据)
			
			//给id为2的人+100
			ps.setInt(1, -100);
			ps.setInt(2, 2);
			ps.executeUpdate();
			
			//成功:提交事务
			conn.commit();
			
		} catch (SQLException e) {
			
			//失败:回滚事务
			try {
				conn.rollback();
			} catch (SQLException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
			}
			e.printStackTrace();
		}finally{
			JDBCUtils.close(conn,ps,rs);
		}
	}
}

分析:
如果没有插入事务的话,由于中间插入的异常数据,导致异常之后的代码不能被执行,但是异常数据之前的代码仍可执行。但是插入事务后,一旦出现异常数据,整个代码都不会被执行

二.事务的特性(ACID)

1.原子性
事务中包含的逻辑,不可分割
2.一致性
事务执行前后,数据完整性
3.隔离性
事务在执行期间不应该受到其他事务的影响
4.持久性
事务执行成功,那么数据应该持久的保存到磁盘上

事务安全问题

  1. 读 问题
    脏读
    不可重复读
    幻读
  2. 写问题
    丢失更新(悲观锁、乐观锁)

事务隔离级别(由上而下依次提高):

  1. Read Uncommitted —(读未提交)
    引发问题:脏读
  2. Read Committed —(读已提交)
    解决:脏读 引发:不可重复读
  3. Repeatable Read —(重复读)
    解决:脏读、不可重复读 未解决:幻读
  4. Serializable —(可串行化)
    解决:脏读、不可重复读、幻读

脏读:一个事务读到另外一个事务还未提交的数据
不可重复读: 一个事务读到了另外一个事务提交的数据,造成了前后两次查询结果不一致。
可重复读:一个事务不受另外一个事务的影响,即使另外一个事务执行更新,也不受其影响,前后两次查询结果依然一致。
幻读:一个事务读到了另一个事务insert的数据,造成前后查询结果不一致。
可串行化:如果有一个连接的隔离级别设置为了串行化,那么谁先打开了事务,谁就有了先执行的权利,谁后打开事务,谁就只能等着,等前面的那个事务,提交或者回滚后,才能执行。但是这种隔离级别一般比较少用,容易造成性能上的问题,效率比较低。

按效率划分,从高到低:
读未提交 > 读已提交 > 重复读 > 可串行化
按拦截程度,从高到低:
可串行化 > 重复读 > 读已提交 > 读未提交

读未提交 演示:

  1. 设置A窗口的隔离级别为: 读未提交
    在这里插入图片描述

  2. 两个窗口都分别开启事务

在这里插入图片描述

读已提交 演示

  1. 设置A窗口的隔离级别为 读已提交
    在这里插入图片描述

  2. AB两个窗口都开启事务,在B窗口执行更新操作
    在这里插入图片描述

  3. 在A窗口执行的查询结果不一致,一次是在B窗口提交事务之前,一次是在B窗口提交事务之后

在这里插入图片描述

事务的丢失更新

在这里插入图片描述

解决丢失更新的方法有两种:

  1. 乐观锁
  2. 悲观锁

悲观锁:
在这里插入图片描述

乐观锁(需要程序员手动):

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值