MySQL---當Java遇上MySQL⑤---單執行緒與多執行緒下的事務
阿新 • • 發佈:2018-12-11
事務transaction
- 原子性(atomicity):組成事務處理的語句形成了一個邏輯單元,不能只執行其中的一部分。
- 一致性(consistency):在事務處理執行前後,資料庫是一致的(資料庫資料完整性約束)。
- 隔離性(isolcation):一個事務處理對另一個事務處理的影響。
- 持續性(durability):事務處理的效果能夠被永久儲存下來 。
- 一個事務只會有一個結果:要麼成功、要麼失敗。
MySQL事務:開啟、提交、回滾
Start transaction;開始一個事務。
Commit;提交所做的修改。
Rollback;回滾所做的修改。如果在操作時出錯,應該從新開始一個事務。
資料庫表結構:
CREATE TABLE `tb_user` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`username` VARCHAR(10) DEFAULT NULL,
`password` VARCHAR(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8
沒有事務下執行sql語句
演示沒有事務下執行 多條SQL語句
從上往下執行,遇到非法的sql語句就會丟擲異常,由於出現異常,接下來的sql語句將不會執行。
@Test public void noTransaction() throws Exception { Connection con = ConnUtil.getConnection(); Statement st = con.createStatement(); //SQL 語句正確 String sql = "insert into tb_user(username,password) value('Rose','123')"; st.executeUpdate(sql); //正常執行 //SQL 語句錯誤:id是INT型,並非 VARCHAR sql = "insert into tb_uservalue('u123','Rose','123')"; st.executeUpdate(sql); //這裡會出現異常 //SQL 語句正確 sql = "insert into tb_user(username,password) value('Java','1234')"; st.executeUpdate(sql); //由於上一句出現異常,這一句就不會執行。 con.close(); }
演示沒有事務下執行 批量處理
由於是批量處理,java與mysql之間的只有在executeBatch()時才進行通訊,而且把所有sql語句傳送過去,當mysql執行出現錯誤時,會把錯誤資訊封裝起來,等這次批量sql語句都執行完畢才把所有語句的執行結果反饋給java。
@Test public void noTransaction2() throws Exception { Connection con = ConnUtil.getConnection(); Statement st = con.createStatement(); //SQL 語句正確 String sql = "insert into tb_user(username,password) value('Rose','123')"; st.addBatch(sql); //SQL 語句錯誤:id是INT型,並非 VARCHAR sql = "insert into tb_uservalue('u123','Rose','123')"; st.addBatch(sql); //SQL 語句正確 sql = "insert into tb_user(username,password) value('Java','1234')"; st.addBatch(sql); /* 執行時下面這一句會出現異常,因為批量處理是隻有有一條語句出現問題,就會丟擲異常 * 但是語法正確的sql語句會照樣執行,不同於上面演示!!! * 因為上面演示是 每條sql語句都是要進行一次通訊,在出現異常後,與mysql的連線就斷開了, * 而 批量處理 則是一次性把所有sql語句傳送過去,即使異常也只是影響下一次的通訊。 */ st.executeBatch(); con.close(); }
有事務的情況下執行 sql 語句
演示有事務下執行 多條SQL語句
因為開啟了事務,所以當第二條sql語句執行時,mysql反饋過來執行錯誤的資訊,excute()方法就丟擲一個異常,這時該異常被捕捉到,進入了catch塊中,通過 con.rollback()方法進行事務回滾。
@Test
public void transaction(){
Connection con = ConnUtil.getConnection();
try {
con.setAutoCommit( false ); //開啟事務
Statement st = con.createStatement();
//SQL 語句正確
String sql = "insert into tb_user(username,password) value('Rose','123')";
st.executeUpdate(sql); //正常執行
//SQL 語句錯誤:id是INT型,並非 VARCHAR
// sql = "insert into tb_uservalue('u123','Rose','123')";
// st.executeUpdate(sql); //這裡會出現異常
//SQL 語句正確
sql = "insert into tb_user(username,password) value('Java','1234')";
st.executeUpdate(sql);
con.commit(); //提交事務
System.out.println("事務完成...");
} catch (SQLException e) {
e.printStackTrace();
try {
con.rollback();
System.out.println("事務回滾...");
} catch (SQLException e1) {
throw new RuntimeException( e1.getMessage(), e1);
}
} finally {
if( con != null ) {
try {
//如果後序還有其他業務需要訪問資料庫的話,應該還原成事務自動提交
//con.setAutoCommit( true );
//如果沒有後序操作應該關閉連線
con.close();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
演示有事務多執行緒下執行多條sql語句
由於獲取connection的工具是做成單例的形式,所以多執行緒下不同執行緒共享同一個connection連線,當一個執行緒的事務執行完畢後如果出現問題應該就回滾當前事務,而不影響其他執行緒,但是因為單例就會影響到其他執行緒的事務。解決方案:見連線池篇
@Test
public void demo1(){
Connection con = ConnUtil.getConnection();
try {
con.setAutoCommit( false ); //開啟事務
Statement st = con.createStatement();
String sql = "insert into tb_user(username,password) value('Rose','123')";
st.executeUpdate(sql);
//處理其他業務
new MyThread(1).start();
new MyThread(2).start();
con.commit(); //提交事務
System.out.println("主執行緒:事務完成...");
} catch (SQLException e) {
//e.printStackTrace();
try {
con.rollback(); //事務回滾
System.out.println("主執行緒:事務回滾...");
} catch (SQLException e1) {
throw new RuntimeException( e1.getMessage(), e1);
}
} finally {
if( con != null ) {
try {
//con.setAutoCommit( true ); //還原設定
con.close();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
class MyThread extends Thread{
int num ;
public MyThread(int num) {
this.num = num;
}
@Override
public void run() {
Connection con = ConnUtil.getConnection();
try {
con.setAutoCommit( false ); //開啟事務
Statement st = con.createStatement();
String sql = "insert into tb_user(username,password) value('Rose"+num+"','123')";
st.executeUpdate(sql);
con.commit(); //提交事務
System.out.println( "子執行緒"+num+":事務完成...");
} catch (SQLException e) {
//e.printStackTrace();
try {
con.rollback(); //事務回滾
System.out.println("子執行緒"+num+":事務回滾...");
} catch (SQLException e1) {
throw new RuntimeException( e1.getMessage(), e1);
}
} finally {
if( con != null ) {
try {
//con.setAutoCommit( true ); //還原設定
con.close();
} catch (SQLException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
}
}
}
}