JDBC事务的隔离级别

news/2024/5/19 0:49:18 标签: JDBC

----------------------------------------------JDBC事务的隔离级别----------------------------------------------------

事务隔离级别

 

l  隔离性(Isolation):隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。

(准确的说隔离级别是可以设置的,可以设置为事物之间相互干扰,也可以设置为事务之间不会相互干扰,主要体现在两个事务一个在写数据,一个在读数据,对读的一方是有影响的)

Ø  事务的并发读问题

l  脏读:读取到另一个事务未提交数据;

l  不可重复读:两次读取不一致;

l  幻读(虚读):读到另一事务已提交数据。

 

Ø  2 五大并发事务问题

因为并发事务导致的问题大致有5类,其中两类是更新问题,三类是读问题。

 

l  脏读(dirty read):读到未提交更新数据

 

时间

转账事务A

取款事务B

T1

 

开始事务

T2

开始事务

 

T3

 

查询账户余额为1000元

T4

 

取出500元把余额改为500元

T5

查看账户余额为500元(脏读)

 

T6

 

撤销事务,余额恢复为1000元

T7

汇入100元把余额改为600元

 

T8

提交事务

 

  

A事务查询到了B事务未提交的更新数据,A事务依据这个查询结果继续执行相关操作。但是接着B事务撤销了所做的更新,这会导致A事务操作的是脏数据。(这是绝对不允许出现的事情)

 

l  虚读(幻读)(phantom read):读到已提交插入数据

 

时间

统计金额事务A

转账事务B

T1

 

开始事务

T2

开始事务

 

T3

统计总存款数为10000元

 

T4

 

新增一个存款账户,存款为100元

T5

 

提交事务

T6

再次统计总存款数为10100元

 

 

  A事务第一次查询时,没有问题,第二次查询时查到了B事务已提交的新插入数据,这导致两次查询结果不同。(在实际开发中,很少会对相同数据进行两次查询,所以可以考虑是否允许虚读)

 

l  不可重复读(unrepeatable read):读到已提交更新数据

 

时间

取款事务A

转账事务B

T1

 

开始事务

T2

开始事务

 

T3

 

查询账户余额为1000元

T4

查询账户余额为1000元

 

T5

 

取出100元,把余额改为900元

T6

 

提交事务

T7

查询账户余额为900元(与T4读取的一不一致)

 

 

  不可重复读与虚读有些相似,都是两次查询的结果不同。后者是查询到了另一个事务已提交的新插入数据,而前者是查询到了另一个事务已提交的更新数据。

 

Ø  3 四大隔离级别

 

隔离级别

脏读

不可重复读

虚读

READ UNCOMMITTED

允许

允许

允许

READ COMMITTED

不允许

允许

允许

REPEATABLE READ

不允许

不允许

允许

SERIALIZABLE

不允许

不允许

不允许

 

Ø  4 哪种隔离级别最好

4个等级的事务隔离级别,在相同数据环境下,使用相同的输入,执行相同的工作,根据不同的隔离级别,可以导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力是不同的。

 

1 SERIALIZABLE(串行化)

当数据库系统使用SERIALIZABLE隔离级别时,一个事务在执行过程中完全看不到其他事务对数据库所做的更新。当两个事务同时操作数据库中相同数据时,如果第一个事务已经在访问该数据,第二个事务只能停下来等待,必须等到第一个事务结束后才能恢复运行。因此这两个事务实际上是串行化方式运行。

 

2 REPEATABLE READ(可重复读)

当数据库系统使用REPEATABLEREAD隔离级别时,一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,但是不能看到其他事务对已有记录的更新。

 

3 READ COMMITTED(读已提交数据)

  当数据库系统使用READ COMMITTED隔离级别时,一个事务在执行过程中可以看到其他事务已经提交的新插入的记录,而且还能看到其他事务已经提交的对已有记录的更新。

 

4 READ UNCOMMITTED(读未提交数据)

  当数据库系统使用READ UNCOMMITTED隔离级别时,一个事务在执行过程中可以看到其他事务没有提交的新插入的记录,而且还能看到其他事务没有提交的对已有记录的更新。

 

你可能会说,选择SERIALIZABLE,因为它最安全!没错,它是最安全,但它也是最慢的!而且也最容易产生死锁。四种隔离级别的安全性与性能成反比!最安全的性能最差,最不安全的性能最好!

 

MySQL的默认隔离级别为REPEATABLE READ

Oracle数据库支持READ COMMITTED 和 SERIALIZABLE这两种事务隔离级别。所以Oracle不支持脏读Oracle的默认隔离级别是READ COMMITTED

Ø  6 JDBC设置隔离级别

con.setTransactionIsolation(int level)

参数可选值如下:

l  Connection.TRANSACTION_READ_UNCOMMITTED;

l  Connection.TRANSACTION_READ_COMMITTED;

l  Connection.TRANSACTION_REPEATABLE_READ;

l  Connection.TRANSACTION_SERIALIZABLE。

 

事务总结:

事务的特性:ACID

事务开始边界与结束边界:开始边界(con.setAutoCommit(false)),结束边界(con.commit()或con.rollback());

事务的隔离级别: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。多个事务并发执行时才需要考虑并发事务。

 

事务的隔离主要体现在读数据和写数据

 

Read committed(oracle默认的隔离级别)隔离级别代码示例

不可提交读代码示例:我们需要两个类,一个修改数据,一个查询数据


修改数据类

package com.rl.jdbc.tran;

 

import java.math.BigDecimal;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.SQLException;

 

import com.rl.jdbc.utils.DBUtils;

 

public class JDBCTran1 {

 

       public static void main(String[] args){

              //定义事务执行语句

              String sql = "update myaccount t set t.balance = t.balance - ? where t.id = ?";

              Connection conn = DBUtils.getConn();

              PreparedStatement ps = null;

              try {

                     //获得预编译对象

                     ps = conn.prepareStatement(sql);

                     //设置手动提交事务,让当前的连接connection的所有的数据库变更

                     //的sql都在一个事务之内,要么全部成功要么全部失败。

                     conn.setAutoCommit(false);

                     //给执行sql语句赋参数值

                     ps.setBigDecimal(1, new BigDecimal(1000));

                     ps.setInt(2, 1);

                     ps.executeUpdate();

                     //手动提交事务

                     conn.commit();

              } catch (Exception e) {

                     //如果发生异常就事务回滚

                     try {

                            conn.rollback();

                     } catch (SQLException e1) {

                            e1.printStackTrace();

                     }

                     e.printStackTrace();

              } finally{

                     //关闭资源

                     DBUtils.closeUpdate(ps);

              }

       }

}

查询数据类

package com.rl.jdbc.tran;

 

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

 

import com.rl.jdbc.utils.DBUtils;

 

public class JDBCRead {

 

       public static void main(String[] args) {

              Connection conn = DBUtils.getConn();

              PreparedStatement ps = null;

              try {

                     //建立开启事务的查询语句

                     String sql = "select * from myaccount t where t.id = 2 for update";

                     //查询事务的隔离级别

                     System.out.println(conn.getTransactionIsolation());

                     conn.setAutoCommit(false);

                     ps = conn.prepareStatement(sql);

                     ps.executeUpdate();

                     read(conn);

                     read(conn);

                     conn.commit();

              } catch (SQLException e) {

                     e.printStackTrace();

              } finally {

                     DBUtils.closeUpdate(ps);

              }

             

       }

      

       /**

        * 创建一个查询数据的方法

        */

       public static void read(Connection conn) {

              PreparedStatement ps = null;

              String sql = "select * from myaccount t where t.id = ?";

              ResultSet rs = null;

              try {

                     ps = conn.prepareStatement(sql);

                     ps.setInt(1, 1);

                     rs = ps.executeQuery();

                     rs.next();

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

              } catch (SQLException e) {

                     e.printStackTrace();

              } finally {

                     try {

                            if(rs != null)

                                   rs.close();

                            if(ps != null)

                                   ps.close();

                     } catch (SQLException e) {

                            e.printStackTrace();

                     }

              }

       }

}

幻读(虚读)代码示例:

添加数据类

package com.rl.jdbc.tran;

 

import java.math.BigDecimal;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.SQLException;

 

import com.rl.jdbc.utils.DBUtils;

 

public class JDBCTran2 {

 

       public static void main(String[] args){

              //定义事务执行语句

              String sql = "insert into myaccount values(?,?,?)";

              Connection conn = DBUtils.getConn();

              PreparedStatement ps = null;

              try {

                     //获得预编译对象

                     ps = conn.prepareStatement(sql);

                     //设置手动提交事务,让当前的连接connection的所有的数据库变更

                     //的sql都在一个事务之内,要么全部成功要么全部失败。

                     conn.setAutoCommit(false);

                     //给执行sql语句赋参数值

                     ps.setInt(1, 3);

                     ps.setString(2, "王五");

                     ps.setBigDecimal(3, new BigDecimal(10001));

                     ps.executeUpdate();

                     //手动提交事务

                     conn.commit();

              } catch (Exception e) {

                     //如果发生异常就事务回滚

                     try {

                            conn.rollback();

                     } catch (SQLException e1) {

                            e1.printStackTrace();

                     }

                     e.printStackTrace();

              } finally{

                     //关闭资源

                     DBUtils.closeUpdate(ps);

              }

       }

}

 

