1、JDBC中的事務
最簡單的事務的概念的總結
在一個方法中,同時存在多條更新類語句(insert delelte update),那麼這些語句需要以整體方式執行,要麼都成功,要麼都失敗,不可以拆分執行。
舉一個很簡單的例子,A和B兩個人,A從B中購買東西,A需要向B支付一定的費用,假如A、B各有9000元,A需要向B支付5000元,B需要收入5000元,這兩個操作就包含在一個事務之中,兩個操作需要以整體方式執行,不可以拆分,要麼都成功,要麼都失敗,絕不允許只有A支付了,B沒有收到的這樣的情況。
事務的四個特性
1、一致性:
事務在執行前後,數據的大小總和保持不變。
也就是在上面的例子中的A在支付前後,A和B的總金額都是18000,絕對不允許在這個事務執行之後,這個數據的總和發生改變。
2、原子性
構成事務的語句,以整體方式執行,要麼都成功,要麼都失敗。
3、持久性
事務做出的改變,一經確認,具有永久時效性,不可以再撤回。
4、隔離性
事務執行期間,涉及的數據暫時處於鎖定的狀態,其他事務不能處理.
事務的隔離性在數據庫底層是通過鎖機制完成的.
隔離性就好比java線程中的兩個線程不可以同時去修改一個數據,必須加上互斥鎖這樣的鎖,才能保證隔離性。
2、JDBC中採用事務的方式來執行sql語句
我們假定有兩個人,“武松”和“西門吹雪”兩個人做生意,後者需要向前者支付5000元,假定兩個人都有9000,我們在這裏偷偷的給武松的金錢的數據類型大小,設置成4位的,也就是不會超過9999,意思就是,就算收到了5000,也不會加進來稱爲自己的錢,那麼這個時候,我們再看西門吹雪是不是成功支付了,還是沒有成功呢?我們根據事務的原子性和一致性,也知道,是不會成功的,具體看一下代碼:
先看一下和數據庫有關的DBUtils類:
package com.neusoft.system.db;
import java.sql.*;
//資源文件解析器
import java.util.ResourceBundle;
public class DBUtils
{
//1.定義驅動串---整個驅動jar包中,核心類的路徑
private static String driver=null;
//2.定義鏈接串---數據庫所在的位置及名稱
private static String url=null;
private static String userName=null;
private static String password=null;
/**
* 靜態塊
* 在類被第一次加載入內存時候,執行,以後不再執行
*/
static
{
System.out.println("run static .......");
try
{
//獲取資源文件解析器實例
ResourceBundle bundle=ResourceBundle.getBundle("DBOPtions");
//從資源文件獲取數據
driver=bundle.getString("DRIVER");
url=bundle.getString("URL");
userName=bundle.getString("USERNAME");
password=bundle.getString("PASSWORD");
//3.加載驅動
Class.forName(driver);
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
/**
* 當一個類中所有的成員(屬性和方法)都是static,那麼此時構造器應該私有
*/
private DBUtils() {}
public static Connection getConnection()throws Exception
{
//4.創建鏈接
Connection conn=DriverManager.getConnection(url, userName, password);
return conn;
}
public static void close(PreparedStatement pstm)
{
try
{
if(pstm!=null)
{
pstm.close();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public static void close(Connection conn)
{
try
{
if(conn!=null && !conn.isClosed())
{
conn.close();
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
public static void main(String[] args)
{
try
{
System.out.println(DBUtils.getConnection());
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
}
事務的方式來執行更新操作:
package transaction;
import java.sql.*;
import com.neusoft.system.db.DBUtils;
public class Person
{
public static void main(String[] args)
{
try
{
boolean tag=Person.tiger();
System.out.println(tag);
}
catch (Exception e)
{
e.printStackTrace();
}
}
private static boolean tiger()throws Exception
{
//1.定義JDBC接口
Connection conn=null;
PreparedStatement pstm1=null; //扣減西門吹雪
PreparedStatement pstm2=null; //增加武松
try
{
//2.創建連接
conn=DBUtils.getConnection();
//3.完成對西門吹雪賬戶的處理
//3.1定義SQL語句
String sql1="update person set umoney=umoney-? where uid=?";
//3.2編譯SQL語句
pstm1=conn.prepareStatement(sql1);
pstm1.setObject(1, 5);
pstm1.setObject(2, 2);
//4.處理武松的賬戶
//4.1定義SQL語句
String sql2="update person set umoney=umoney+? where uid=?";
//4.2編譯SQL及賦值
pstm2=conn.prepareStatement(sql2);
pstm2.setObject(1, 5);
pstm2.setObject(2, 1);
//5.以事務方式執行SQL
//5.1定義事務返回值
boolean tag=false;
/**
* 5.2開啓事務
* conn內部存在一個叫autoCommit的屬性,該屬性默認值爲true,
* 表示遇到更新類語句,立即修改數據庫
* setAutoCommit(false); 遇到更新類語句,先不要修改數據庫,而是進行彩排(模擬執行),
* 等待後繼指令,後繼指令如果是更新數據庫那麼再去更新,如果是撤銷,那麼取消更新操作
*/
conn.setAutoCommit(false);
try
{
//5.3在事務內部執行所有的更新語句
pstm1.executeUpdate();
pstm2.executeUpdate();
//5.4.1:確認對數據的修改---提交事務
conn.commit();
//5.5修改事務返回值,表示執行成功
tag=true;
}
catch(Exception ex)
{
//5.4.2:取消對數據的修改---事務回滾
conn.rollback();
ex.printStackTrace();
}
finally
{
//5.6結束事務
conn.setAutoCommit(true);
}
return tag;
}
finally
{
DBUtils.close(pstm1);
DBUtils.close(pstm2);
DBUtils.close(conn);
}
}
}
【注】
事務不提交,不會執行sql語句。
在捕捉到異常的時候,要進行事務的回滾,這樣纔不會出差錯。
JDBC結束之後,一定記得關閉連接資源,否則內存泄漏。