2017-03-14 9 views
1

私はファントムリードを作成しようとしていますが、学習のために残念ながら私はできません。私は、Javaスレッド、JDBC、MySQLを使用しています。 ここで私が使用していますプログラムです:ファントムリードを生成できません

package com.isolation.levels.phenomensa; 

import javax.xml.transform.Result; 
import java.sql.Connection; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.util.concurrent.CountDownLatch; 

import static com.isolation.levels.ConnectionsProvider.getConnection; 
import static com.isolation.levels.Utils.printResultSet; 

/** 
* Created by dreambig on 13.03.17. 
*/ 
public class PhantomReads { 


    public static void main(String[] args) { 


     setUp(getConnection());// delete the newly inserted row, the is supposed to be a phantom row 
     CountDownLatch countDownLatch1 = new CountDownLatch(1); // use to synchronize threads steps 
     CountDownLatch countDownLatch2 = new CountDownLatch(1); // use to synchronize threads steps 

     Transaction1 transaction1 = new Transaction1(countDownLatch1, countDownLatch2, getConnection()); // the first runnable 
     Transaction2 transaction2 = new Transaction2(countDownLatch1, countDownLatch2, getConnection()); // the second runnable 

     Thread thread1 = new Thread(transaction1); // transaction 1 
     Thread thread2 = new Thread(transaction2); // transaction 2 

     thread1.start(); 
     thread2.start(); 

    } 

    private static void setUp(Connection connection) { 

     try { 
      connection.prepareStatement("DELETE from actor where last_name=\"PHANTOM_READ\"").execute(); 
     } catch (SQLException e) { 
      e.printStackTrace(); 
     } 
    } 


    public static class Transaction1 implements Runnable { 


     private CountDownLatch countDownLatch; 
     private CountDownLatch countDownLatch2; 
     private Connection connection; 


     public Transaction1(CountDownLatch countDownLatch, CountDownLatch countDownLatch2, Connection connection) { 
      this.countDownLatch = countDownLatch; 
      this.countDownLatch2 = countDownLatch2; 
      this.connection = connection; 
     } 

     @Override 
     public void run() { 


      try { 

       String query = "select * from actor where first_name=\"BELA\""; 

       connection.setAutoCommit(false); // start the transaction 

       // the transaction isolation, dirty reads and non-repeatable reads are prevented ! 
       // only phantom reads can occure 
       connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); 

       //read the query result for the first time. 
       ResultSet resultSet = connection.prepareStatement(query).executeQuery(); 
       printResultSet(resultSet);     // print result. 

       //count down so that thread2 can insert a row and commit. 
       countDownLatch2.countDown(); 
       //wait for the second query the finish inserting the row 
       countDownLatch.await(); 

       System.out.println("\n ********* The query returns a second row satisfies it (a phantom read) ********* !"); 
       //query the result again ... 
       ResultSet secondRead = connection.createStatement().executeQuery(query); 

       printResultSet(secondRead); //print the result 

      } catch (SQLException e) { 
       e.printStackTrace(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

     } 
    } 


    public static class Transaction2 implements Runnable { 


     private CountDownLatch countDownLatch; 
     private CountDownLatch countDownLatch2; 
     private Connection connection; 


     public Transaction2(CountDownLatch countDownLatch, CountDownLatch countDownLatch2, Connection connection) { 
      this.countDownLatch = countDownLatch; 
      this.countDownLatch2 = countDownLatch2; 
      this.connection = connection; 
     } 


