什么是Java事務
通常的觀念認為,事務僅與數(shù)據(jù)庫相關(guān)。
事務必須服從ISO/IEC所制定的ACID原則。ACID是原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)的縮寫。事務的原子性表示事務執(zhí)行過程中的任何失敗都將導致事務所做的任何修改失效。一致性表示當事務執(zhí)行失敗時,所有被該事務影響的數(shù)據(jù)都應該恢復到事務執(zhí)行前的狀態(tài)。隔離性表示在事務執(zhí)行過程中對數(shù)據(jù)的修改,在事務提交之前對其他事務不可見。持久性表示已提交的數(shù)據(jù)在事務執(zhí)行失敗時,數(shù)據(jù)的狀態(tài)都應該正確。
通俗的理解,事務是一組原子操作單元,從數(shù)據(jù)庫角度說,就是一組SQL指令,要么全部執(zhí)行成功,若因為某個原因其中一條指令執(zhí)行有錯誤,則撤銷先前執(zhí)行過的所有指令。更簡答的說就是:要么全部執(zhí)行成功,要么撤銷不執(zhí)行。
既然事務的概念從數(shù)據(jù)庫而來,那Java事務是什么?之間有什么聯(lián)系?
實際上,一個Java應用系統(tǒng),如果要操作數(shù)據(jù)庫,則通過JDBC來實現(xiàn)的。增加、修改、刪除都是通過相應方法間接來實現(xiàn)的,事務的控制也相應轉(zhuǎn)移到Java程序代碼中。因此,數(shù)據(jù)庫操作的事務習慣上就稱為Java事務。
事務的特性:
1) 原子性(atomicity):事務是數(shù)據(jù)庫的邏輯工作單位,而且是必須是原子工作單位,對于其數(shù)據(jù)修改,要么全部執(zhí)行,要么全部不執(zhí)行。
2) 一致性(consistency):事務在完成時,必須是所有的數(shù)據(jù)都保持一致狀態(tài)。在相關(guān)數(shù)據(jù)庫中,所有規(guī)則都必須應用于事務的修改,以保持所有數(shù)據(jù)的完整性。
3) 隔離性(isolation):一個事務的執(zhí)行不能被其他事務所影響。
4) 持久性(durability):一個事務一旦提交,事物的操作便永久性的保存在DB中。即使此時再執(zhí)行回滾操作也不能撤消所做的更改。
事務(Transaction):是并發(fā)控制的單元,是用戶定義的一個操作序列。這些操作要么都做,要么都不做,是一個不可分割的工作單位。通過事務,sql server 能將邏輯相關(guān)的一組操作綁定在一起,以便服務器 保持數(shù)據(jù)的完整性。事務通常是以begin transaction開始,以commit或rollback結(jié)束。Commint表示提交,即提交事務的所有操作。具體地說就是將事務中所有對數(shù)據(jù)的更新寫回到磁盤上的物理數(shù)據(jù)庫中去,事務正常結(jié)束。Rollback表示回滾,即在事務運行的過程中發(fā)生了某種故障,事務不能繼續(xù)進行,系統(tǒng)將事務中對數(shù)據(jù)庫的所有已完成的操作全部撤消,滾回到事務開始的狀態(tài)。
自動提交事務:每條單獨的語句都是一個事務。每個語句后都隱含一個commit。 (默認)
顯式事務:以begin transaction顯示開始,以commit或rollback結(jié)束。
隱式事務:當連接以隱式事務模式進行操作時,sql server數(shù)據(jù)庫引擎實例將在提交或回滾當前事務后自動啟動新事務。無須描述事物的開始,只需提交或回滾每個事務。但每個事務仍以commit或rollback顯式結(jié)束。連接將隱性事務模式設置為打開之后,當數(shù)據(jù)庫引擎實例首次執(zhí)行下列任何語句時,都會自動啟動一個隱式事務:alter table,insert,create,open ,delete,revoke ,drop,select, fetch ,truncate table,grant,update在發(fā)出commit或rollback語句之前,該事務將一直保持有效。在第一個事務被提交或回滾之后,下次當連接執(zhí)行以上任何語句時,數(shù)據(jù)庫引擎實例都將自動啟動一個新事務。該實例將不斷地生成隱性事務鏈,直到隱性事務模式關(guān)閉為止。
JDBC事務管理
在使用JDBC的時候, 如何進行事務的管理。直接看一下代碼
示例代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
/** * @Title: JDBCTrans.java * @Package com.oscar999.trans * @Description: * @author XM * @date Feb 14, 2017 4:38:27 PM * @version V1.0 */ package com.oscar999.trans; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * @author * */ public class JDBCTrans { public JDBCTrans() { } /** * * @param sHostName * @param sPortNumber * @param sSid * @param userName * @param password * @return * @throws SQLException */ public Connection getConnection(String sHostName, String sPortNumber, String sSid, String userName, String password) throws SQLException { Connection conn = null ; String url = getOraclURL(sHostName, sPortNumber, sSid); conn = DriverManager.getConnection(url,userName,password); return conn; } /** * * @param conn * @param sql * @throws SQLException */ public void add(Connection conn, String sql) throws SQLException { Statement stmt = null ; try { stmt = conn.createStatement(); stmt.execute(sql); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (stmt != null ) stmt.close(); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub String sHostName = "" ; String sPortNumber = "" ; String sSid = "" ; String userName = "" ; String password = "" ; sHostName = "" ; sPortNumber = "" ; sSid = "" ; userName = "" ; password = "" ; try { Class.forName( "oracle.jdbc.driver.OracleDriver" ); } catch (ClassNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } JDBCTrans jdbcTrans = new JDBCTrans(); Connection conn = null ; try { conn = jdbcTrans.getConnection(sHostName, sPortNumber, sSid, userName, password); conn.setAutoCommit( false ); // can't insert, update //1. add SQL String addSQL = "insert into TEST_TABLE values('name1','value1')" ; jdbcTrans.add(conn,addSQL); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { /*if (conn != null) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }*/ } } private String getOraclURL(String sHostName, String sPortNumber, String sSid) { String url = "jdbc:oracle:thin:@" + sHostName + ":" + sPortNumber + ":" + sSid; return url; } } |
針對以上代碼, 說明如下:
以上代碼有幾點說明的部分:
1. conn.setAutoCommit(false)
執(zhí)行之后不提交事務。
對于Select沒有影響, 但對于Insert和Update的話, 沒有提交數(shù)據(jù)就不會被修改
2. conn.close();
關(guān)閉Connection的代碼有被Mark掉, 是想呈現(xiàn)conn.setAutoCommit(false)
的效果。
原因是在 Connection Close的時候會執(zhí)行一次Commit.
而如果Connection是在應用服務器中使用連接池的話, Connection就不會被Close, 也就不會執(zhí)行Commit.
3. setAutoCommit(false)
用法大多數(shù)是在要執(zhí)行多條語句才提交。
所以針對以上第三點, 更接近實際的狀況的代碼如示例代碼2
示例代碼2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
|
/** * @Title: JDBCTrans.java * @Package com.oscar999.trans * @Description: * @author XM * @date Feb 14, 2017 4:38:27 PM * @version V1.0 */ package com.oscar999.trans; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.Statement; /** * @author * */ public class JDBCTrans { public JDBCTrans() { } /** * * @param sHostName * @param sPortNumber * @param sSid * @param userName * @param password * @return * @throws SQLException */ public Connection getConnection(String sHostName, String sPortNumber, String sSid, String userName, String password) throws SQLException { Connection conn = null ; String url = getOraclURL(sHostName, sPortNumber, sSid); conn = DriverManager.getConnection(url, userName, password); return conn; } /** * * @param conn * @param sql * @throws SQLException */ public void add(Connection conn, String sql) throws SQLException { Statement stmt = null ; try { stmt = conn.createStatement(); stmt.execute(sql); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { if (stmt != null ) stmt.close(); } } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub String sHostName = "" ; String sPortNumber = "" ; String sSid = "" ; String userName = "" ; String password = "" ; sHostName = "" ; sPortNumber = "" ; sSid = "" ; userName = "" ; password = "" ; try { Class.forName( "oracle.jdbc.driver.OracleDriver" ); } catch (ClassNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } JDBCTrans jdbcTrans = new JDBCTrans(); Connection conn = null ; try { conn = jdbcTrans.getConnection(sHostName, sPortNumber, sSid, userName, password); conn.setAutoCommit( false ); // can't insert, update // 1. add SQL 1 String addSQL = "insert into TEST_TABLE values('name1','value1')" ; jdbcTrans.add(conn, addSQL); //2. add SQL 2 addSQL = "insert into TEST_TABLE values('name2','value2')" ; jdbcTrans.add(conn, addSQL); conn.commit(); } catch (SQLException e) { // TODO Auto-generated catch block if (conn!= null ){ try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } } e.printStackTrace(); } finally { if (conn != null ) { try { conn.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } private String getOraclURL(String sHostName, String sPortNumber, String sSid) { String url = "jdbc:oracle:thin:@" + sHostName + ":" + sPortNumber + ":" + sSid; return url; } } |
這里需要說明的是: conn.rollback();
只要執(zhí)行有異常,就要rollback , 這一步必不可少
如果沒有在執(zhí)行出現(xiàn)異常的時候進行回滾。如果在執(zhí)行第一條語句之后出現(xiàn)異常,con既沒有提交也沒有回滾,表就會被鎖住(如果oracle數(shù)據(jù)庫就是行鎖),而這個鎖卻沒有機會釋放。
可能在執(zhí)行con.close()
的時候會釋放鎖,但還是如果應用服務器使用了數(shù)據(jù)庫連接池,連接不會被斷開。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家學習或者使用java能帶來一定的幫助,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。