使用JDBC实例理解数据库的事务隔离

数据库的事务是用来处理数据的一个机制,作为一个整体,要么全部提交,要么全部回滚。
    事务的4大特性(ACID):

原子性(Atomicity):事务是数据库的逻辑工作单位,它对数据库的修改要么全部执行,要么全部不执行。 
    一致性(Consistemcy):事务前后,数据库的状态都满足所有的完整性约束。 
    隔离性(Isolation):并发执行的N个事务是隔离的,一个事务不影响另一个事务,通过设置数据库的隔离级别,一个事务在没有commit之前,被修改的数据不可能被其他事务看到。 
    持久性(Durability):持久性意味着当系统或介质发生故障时,确保已提交事务的更新不能丢失。持久性主要在于DBMS的恢复性能。

今天重点理解事务的隔离性。

一、事务的隔离级别

事务的隔离级别是由底层的数据库实现的,通常有如下五种:

TRANSACTION_NONE:没有事务

TRANSACTION_READ_UNCOMMITTED:读取没有提交的事务

TRANSACTION_READ_COMMITTED:读取提交的事务

TRANSACTION_REPEATABLE_READ:不可重复读事务

TRANSACTION_SERIALIZABLE:序列化事务

他们都可以通过java.sql.Connection获取,对应的整数值分别为0、1、2、4、8,级别从低到高,低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

设置不同的级别会出现不同的问题:

1、 READ_UNCOMMITTED:会出现脏读、不可重复读和幻读问题;
        2、READ_COMMITTED:会出现不可重复读和幻读问题;
        3、REPEATABLE_READ:不会出现上面的问题,且允许事务的并发执行。
        4、SERIALIZABLE:串行化,不会出现上面的问题,事务执行的时候不允许别的事务并发执行。事务串行化执行,事务只能一个接着一个地执行,而不能并发执行。一个事务在执行过程中完全看不到其他事务对数据库所做的更新。

我们以JDBC为例,设置不同的隔离级别,理解上述问题。

二、JDBC的事务处理

1、  获取事务的隔离级别

通过通过java.sql.Connection获取。

System.out.println(Connection.TRANSACTION_NONE);//0

System.out.println(Connection.TRANSACTION_READ_UNCOMMITTED);//1

System.out.println(Connection.TRANSACTION_READ_COMMITTED);//2

System.out.println(Connection.TRANSACTION_REPEATABLE_READ);//4

System.out.println(Connection.TRANSACTION_SERIALIZABLE);//8

2、  JDBC的开启事务

JDBC中默认是自动提交数据,只要把Connection的对象的setAutoCommit设为false,conn.setAutoCommit(false);由自动提交变为手动提交,就开启了事务。

3、  设置事务的隔离级别

conn.setTransactionIsolation(4);//参数为整数,不同的值代表不同的级别

4、  获取事务的隔离级别

conn.getTransactionIsolation()

用这种方法,可以获取  Orace数据库默认提供的是READ_COMMITTED隔离级别,MySQL是TRANSACTION_REPEATABLE_READ。

5、  事务处理

conn.commit();//事务提交

conn.rollback();//事务回滚

6、  关闭事务

conn.setAutoCommit(true)

三、脏读、不可重复读和幻读

1、可重复读:

A事务进行的过程中进行了一次读操作,这时B事务对此数据进行了Update修改,并commit,这时事务A再一次读取此条数据,读取到的将不是B事务修改后的值,而是原值,对原值可重复读。

2、不可重复读:

A事务进行的过程中进行了一次读操作,这时B事务对此数据进行了Update修改,并commit,这时事务A再一次读取此条数据,读取到的是B事务修改后的值,而原来的值不可以再读取到,所以叫做不可重复读。

不可重复读是指在同一个事务内,两个相同的查询返回了不同的结果,不可重复读的重点是修改,对某一行或者某一条数据进行修改,避免不可重复读需要锁行就行。

3、幻读:

事务T1读取一条指定where条件的语句,返回结果集。此时事务T2插入一行新记录,恰好满足T1的where条件。然后T1使用相同的条件再次查询,结果集中可以看到T2插入的记录,这条新纪录就是幻想。幻读的重点在于在表中新增或者删除 符合条件的整行,避免幻影读则需要锁表。

4、脏读:

脏读又称无效数据读出。一个事务读取另外一个事务还没有提交的数据叫脏读。

例如:事务T1修改了一行数据,但是还没有提交,这时候事务T2读取了被事务T1修改后的数据,之后事务T1因为某种原因Rollback了,那么事务T2读取的数据就是脏的。

解决办法:把数据库的事务隔离级别调整到READ_COMMITTED

四、JDBC实例理解事务的隔离级别

1、  脏读:

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

public class TestTxIsolation {

public static void main(String[] args) {

/**

* 脏读:把隔离级别设为1

*  一个A事务读取另一个B事务没有提交的数据,说明库里还没有改数据,但是A事务已经查询出该数据,没有和数据库保持一致,

*/

try {

Class.forName("com.mysql.jdbc.Driver");

Connection connA = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/test", "root", "root");

connA.setAutoCommit(false);// 开启了A事务

connA.setTransactionIsolation(2);// 设置事务的隔离级别为1,说明可以读取没有提交的数据

Connection connB = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/test", "root", "root");

connB.setAutoCommit(false);// 开启了B事务

String sql = "updateJava607 set name='唐飞' whereid=1";

PreparedStatement pstmtB = connB.prepareStatement(sql);

pstmtB.executeUpdate();

// B事务没有提交

// connB.commit();

// A事务查询数据

String sqlA = "select *from Java607";

PreparedStatement pstmtA = connA.prepareStatement(sqlA);

ResultSet rs = pstmtA.executeQuery();

while (rs.next()) {

System.out.println(rs.getInt("id") + "  "

+ rs.getString("name"));

}

connB.setAutoCommit(true);// 关闭B事务

connA.setAutoCommit(true);// 关闭A事务

} catch (ClassNotFoundException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

} catch (SQLException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

}

}

2、  幻读

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

public class TestTxIsolation2 {

public static void main(String[] args) {

/**

* 幻读:如果隔离级别设为1、2

* A事务对表进行某种条件的查询操作,这时B事务插入或者删除符合某种条件的操作,A事务对表进行再次查询同样的操作,发现前后两次的结果不一样

* 解决方案:设置隔离级别设为4、8

* 只要锁定整张表

*/

try {

Class.forName("com.mysql.jdbc.Driver");

Connection connA = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/test", "root", "root");

connA.setAutoCommit(false);//开启了A事务

connA.setTransactionIsolation(2);//设置事务的隔离级别为1或2,会出现幻读

//A事务查询数据

String sqlA="select * from Java607";

PreparedStatementpstmtA=connA.prepareStatement(sqlA);

ResultSet rs=  pstmtA.executeQuery();

while(rs.next()){

System.out.println(rs.getInt("id")+"  "+rs.getString("name"));

}

Connection connB = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/test", "root", "root");

connB.setAutoCommit(false);//开启了B事务

String sql="insert into Java607 values(9,'打分7')";

PreparedStatementpstmtB=connB.prepareStatement(sql);

pstmtB.executeUpdate();

//B事务提交

connB.commit();

connB.setAutoCommit(true);//关闭B事务

//A事务再次查询数据

String sqlA2="select * from Java607";

PreparedStatementpstmtA2=connA.prepareStatement(sqlA2);

ResultSet rs2=  pstmtA2.executeQuery();

System.out.println("=======");

while(rs2.next()){

System.out.println(rs2.getInt("id")+"  "+rs2.getString("name"));

}

connB.setAutoCommit(true);//关闭B事务

connA.setAutoCommit(true);//关闭A事务

} catch (ClassNotFoundException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

} catch (SQLException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

}

}

3、  不可重复读

import java.sql.Connection;

import java.sql.DriverManager;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

public class TestTxIsolation3 {

public static void main(String[] args) {

/**

*不可重复读隔离级别=1,2

*A事务查询某条记录的某个值,然后B事务修改这个值,A事务查询再一次查询,发现的得到的值已经不是以前那个值,所以叫不可重复读

*解决方案:设置隔离级别设为4。8

*只要锁定操作的一条记录

*/

try {

Class.forName("com.mysql.jdbc.Driver");

Connection connA = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/test", "root", "root");

connA.setAutoCommit(false);//开启了A事务

connA.setTransactionIsolation(8);//设置事务的隔离级别为1,

//A事务查询数据

String sqlA="select name from java607 where id=1";

PreparedStatementpstmtA=connA.prepareStatement(sqlA);

ResultSet rs=  pstmtA.executeQuery();

while(rs.next()){

System.out.println(rs.getString("name"));

}

Connection connB = DriverManager.getConnection(

"jdbc:mysql://localhost:3306/test", "root", "root");

connB.setAutoCommit(false);//开启了B事务

String sql="update Java607 set name='new3' where id=1";

PreparedStatementpstmtB=connB.prepareStatement(sql);

pstmtB.executeUpdate();

//B事务提交

connB.commit();

//connB.close();

connB.setAutoCommit(true);//关闭B事务

//A事务查询数据

String sqlA2="select name from java607 where id=1";

PreparedStatementpstmtA2=connA.prepareStatement(sqlA2);

ResultSet rs2=  pstmtA2.executeQuery();

while(rs2.next()){

System.out.println(rs2.getString("name"));

}

connB.setAutoCommit(true);//关闭A事务

connA.setAutoCommit(true);//关闭A事务

} catch (ClassNotFoundException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

} catch (SQLException e) {

// TODO Auto-generatedcatch block

e.printStackTrace();

}

}

}

使用JDBC实例理解数据库的事务隔离相关推荐

