Advertisement
Guest User

Untitled

a guest
Feb 19th, 2014
72
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 13.16 KB | None | 0 0
  1. import java.io.*;
  2. import java.sql.*;
  3. import java.util.*;
  4.  
  5.  
  6. /****** Proof Of Concept
  7.   of an non-understandable "java.sql.SQLException: Value conflicts occurs" thru OJDBC driver
  8.  
  9. Summary :
  10. =========
  11. - In session1 (using JDBC), 5 records are retrieved with FOR UDPATE and usage of MIN() function
  12.   on a column in the where clause, before being deleted.
  13. - In session2 *DURING* the above processing, an SQL query is updating the column looked for min value
  14.   with a value *LOWER* than the one actually existing.
  15.  
  16. ==> "Value conflicts occurs" exception occurs once the seconds session has commited,
  17.     during the next ResultSet.deleteRow() on the first session.
  18.    
  19.      
  20.  
  21. Based on http://docs.oracle.com/cd/B19306_01/java.102/b14355.pdf  
  22.  
  23. Page 19-14 and 19-15
  24. ---------------------------------------------------------------------------------------------
  25. It is important to be aware of the following facts regarding updatable result sets with the JDBC drivers:
  26. - The drivers do not enforce write locks for an updatable result set.
  27. - The drivers do not check for conflicts with a result set DELETE or UPDATE operation.
  28. A conflict will occur if you try to perform a DELETE or UPDATE operation on a row
  29. updated by another committed transaction.
  30. The Oracle JDBC drivers use the ROWID to uniquely identify a row in a database table.
  31. As long as the ROWID is valid when a driver tries to send an UPDATE or DELETE
  32. operation to the database, the operation will be run.
  33. The driver will not report any changes made by another committed transaction. Any
  34. conflicts are silently ignored and your changes will overwrite the previous changes.
  35. To avoid such conflicts, use the Oracle FOR UPDATE feature when running the query
  36. that produces the result set. This will avoid conflicts, but will also prevent
  37. simultaneous access to the data. Only a single write lock can be held concurrently on a
  38. data item.
  39. ---------------------------------------------------------------------------------------------
  40.  
  41. and
  42.  
  43. page 19-19 table 19-1
  44. ---------------------------------------------------------------------------------------------
  45. Summary of Visibility of Internal and External Changes :
  46. Table 19–1 summarizes the discussion in the preceding sections regarding whether a
  47. result set object in the Oracle JDBC implementation can see changes made internally
  48. through the result set itself, and changes made externally to the underlying database
  49. from elsewhere in your transaction or from other committed transactions
  50.  
  51.                     Can See     Can See     Can See     Can See     Can See     Can See
  52.                     Internal    Internal    Internal    External    External    External
  53. Result Set Type     DELETE?     UPDATE?     INSERT?     DELETE?     UPDATE?     INSERT?
  54. forward-only            no          yes         no          no          no          no
  55. scroll-sensitive        yes         yes         no          no          yes         no
  56. scroll-insensitive      yes         yes         no          no          no          no
  57. ---------------------------------------------------------------------------------------------
  58.  
  59.  
  60. I would expect that once I the query was firstly executed and the ResultSet initially filled,
  61. the min value was computed and didn't change (or get checked against DB) at deletion of one record
  62. of the ResultSet, leading to "Value Conflict Occurs" exception.
  63.  
  64. The ResultSet in the code was FORWARD ONLY type.
  65.  
  66. The update is done on another row of the ones retrieved in the ResultSet, so I beg in not something
  67. about locking, or ROWID
  68. The actual query is input by end-user, and should retrieve records that he wanted to be processed.
  69. So another approach (I mean solution) could not be to do the work in 2 steps :
  70.    *FIRST* get the min value and *THEN* use that value in the where clause.
  71.  
  72.  
  73. I've tried change the query to (hopefully) compute the min value only once :
  74. eg:
  75. SELECT B
  76. FROM POC p, (select min(C) "MIN_C" from POC where A>100) MIN_POC
  77. WHERE A>100 and C=MIN_POC.MIN_C
  78. or
  79. WITH MIN_POC AS (SELECT min(C) min_c FROM POC WHERE A>100)
  80. SELECT B
  81. FROM POC, MIN_POC
  82. WHERE A>100 AND C=MIN_C
  83.  
  84. NOTE : I cannot set FOR UPDATE or I get this error:
  85. ORA-02014: cannot select FOR UPDATE from view with DISTINCT, GROUP BY, etc.
  86.  
  87. And during execution, I have this:
  88.  java.sql.SQLException: Invalid operation for read only resultset: deleteRow
  89.  
  90.  
  91. ###################
  92. ###  TEST CASE  ###
  93. ###################
  94.  
  95.  
  96. 1 : CREATE TABLE
  97. ################
  98. ---------------------------------------------------------------------------------------------
  99. CREATE TABLE POC
  100. ( A  NUMBER,
  101.   B  NUMBER,
  102.   C  NUMBER
  103. );
  104. ---------------------------------------------------------------------------------------------
  105.  
  106.  
  107. 2 : FILL THE TABLE
  108. ##################
  109. ---------------------------------------------------------------------------------------------
  110. -- insert 5 records with each same 'C' from which we will retrieve min from table
  111. INSERT INTO POC VALUES (101, 1, 100);
  112. INSERT INTO POC VALUES (102, 1, 100);
  113. INSERT INTO POC VALUES (103, 1, 100);
  114. INSERT INTO POC VALUES (104, 1, 100);
  115. INSERT INTO POC VALUES (105, 1, 100);
  116. -- and 1 record that will be updated to have a lower C than 100...
  117. INSERT INTO POC VALUES (999, 9, 999);
  118. ---------------------------------------------------------------------------------------------
  119.  
  120. 3 : CUSTOMIZE config file  (config.txt)
  121. #######################################
  122. ---------------------------------------------------------------------------------------------
  123. IP_DB:=demo10g
  124. PORT_DB:=1521
  125. SID:=L10G
  126. USR_DB:=HF509
  127. PWD_DB:=HF509
  128. SQL_SELECT:=SELECT B FROM POC WHERE A>100 and C=(select min(C) from POC where A>100) FOR UPDATE
  129. SQL_UPDATE:=UPDATE POC set C = MOD(A,100) WHERE  A>100 and C=(select max(C) from POC where A>100)
  130. SQL_SELECT_OK:=SELECT B FROM POC WHERE A>100 and C=(select min(C) from POC where A>100) FOR UPDATE
  131. SQL_SELECT_KO1:=SELECT B FROM POC p, (select min(C) "MIN_C" from POC where A>100) MIN_POC WHERE A>100 and C=MIN_POC.MIN_C
  132. SQL_SELECT_KO2:=WITH min_poc AS (SELECT min(C) min_c FROM POC WHERE A>100) select B from POC, MIN_POC where A>100 and C = MIN_C
  133. SQL_SELECT_KO3:=SELECT B FROM POC WHERE A>100 and C=POC_MIN.get_min_POC(0) FOR UPDATE
  134. ---------------------------------------------------------------------------------------------
  135. Customize the first 5 variables to get connection to DB.
  136. Note that 5 lasts variable are not used. It is just to have them available and quickly switch for testing.
  137. (ie: the active query used in runtime will be the 'SQL_SELECT' one)
  138.  
  139.  
  140. 4 : launch jar file simulated GOLD JMS Agent  (windows .bat example)
  141. ############################################
  142. ---------------------------------------------------------------------------------------------
  143. set CLASSPATH=.;E:\_WORK_\_OJDBC_\ojdbc14_20100407_10.2.0.5.jar
  144. java runME config.txt
  145. ---------------------------------------------------------------------------------------------
  146.  
  147.  
  148. 5 : Wait for the first DELETE (5 seconds) and run the UPDATE + COMMIT in *ANOTHER* Oracle session
  149. #################################################################################################
  150. ---------------------------------------------------------------------------------------------
  151. UPDATE POC set C = MOD(A,100) WHERE  A>100 and C=(select max(C) from POC where A>100);
  152. COMMIT;
  153. ---------------------------------------------------------------------------------------------
  154.  
  155.  
  156. 6 : OUTPUT EXAMPLE :
  157. ####################
  158. ---------------------------------------------------------------------------------------------
  159. sql_SEL=SELECT B FROM POC WHERE A>100 and C=(select min(C) from POC where A>100) FOR UPDATE
  160. OK
  161. jdbc:oracle:thin:@demo10g:1521:L10G,HF509,HF509
  162. con=oracle.jdbc.driver.T4CConnection@19fcc69
  163. Record No0
  164. Done ! It's time to run the UPDATE in another session and commit !
  165. Record No1
  166. ex=Un conflit de valeur s'est produit
  167. java.sql.SQLException: Un conflit de valeur s'est produit
  168.         at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:113)
  169.         at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:147)
  170.         at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:209)
  171.         at oracle.jdbc.driver.UpdatableResultSet.executeDeleteRow(UpdatableResultSet.java:2835)
  172.         at oracle.jdbc.driver.UpdatableResultSet.deleteRow(UpdatableResultSet.java:1863)
  173.         at runME.main(runME.java:235)
  174. ---------------------------------------------------------------------------------------------
  175.        
  176.        
  177.  
  178. 7 : in case of need, here's how to compile this source.
  179. #######################################################
  180. ---------------------------------------------------------------------------------------------
  181. javac -cp E:\_WORK_\_OJDBC_\ojdbc14_20100407_10.2.0.5.jar;. runME.java
  182. jar cf runME.jar *.class
  183. ---------------------------------------------------------------------------------------------
  184.  
  185.  
  186. 8 : Optionnal (and not working) : the use of a package to get the min value of the C colum :
  187. ############################################################################################
  188. ---------------------------------------------------------------------------------------------
  189. CREATE OR REPLACE PACKAGE POC_MIN AS
  190.  
  191. FUNCTION get_min_POC(p_site    IN NUMBER )
  192. RETURN NUMBER;
  193.  
  194. END POC_MIN;
  195. /
  196.  
  197.  
  198. CREATE OR REPLACE PACKAGE BODY POC_MIN AS
  199.  
  200. FUNCTION get_min_POC (p_site    IN NUMBER)
  201.  RETURN NUMBER IS
  202. -- PRAGMA AUTONOMOUS_TRANSACTION;  -- useless
  203. o_retour NUMBER :=0 ;
  204. BEGIN
  205.    
  206.    select min(C)
  207.    into o_retour
  208.    from POC
  209.    where A>100;
  210.  
  211.     return    o_retour;          
  212.  
  213.     EXCEPTION
  214.     WHEN OTHERS THEN
  215.         IF (sqlcode = -1) THEN
  216.             return -1;--Erreur : Unique constraint violated
  217.         ELSE
  218.             return -2;--Erreur : pble oracle
  219.         END IF;
  220. END get_min_POC;
  221.  
  222. END POC_MIN;
  223. /
  224. ---------------------------------------------------------------------------------------------
  225. to be used with this query (SQL_SELECT_KO3)
  226. SELECT B FROM POC WHERE A>100 and C=POC_MIN.get_min_POC(0) FOR UPDATE
  227.  
  228.  
  229.  
  230. *******/
  231.  
  232. public class runME {
  233.  
  234.     static Connection con;     
  235.     static ResultSet rs;
  236.     static PreparedStatement ps;
  237.     static Integer record;
  238.     static HashMap hmParam=new HashMap();
  239.  
  240.     /**
  241.      * @param args
  242.      */
  243.     public static void main(String[] args) throws Exception
  244.     {
  245.         try
  246.         {   // Get param from file
  247.             File configFile = new File(args[0]);
  248.             hmParam = ReadWriteTextFile.fileToHashMap(configFile, ":=");
  249.             //System.out.println(hmParam.entrySet());
  250.            
  251.             String sIP_DB = hmParam.get("IP_DB").toString();
  252.             int PORT_DB = Integer.parseInt(hmParam.get("PORT_DB").toString());
  253.             String sSID = hmParam.get("SID").toString();
  254.             String sUSR_DB = hmParam.get("USR_DB").toString();
  255.             String sPWD_DB = hmParam.get("PWD_DB").toString();
  256.             String sql_SEL = hmParam.get("SQL_SELECT").toString();
  257.            
  258.             System.out.println("sql_SEL="+sql_SEL);
  259.  
  260.             // very small simulation of com.aldata.common.bean.jmsagent.JMSPublisherBean
  261.             DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
  262.  
  263.             System.out.println("OK");
  264.  
  265.             System.out.println("jdbc:oracle:thin:@" + sIP_DB + ":" + PORT_DB + ":" + sSID+ "," + sUSR_DB + "," + sPWD_DB);
  266. //          con = DriverManager.getConnection("jdbc:oracle:thin:@demo10g:1521:L10G","HF509","HF509");
  267.             con = DriverManager.getConnection("jdbc:oracle:thin:@" + sIP_DB + ":" + PORT_DB + ":" + sSID, "" + sUSR_DB, "" + sPWD_DB);
  268.  
  269.             System.out.println("con="+con.toString());
  270.            
  271.             ps = con.prepareStatement(sql_SEL, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
  272.             rs = ps.executeQuery();
  273.             record = 0;
  274.             while(rs.next()) {
  275.                 //Some work should happen here.... ;-)
  276.                 // while this kind of query may happen, in another oracle session :
  277.                 // UPDATE POC set C = MOD(A,100) WHERE  A>100 and C=(select max(C) from POC where A>100;
  278.                 System.out.println("Record No"+(record++));
  279.                 Thread.sleep(5000);
  280.                 rs.deleteRow();    
  281.                 System.out.println("Done ! It's time to run the UPDATE in another session and commit !");
  282.             }
  283.             System.out.println("Finished !");
  284.         }
  285.         catch (Exception ex) {
  286.                 System.out.println("ex="+ex.getMessage());
  287.                 ex.printStackTrace();
  288.         }
  289.         finally {
  290.                 Thread.sleep(5000);
  291.                 rs.close();
  292.                 ps.close();
  293.                 con.rollback();
  294.         }
  295.        
  296.     }
  297.  
  298. }
  299.  
  300. class ReadWriteTextFile {
  301.  
  302.   /**
  303.   * Fetch the entire contents of a text file, and return it in a String.
  304.   * This style of implementation does not throw Exceptions to the caller.
  305.   *
  306.   * @param aFile is a file which already exists and can be read.
  307.   */
  308.   static public HashMap fileToHashMap(File aFile,String sep) {
  309.     BufferedReader input = null;
  310.     HashMap<String, String>  hm = new HashMap<String, String>();
  311.     String[] sa;
  312.     try {
  313.       input = new BufferedReader( new FileReader(aFile) );
  314.       String line = null;
  315.  
  316.       while (( line = input.readLine()) != null){
  317.         sa=line.split(sep);
  318.         hm.put(sa[0],sa[1]);
  319.       }
  320.     }
  321.     catch (FileNotFoundException ex) {
  322.       ex.printStackTrace();
  323.     }
  324.     catch (IOException ex){
  325.       ex.printStackTrace();
  326.     }
  327.     finally {
  328.       try {
  329.         if (input!= null) {
  330.           //flush and close both "input" and its underlying FileReader
  331.           input.close();
  332.         }
  333.       }
  334.       catch (IOException ex) {
  335.         ex.printStackTrace();
  336.       }
  337.     }
  338.     return hm;
  339.   }
  340. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement