Log4X

链路纵横
自己动手,了解jdbc的事务特性

先建测试表

CREATE TABLE [aaa] (
 
[a] [int] NULL ,
 
[b] [int] NULL 
ON [PRIMARY]
GO

 

下面是测试方法,在一个事务中,先count(*)一次,再插入一条数据错误数据,一条正确数据,再count(*)一次,然后提交。
都是对空表操作

 1 public static void testJDBC() {
 2   Connection conn = getConnection_tx();
 3   try {
 4    conn.setAutoCommit(false);
 5    Statement st = conn.createStatement();
 6 
 7    ResultSet r1 = st.executeQuery("select count(*) from aaa");//query_1
 8    r1.next();
 9    System.out.println(r1.getInt(1));
10 
11    try {
12     st.executeUpdate("INSERT INTO aaa values ('abc',22)"); //execute_1
13    } catch (Exception e) {
14     e.printStackTrace();
15    }
16    st.executeUpdate("INSERT INTO aaa values (33,22)");//execute_2
17    ResultSet i = st.executeQuery("select count(*) from aaa");//query_2
18    i.next();
19    System.out.println(i.getInt(1));
20    conn.commit();
21   } catch (SQLException e) {
22    if (conn != null) {
23     try {
24      conn.rollback();
25     } catch (SQLException e1) {
26      e1.printStackTrace();
27     }
28    }
29    e.printStackTrace();
30   } finally {
31    if (conn != null) {
32     try {
33      conn.close();
34     } catch (SQLException e1) {
35      e1.printStackTrace();
36     }
37    }
38   }
39  }
40 
41 


分析:以上执行过程中,query_1返回0,query_2返回1。这说明了:
- execute_1执行失败,但并未影响execute_2执行。
  这点说明了sql执行错误是在execute_1的时候就抛出,而不是在commit的时候,因此事务没有rollback。
- 第二次执行count语句(query_2)虽然在commit之前,但数量依然比第一次增加了1。
  这点说明在一个事务中,select语句并不只针对数据库的实际内容,而还要算上事务提交之前的所有做过的改变。

再看下面两个线程,SelectThread1 查询了两次,设间隔5秒
SelectThread2 插入一次,8秒后提交.
两个线程同时对空表操作
结果:query_1返回0,query_2返回1,但是query_1和query_2的实际执行间隔并不是5秒,而是接近8秒。

 1 class SelectThread1 implements Runnable{
 2 
 3  public void run() {
 4   Connection conn = DBControl.getConnection_tx();
 5   try {
 6    try {
 7     TimeUnit.MILLISECONDS.sleep(500);
 8    } catch (InterruptedException e) {
 9     return;
10    }   
11    Statement st = conn.createStatement();
12    ResultSet r1 = st.executeQuery("select count(*) from aaa");//query_1  #2
13    r1.next();
14    System.out.println(r1.getInt(1));
15    try {
16     TimeUnit.SECONDS.sleep(5);
17    } catch (InterruptedException e) {
18     return ;
19    }
20    ResultSet i = st.executeQuery("select count(*) from aaa");//query_2 #5
21    i.next();
22    System.out.println(i.getInt(1));
23    
24   } catch (SQLException e) {
25    e.printStackTrace();
26   }finally{
27    if(conn!=null){
28     try {
29      conn.close();
30     } catch (SQLException e) {
31      e.printStackTrace();
32     }
33    }
34   }
35   
36  }
37 }
38 
39 class SelectThread2 implements Runnable{
40 
41  public void run() {
42   Connection conn = DBControl.getConnection_tx();
43   try {
44    conn.setAutoCommit(false);// #1
45    Statement st = conn.createStatement();
46    try {
47     TimeUnit.SECONDS.sleep(3);
48    } catch (InterruptedException e) {
49     return ;
50    }
51    st.executeUpdate("INSERT INTO aaa values (33,22)"); //#3
52    //st.executeQuery("select count(*) from aaa"); 
53    try {
54     TimeUnit.SECONDS.sleep(8);
55    } catch (InterruptedException e) {
56     return ;
57    }
58    conn.commit(); //#4
59    
60   }  catch (SQLException e) {
61    if (conn != null) {
62     try {
63      conn.rollback();
64     } catch (SQLException e1) {
65      e1.printStackTrace();
66     }
67    }
68    e.printStackTrace();
69   } finally {
70    if (conn != null) {
71     try {
72      conn.close();
73     } catch (SQLException e1) {
74      e1.printStackTrace();
75     }
76    }
77   }
78   
79  }
80 }
81 
82  public class SelectThread{
83  
84   public static void main(String[] args){
85    ExecutorService es = Executors.newCachedThreadPool();
86    es.execute(new SelectThread1());
87    es.execute(new SelectThread2());
88    es.shutdown();
89   }
90   
91  }
92 
93 

分析:我使用了sleep()人工影响到各关键步骤的执行顺序。
再加上运行结果,标出了关键步骤执行的先后顺序(#开头)。
从这个顺序可以得到几点比较关键的结论:
- 一个事务对数据库独占以后,不在该事务中的select语句的执行也被阻塞。
- 事务并不是从conn.setAutoCommit(false);开始,而是从st.executeUpdate("INSERT INTO aaa values (33,22)");开始。
  也就是有实际增删改操作时,事务才开始。
- 如果把#3那一步换成下面那句注释语句,则query_1和query_2实际执行间隔又成了5秒,也就是#4和#5先后顺序就反过来了。
  说明执行select语句并不会开始一个事务,也完全不需要commit。

posted on 2008-09-18 14:20  YYX  阅读(427)  评论(0编辑  收藏  举报