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

                      使用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) {

    /**

     * 幻读:如果隔离级别设为12

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

     * 解决方案:设置隔离级别设为48

     * 只要锁定整张表

     */

    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);//设置事务的隔离级别为12,会出现幻读

         //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事务查询再一次查询,发现的得到的值已经不是以前那个值,所以叫不可重复读

     *解决方案:设置隔离级别设为48

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

     */

    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();

    }

}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值