查询数据类

package com.rl.jdbc.tran;

 

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.SQLException;

 

import com.rl.jdbc.utils.DBUtils;

 

public class JDBCRead2 {

 

       public static void main(String[] args) {

              Connection conn = DBUtils.getConn();

              PreparedStatement ps = null;

              try {

                     //建立开启事务的查询语句

                     String sql = "select * from myaccount t where t.id = 2 for update";

                     //查询事务的隔离级别

                     System.out.println(conn.getTransactionIsolation());

                     conn.setAutoCommit(false);

                     ps = conn.prepareStatement(sql);

                     ps.executeUpdate();

                     read(conn);

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

                     read(conn);

                     conn.commit();

              } catch (SQLException e) {

                     e.printStackTrace();

              } finally {

                     DBUtils.closeUpdate(ps);

              }

             

       }

      

       /**

        * 创建一个查询数据的方法

        */

       public static void read(Connection conn) {

              PreparedStatement ps = null;

              String sql = "select * from myaccount";

              ResultSet rs = null;

              try {

                     ps = conn.prepareStatement(sql);

                     rs = ps.executeQuery();

                     while(rs.next()) {

                            System.out.println("用户名" + rs.getString("username")+ "  余额" + rs.getString("balance"));

                     }

              } catch (SQLException e) {

                     e.printStackTrace();

              } finally {

                     try {

                            if(rs != null)

                                   rs.close();

                            if(ps != null)

                                   ps.close();

                     } catch (SQLException e) {

                            e.printStackTrace();

                     }

              }

       }

}

 

Serializeble的隔离级别需要在修改和查询两个类设置隔离级别,要在事务提交前设置

//事务隔离级别要在提交事务前提交

conn.setTransactionIsolation(conn.TRANSACTION_SERIALIZABLE);

conn.setAutoCommit(false);

 


http://www.niftyadmin.cn/n/1564524.html

相关文章

测试一下

最近一直在下雨转载于:https://blog.51cto.com/5478906/926095

JDBC中DBCP数据库连接池详解

------------------------------------------------JDBC中DBCP数据库连接池详解---------------------------------------------- JDBC数据库连接池接口(DataSource)  Java为数据库连接池提供了公共的接口:javax.sql.DataSource,…

SQL转换StringBuffer

SQL转换StringBuffer 一个sql快速转换成StringBuffer格式拼接的小工具。当然不止SQL,XML转换StringBuffer格式拼接也是没问题哒! 看下效果: SELECT d.deptno, d.dname, d.loc, COUNT(e.empno) AS mycount, NVL(AVG(e.sal), 0) AS myavg…

CSS3 Gradient

CSS3发布很久了,现在在国外的一些页面上常能看到他的身影,这让我羡慕已久,只可惜在国内为了兼容IE,让这一项技术受到很大的限制,很多Web前端人员都望而止步。虽然如此但还是有很多朋友在钻研CSS3在web中的应用&#xf…

JDBC中C3PO数据库连接池详解

-----------------------------------------------------JDBC中C3PO数据库连接池详解------------------------------------------------ C3P0简介C3P0也是开源免费的连接池!C3P0被很多人看好!C3P0的使用C3P0中池类是:ComboPooledDataSource。…

XML转换StringBuffer

XML转换StringBuffer 一个xml快速转换成StringBuffer格式拼接的小工具。减轻的xml拼接工作量。 看下效果&#xff1a;<PARAM><DBID>35</DBID><SEQUENCE>atgtca</SEQUENCE><MAXNS>10</MAXNS><MINIDENTITIES>90</MINIDENTI…

Timer计时器

1、新建一个项目&#xff1a;Lesson34_Timer 2、MainActivity.java代码如下&#xff1a; package basic.android.timer;02 03import java.util.Timer;04import java.util.TimerTask;05 06import android.app.Activity;07import android.os.Bundle;08import android.os.Handler;…

xml的概述

---------------------------------------xml的概述------------------------------------------- XML概述1 什么是XML XML全称为Extensible Markup Language, 意思是可扩展的标记语言&#xff0c;它是 SGML&#xff08;标准通用标记语言&#xff09;的一个子集。 XML语法上和…