  1. 【数据库】事务隔离级别

    事务隔离级别 为了更好地说明事务隔离级别,请先移步快速理解脏读.不可重复读.幻读 有四种隔离级别,分别是读未提交(Read uncommitted),读已提交(Read committed),可重复读 ...

  2. 数据库的事务隔离技术 之 MVCC

    在mysql中,提供了两种事务隔离技术,第一个是mvcc,第二个是next-key技术. 这个在使用不同的语句的时候可以动态选择.不加lock in share mode之类的就使用mvcc.否则使用 ...

  3. 数据库的事务隔离级别

    ANSI/ISO SQL92标准定义了一些数据库操作的隔离级别: l 未提交读(read uncommitted) l 提交读(read committed) l 重复读(repeatable rea ...

  4. 【数据库】Oracle数据库查看事务隔离级别

    查看Oracle数据库事务的隔离级别. --Oracle 查看事务隔离级别 SELECT * From dual for update;SELECT s.sid, s.serial#, case bi ...

  5. 数据库的事务,隔离级别和3大范式

    *数据库事务的想关操作 1.事务开始:开始是一个事物,作为回滚的标记 2,回滚 rollback :回滚到上一个事务开始的地方, 或者回滚到某个存档点,期间没被 commit ; 操作都会被撤回 3. ...

  6. spring的事务隔离_再深一点:面试工作两不误,源码级理解Spring事务

    原创:小姐姐味道(微信公众号ID:xjjdog),欢迎分享,转载请保留出处. Spring有5种隔离级别,7种传播行为.这是面试常问的内容,也是代码中经常碰到的知识点.这些知识枯燥而且乏味,其中有些非 ...

  7. 2.4.4 案例理解4种事务的隔离级别

    2.4.4 通过案例理解4种事务的隔离级别 MySQL数据库管理·实战案例7 需求 1 分析当前数据库的事务隔离级别 2 对四种事务隔离级别进行切换,并对比其效果,最终使用哪种隔离级别. # 设置隔离 ...

  8. 数据库事务的四大特性及事务隔离级别

    概要: 事务的四个特性:原子性.一致性.隔离性.持久性 事务不隔离带来的问题:更新丢失.脏读.不可重复读.虚读(幻读).其中更新丢失就是并发写,这是一定不允许的,因此一定要解决更新丢失问题. 事务隔离 ...

  9. 数据库 事务隔离级别之可重复读

    通过下面的sql语句,在sql客户端查询可以获取数据库的事务隔离级别: show variables like '%isolation%'; 查看全局事务隔离级别和session事务隔离级别(mysq ...

最新文章

  1. 一键修改分辨率bat_求使用批处理BAT设置分辨率的方法介绍?
  2. BGP 路由属性 公认必遵 ORIGIN
  3. Mysql 添加字段 修改字段 删除字段
  4. linux 文件理解,对linux中文件系统的理解
  5. jQuery实现表格隔行换颜色:
  6. 过滤器如何配置(javax.servlet.Filter)?
  7. 网管人员必备的常用命
  8. java如何让cpu过负荷_服务器开发过载问题如何解决
  9. 微信小程序学习日记---模板(template)全解析
  10. 杰理AD14N/AD15N---串口中断问题
  11. lmdb数据库的读取与转换(一) —— 基本操作
  12. 一文看懂YOLO v3
  13. 完全用计算机制作的三维动画,通过四个步骤告诉你三维动画怎么制作
  14. 手机兼职赚钱,分享2个手机可操作的项目给你!
  15. Java cipher加密与解密
  16. 原子物理与原子核物理知识结构(含链接)
  17. php模板引擎循环start,smarty模板引擎foreach和section循环操作详解
  18. Linux下实现文件实时同步(rsync命令+rsync作为服务+xinetd托管rsync)
  19. 【EM(electron migration)】
  20. ChatGPT当中的“GPT”是什么意思?

热门文章

  1. 【面经】某团面经---一篇到底
  2. 基于java的网络抓包技术研究与实现(转)
  3. 动物识别专家系统(Java实现已开源)
  4. OpenGL笔记16 文字绘制
  5. FW:行人检测简述_拔剑-浆糊的传说_新浪博客
  6. 浅谈MPLSVPN数据包的处理过程
  7. 从源码角度解读 xml 文件中的 xmlns、xsi、xsd
  8. YOLOV5训练自己目标检测模型和cpu检测
  9. 源码编译安装 ganesha
  10. 自动驾驶全球布局(2)创业公司