事务管理基本认识


事务

事务概念

由一条或多条SQL语句组成,要么都成功要么都失败

事务原则(ACID)

  • 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用
  • 一致性(Consistency)): 执行事务前后,数据保持一致(例如,完整性约束了a+b=10,一个事务改变了a,那么b也应该随之改变)多个事务对同一个数据读取的结果是相同的
  • 隔离性(独立性)(Isolation): 并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的

事务的隔离级别又分为

脏读: 指一个事务读取了另外一个事务未提交的数据

不可重复读: 在一个事务内读取表中的某一行数据,多次读取的结果不同(不一定错误,只是某些场合不对)

幻读: 指在一个事务内读取到了别的事务插入的数据,导致前后读取不一样

四种隔离级别

隔离级别说明
READ UNCOMMITTEDRead Uncommitted)读未提交数据允许事务读取未被其他事务提交的数据,也被称之为脏读(Dirty Read)脏读、不可重复读和幻读都会出现
READ COMMITEDRead Committed)读已提交数据这是Oracle的默认事务隔离级别。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。可以避免脏读,但不可重复读和幻读问题仍然存在
REPEATABLE READRepeatable Read)可重复读这是Mysql的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,在这个事务持续期间,禁止其他事务对这个实例进行更新。但幻读仍然存在。InnoDBFalcon存储引擎通过多版本并发控制(MVCCMultiversion Concurrency Control)机制解决了该问题
SERIALIZABLESerializable)串行化这是最高的隔离级别,但是性能也是最差的,确保事务可以从一个表中读取相同的行,在这个事务的持续期间,禁止其他事务对该表执行插入、更新和删除操作,从而解决幻读问题。总的来说,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

Oracle 支持的2种事务隔离级别:READ COMMITEDSERIALIZABLEOracle默认的事务隔离级别是READ COMMITED

持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响

事务分类

隐式事务:没有明显的开启结束标记(DML语句的INSERTUPDATEDELETE语句本身就是一条事务)

显式事务:具有明显的开启结束标记(一般由多条SQL语句组成,必须具有明显的开启和结束标记)

/*
事务格式:
取消隐式事务自动开启的功能(一般事务是默认开启的,所有得先关闭)
开启事务
编写事务需要的sql语句(1条或者多条)
结束事务(提交和回滚)
*/
-- 这几个命令了解就好
SHOW VARIABLES LIKE '%auto%'-- 查看系统变量
SAVEPOINT 保存点名 -- 设置一个事务的保存点
ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名 -- 撤销保存点

-- MySQL是默认开启事务自动提交的
SET autocommit=0;-- 关闭(一般先手动处理事务,关闭自动条件)
-- SET autocommit=1;-- 开启(默认的)

START TRANSACTION;-- 事务开启;标记一个事务的开启,从这个之后SQL都在同一个事务内

编写事务需要的SQL语句(1条或者多条)

-- 提交:提交就是持久化
COMMIT;
-- 回滚:回到原来的样子
ROLLBACK;

-- 事务结束
SET autocommit=1; -- 然后事务结束,自动开启提交

模拟转账

-- 创建数据库
CREATE DATABASE bank CHARSET SET utf8 COLLATE utf_8_general_ci;
-- 创建表
CREATE TABLE IF NOT EXISTS `account`(
	`id` INT(3) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(20) NOT NULL,
	`money` DECIMAL(9,4) NOT NULL,
	PRIMARY KEY(`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
-- 添加数据
INSERT INTO `account`(`name`,`money`)
VALUES ('张三',2000.0000),('张四',10000.0000);

-- 关闭自动提交
SET autocommit=0;

-- 开启一个事务
START TRANSACTION;

UPDATE `account` SET money=money-500 WHERE `name`='张三';
UPDATE `account` SET money=money+500 WHERE `name`='张四';

COMMIT;-- 提交事务(持久化)
ROLLBACK;-- 回滚到原来的位置

SET autocommit=1;-- 恢复默认值

用Java代码模拟转账

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

public class AcidText {
    public static void main(String[] args) {
        Connection c=null;
        PreparedStatement ps=null;
        ResultSet r=null;

        try {
            c=JdbcUtils.getConnection();

            //关闭自动提交(这一步不仅关闭事务提交,同时自动开启一个事务(也就是说不用另外开启一个事务))
            c.setAutoCommit(false);//这也相当于开启事务


            //编写事务需要的SQL语句
            String sql="update account set money=money-500 where id=2";
            ps= c.prepareStatement(sql);
            ps.executeUpdate();


            String sql1="update account set money=money+500 where id=1";
            ps= c.prepareStatement(sql1);
            ps.executeUpdate();

            //提交事务
            c.commit();
            System.out.println("成功");
        } catch (SQLException throwables) {
            try {
                c.rollback();//如果失败则回滚事务
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        }finally {
            JdbcUtils.release(c,ps,null);
        }
    }
}
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JdbcUtils {
    public static String driver=null;
    public static String url=null;
    public static String username=null;
    public static String password=null;
    static {
        try {
            //加载配置文件
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
            Properties properties = new Properties();
            properties.load(in);
            
            //获取配置文件的信息
            driver=properties.getProperty("driver");
            url=properties.getProperty("url");
            username=properties.getProperty("username");
            password=properties.getProperty("password");
            //加载驱动
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    //连接信息,连接成功,返回数据库对象(固定一个类(驱动管理));Connection代表数据库
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,username,password);
    }

    //释放连接
    public static void release(Connection c, Statement s, ResultSet r){

        try {
            if (c!=null){
                c.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }try {
            if (s!=null){
                s.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }try {
            if (r!=null){
                r.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

#配置文件
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/bank?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=111

补充

查看一些命令设置和隔离级别

-- 查看系统事务隔离级别
select @@global.tx_isolation;

-- 设置当前MySQL连接的隔离级别
set transaction isolation level read committed

-- 设置数据库系统的全局的隔离级别
set global transaction isolation level read committed;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值