---------------------------- 一、什麼是事務 ------------------------------java
什麼是事務?sql
須要注意的是,事務的概念不是針對某個特定的數據庫的,而是針對全部數據庫都須要遵循的一項原則。
咱們先來看一個經典的示例,將設有兩個銀行帳戶,他們之間實現轉帳業務,在這個時候就須要有事務控制,不然將可能出現不可預期的錯誤。
初始餘額信息以下:數據庫
一、假設A要給B轉帳 $1000 ,正常狀況下應該是下面介過樣子滴:安全
二、但是,狀況可能會出現下面這樣不盡人意滴:測試
出現這種狀況的緣由是,A用戶去銀行給B轉帳1000,A操做成功了,可是在B銀行正在準備將A轉過來的錢存入B的帳戶時,這個時候發生了異常,好比忽然斷電了,或者因爲程序錯誤緣由,致使錢沒存入到B帳戶中,就發生了這樣的一幕。this
因此,可見咱們經過事務來控制是多麼的重重要,它能保證咱們的數據處於一致的狀態,達到以下的效果:spa
這主要是用到的事務的特性,將A轉帳和向B存款放在同一個事務中,若是其中某一步發生了錯誤,事務將進行回滾(即一旦B存款發生的錯誤,A的轉帳也失效,將A的錢回退回去),達到操做前得一個正確狀態。3d
---------------------------- 二、事務的特性 ------------------------------
既然咱們須要使用事務,那麼咱們首先的搞清楚事務具備哪些特性,只有熟悉了這些特性,咱們才能更好的根據須要來使用事務管理咱們的應用。blog
一、原子性:保證事務中的全部步驟屬於同一個總體,要麼所有成功,要麼所有失敗。
二、一致性:指數據須要處於一致的狀態。
三、隔離性:因爲數據庫中的資源處於一個共享的狀態,同一時刻可能有多個用戶操做,可能形成數據紊亂的狀態,因此須要經過隔離特性來控制,在某一時刻只能有一個用戶操做,當這個用戶操做完成後再讓其餘用戶操做,以此保證數據的準確性。
四、持久性:數據庫主要提供了數據持久化的功能,一旦某個操做成功提交以後,這些數據就處於一個持久化的狀態了,處於一個安全的狀態。事務
---------------------------- 三、自動事務、局部事務和全局事務 ------------------------------
3.一、自動事務:
對於咱們執行一條SQL語句,這個時候通常不須要經過咱們顯示的管理事務,單其實事務依然是存在的,是數據庫提供的一項隱式管理功能,它不須要咱們顯示的進行開始事務、提交事務等操做。這樣事務稱爲自動事務管理。
須要注意的是:若是咱們沒顯示的對事務進行管理,數據庫就會爲咱們提供自動事務處理機制,不管咱們的SQL語句是怎麼樣的。
3.二、局部事務
其實局部事務和全局事務主要從業務的範圍上存在不一樣,例如咱們進行本行轉帳,就能夠經過局部事務控制,由於整個流程只涉及到一個應用程序和一個數據庫,沒有痛其餘應用程序交互,在業務範圍上相對較小。
3.三、全局事務
全局事務處理的是一個比較寬泛的業務邏輯,它可能跨越多個應用系統以及應用程序,使用邏輯與局部事務管理雷同,只是範圍更加寬而已。
經過全局事務將A銀行轉帳和B銀行進帳控制在同一個事務中,若是A和B都成功了才提交,只要有一方發生異常即回滾事務,整個流程失敗,以此來控制數據的一致性。
---------------------------- 四、自定義事務 ------------------------------
自定義事務能夠顯示的控制事務,下面以一個銀行轉帳的例子來講明:
一、定義一個帳戶實體
/**
* @author Administrator
* 帳戶實體類
*/
public class Account {
private int id;
private String account;
private int money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
二、編寫SQL配置文件
<sqlMap>
<!-- 給類定義別名,簡化代碼 -->
<typeAlias alias="account" type="com.test.bean.Account"/>
<!-- 查詢 -->
<select id="account_select" parameterClass="java.lang.String" resultClass="account">
select account,money from account where account=#account#
</select>
<!-- 更新 -->
<update id="account_update" parameterClass="account">
update account set money=#money# where account=#account#
</update>
</sqlMap>
三、測試事務控制
package com.test.dbutil;
import java.sql.SQLException;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.test.bean.Account;
public class TestAccount {
/**
* 測試事務控制轉帳
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
TestAccount t = new TestAccount();
try {
t.exchange1("A", "C", 500);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 實現轉帳功能【已經事務控制】
* @param aname
* @param bname
* @param money
* @throws SQLException
*/
private void exchange(String aname,String bname, int money) throws SQLException{
SqlMapClient sqlMapClient = BaseDAO.getInstance();
try {
//一、開始事務
sqlMapClient.startTransaction();
/** 具體的事處理 **/
//a、到數據庫查詢出兩個帳戶
Account a = (Account) sqlMapClient.queryForObject("account_select", aname);
Account b = (Account) sqlMapClient.queryForObject("account_select", bname);
if(a != null && b != null){
//b、設置帳戶的金額
a.setMoney(a.getMoney() - money); //a帳戶減小金額
b.setMoney(b.getMoney() + money); //b帳戶增長金額
//c、執行轉帳功能
int ak = sqlMapClient.update("account_update", a);
int bk = sqlMapClient.update("account_update", b);
if(ak != 1 || bk != 1){
throw new SQLException("轉帳失敗");
}
}else{
throw new SQLException("輸入的帳戶信息有誤");
}
System.out.println("轉帳成功:帳戶"+aname+"已經成功向帳戶"+bname+"轉帳。\n轉帳金額:"+money);
//二、提交事務
sqlMapClient.commitTransaction();
}finally{
//三、結束事務
sqlMapClient.endTransaction();
}
}
/**
* 轉行功能【未作事務控制】
* @param aname
* @param bname
* @param money
* @throws SQLException
*/
private void exchange1(String aname,String bname, int money) throws SQLException{
SqlMapClient sqlMapClient = BaseDAO.getInstance();
try {
//a、到數據庫查詢出兩個帳戶
Account a = (Account) sqlMapClient.queryForObject("account_select", aname);
Account b = (Account) sqlMapClient.queryForObject("account_select", bname);
//a帳戶執行轉帳
a.setMoney(a.getMoney() - money);
sqlMapClient.update("account_update", a);
//b帳戶執行金額進帳
b.setMoney(b.getMoney() + money);
sqlMapClient.update("account_update", b);
System.out.println("轉帳成功:帳戶"+aname+"已經成功向帳戶"+bname+"轉帳。\n轉帳金額:"+money);
}catch(Exception e){
throw new SQLException("轉帳失敗");
}
}
}
經過測試咱們能夠發現,經過事務控制的,不會發生數據不一致的狀況,二不經過事務控制的在發生異常的狀況下不能保證數據的正確性。