     @Override 
     public void run() { 

      try { 
       //wait the first thread to read the result 
       countDownLatch2.await(); 

       //insert and commit ! 
       connection.prepareStatement("INSERT INTO actor (first_name,last_name) VALUE (\"BELA\",\"PHANTOM_READ\") ").execute(); 
       //count down so that the thread1 can read the result again ... 
       countDownLatch.countDown(); 
      } catch (SQLException e) { 
       e.printStackTrace(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 

     } 


    } 


} 

は、しかし、これは実際に結果

---------------------------------------------------------- 
| 196 | | BELA | | WALKEN | | 2006-02-15 04:34:33.0 | 
---------------------------------------------------------- The query returns a second row satisfies it (a phantom read) 
---------------------------------------------------------- 
| 196 | | BELA | | WALKEN | | 2006-02-15 04:34:33.0 | 
---------------------------------------------------------- 

あるしかし、私はそれが私が使用しています

---------------------------------------------------------- 
| 196 | | BELA | | WALKEN | | 2006-02-15 04:34:33.0 | 
---------------------------------------------------------- The query returns a second row satisfies it (a phantom read) ! 
---------------------------------------------------------- 
| 196 | | BELA | | WALKEN | | 2006-02-15 04:34:33.0 | 
---------------------------------------------------------- 
---------------------------------------------------------- 
| 196 | | BELA | | PHANTOM_READ | | 2006-02-15 04:34:33.0 | 
---------------------------------------------------------- 

されるべきだと思う: のJava 8 は、 JDBC MySQL InnoDB Sakila DB挿入int O mysqlの

答えて

2

ファントムはを読んで、次のシナリオです:トランザクションが検索条件を満たす行のセットを読み込みます。次に、2番目のトランザクションによって、この検索条件を満たす行が挿入されます。次に、最初のトランザクションは、検索条件を満たす行のセットを再度読み込み、異なる行のセット(例えば、新しく挿入された行を含む)を取得します。

反復可能な読み取りトランザクションが行を読み取った場合、別のトランザクションでこの行が更新または削除され、これらの変更がコミットされ、最初のトランザクションが行を再読み込みすると、以前と同じ定数値スナップショット)。

実際には、ファントムの読み取りが行われる必要はありません。 MySQLは、実際より多くの場合、ファントムの読み取りを防止します。 MySQLでは、ファントムの読み込み(現時点では)は(あなたが誤って)ファントムの行を更新した後にのみ行われます。そうでなければ、行は隠れたままです。これはMySQLに特有のもので、他のデータベースシステムとは異なる動作をします。また、この動作はいつか変更される可能性があります(MySQLではSQL標準で要求されている一貫性のある読み取りをサポートしていますが、特定の状況ではファントム読み取りが発生しません)。あなたは幻ローを取得するために、例えば、次の手順を使用することができます

:あなたが行を削除する場合

insert into actor (first_name,last_name) values ('ADELIN','NO_PHANTOM'); 

transaction 1: 
select * from actor; 
-- ADELIN|NO_PHANTOM 

transaction 2: 
insert into actor (first_name,last_name) values ('BELA','PHANTOM_READ'); 
commit; 

transaction 1: 
select * from actor; -- still the same 
-- ADELIN|NO_PHANTOM 

update actor set last_name = 'PHANTOM READ' 
where last_name = 'PHANTOM_READ'; 

select * from actor; -- now includes the new, updated row 
-- ADELIN|NO_PHANTOM 
-- BELA |PHANTOM READ 

もう面白いことはところで起こる:

insert into actor (first_name,last_name) values ('ADELIN','NO_PHANTOM'); 
insert into actor (first_name,last_name) values ('BELA','REPEATABLE_READ'); 

transaction 1: 
select * from actor; 
-- ADELIN|NO_PHANTOM 
-- BELA |REPEATABLE_READ 

transaction 2: 
delete from actor where last_name = 'REPEATABLE_READ'; 
commit; 

transaction 1:  
select * from actor; -- still the same 
-- ADELIN|NO_PHANTOM 
-- BELA |REPEATABLE_READ 

update actor set last_name = ''; 

select * from actor; -- the deleted row stays unchanged 
-- ADELIN| 
-- BELA |REPEATABLE_READ 

これは標準SQLが必要ですまさにです。あなたが(削除された)行を読み返すと、元の値が得られます。

関連する問題