Spring框架——批處理(batch)和事務(Transaction)
批處理(batch)
批處理(batch)------------>好比快遞員【不能一件一件的送快遞】
- 批處理指的是一次操作中執行多條SQL語句
- 批處理相比於一次一次執行效率會提高很多
- 批處理主要是分兩步:
1.將要執行的SQL語句保存
2.執行SQL語句
- Statement和PreparedStatement都支持批處理操作,這裏我們只需要掌握PreparedStatement的批處理方式:
- 方法:
void addBatch()
- 將要執行的SQL先保存起來,先不執行
- 這個方法在設置完所有的占位符之後調用
int[] executeBatch()
- 這個方法用來執行SQL語句,這個方法會將批處理中所有SQL語句執行
- mysql默認批處理是關閉的,所以我們還需要去打開mysql的批處理:
rewriteBatchedStatements=true
我們需要將以上的參數添加到mysql的url地址中
- 註意:低版本的mysql-jdbc驅動也不支持批處理,一般都是在修改的時候使用批處理,查詢的時候不使用!
案例演示:
1.創建一張新的數據表
CREATE TABLE t_emp( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(50) )
2.反復打開數據庫客戶端,插入語句【相當於每次獲取一個connection連接,執行executeUpdate語句】
INSERT INTO t_emp(NAME) VALUES(‘張三‘); SELECT * FROM t_emp;
3.引出批處理--->執行效率高,資源利用率好!
@Test//測試批處理 public void testBatch(){ //向t_emp表中插入10000條數據 //準備兩個變量 Connection connection = null; PreparedStatement ps = null; try { //獲取數據庫連接 connection=JDBCUtil.getConnection(); //準備SQL模板 String sql = "INSERT INTO t_emp(NAME) VALUES(?)"; //獲取PrepareStatement ps = connection.prepareStatement(sql); //創建一個for循環,來設置占位符 for(int i = 0; i < 10000 ;i++){ //填充占位符 ps.setString(1,"emp"+i); //添加到批處理方法中,調用無參的,有參的是Statement來調用的! ps.addBatch(); } //獲取一個時間戳 long start = System.currentTimeMillis(); //執行批處理 ps.executeBatch(); //獲取一個時間戳 long end = System.currentTimeMillis(); System.out.println("共花費了:"+(end-start)); } catch (SQLException e) { e.printStackTrace(); } }
事務(Transaction)
演示銀行轉賬的功能:
1.創建一張表示賬號的表
CREATE TABLE t_account( id INT PRIMARY KEY AUTO_INCREMENT, a_name VARCHAR(50), balance DECIMAL(11,2) )
2.向表中插入幾個用戶
INSERT INTO t_account(id,a_name,balance) VALUES(NULL,‘sunwukong‘,1000); INSERT INTO t_account(id,a_name,balance) VALUES(NULL,‘zhubajie‘,1000); INSERT INTO t_account(id,a_name,balance) VALUES(NULL,‘shaheshang‘,1000); SELECT * FROM t_account;
3.sunwukong向shaheshang轉賬100元
從sunwukong的賬號減去100元
UPDATE t_account SET balance = balance - 100 WHERE a_name=‘sunwukong‘;
給shaheshang的賬號加上100元
UPDATE t_account SET balance = balance +100 WHERE a_name = ‘shaheshang‘;
重新設置為1000元:
UPDATE t_account SET balance =1000;
4.從java代碼中演示上面的案例:
1.創建Dao類
public class AcountDao { public void update(String name,double money){ //準備兩個變量 Connection conn = null; PreparedStatement ps = null; //準備SQL模板 String sql = "UPDATE t_account SET balance = balance + ? WHERE a_name = ?"; try { conn = JDBCUtil.getConnection(); //獲取PreparedStatement ps = conn.prepareStatement(sql); //填充占位符 ps.setDouble(1, money); ps.setString(2, name); //執行SQL語句 ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JDBCUtil.close(conn, ps, null); } } }
2.測試該DAO
public class TestTransaction { private AcountDao accountDao = new AcountDao(); @Test public void test() { //從sunwukong賬戶向shaheshang賬戶轉賬100元! //1.從sunwukong賬戶扣除100元 accountDao.update("sunwukong", -100); //2.向shaheshang賬戶添加100元 accountDao.update("shaheshang", 100); } }
顯然上面是可以正常執行的!
但是如果上面的程序在suwukong減去100元之後,shaheshang加錢之前,出現了異常,如下所示:
//從sunwukong賬戶向shaheshang賬戶轉賬100元! //1.從sunwukong賬戶扣除100元 accountDao.update("sunwukong", -100); int i =10/0;//添加一個異常 //2.向shaheshang賬戶添加100元 accountDao.update("shaheshang", 100);
- 在開發中我們的一個業務往往需要同時操作多個表,這些操作往往是不可分割,業務中的對數據庫的多次操作,
要麽同時成功,要麽全都失敗。
- 事務的特性(ACID):
原子性(atomicity)
一個事務是一個不可分割的工作單位,事務中包括的諸操作要麽都做,要麽都不做。
一致性(consistency)
事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。
隔離性(isolation)
一個事務的執行不能被其他事務幹擾。
即一個事務內部的操作及使用的數據對並發的其他事務是隔離的,並發執行的各個事務之間不能互相幹擾。
持久性(durability)
持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。
接下來的其他操作或故障不應該對其有任何影響。
- 操作事務的基本步驟:
1.開啟事務
- 開啟事務以後,我們只後的所有操作將都會在同一個事務當中
2.操作數據庫
- 開啟事務以後再去操作數據庫,所有操作將不會直接提交到數據庫中
3.提交事務
- 將修改應用到數據庫
4.回滾事務
- 數據庫操作過程中出現異常了,回滾事務,回滾事務以後,數據庫變成開啟事務之前的狀態
- mysql中的事務控制
#開啟事務
START TRANSACTION
#回滾事務
ROLLBACK
#提交事務
COMMIT
- JDBC中的事務主要通過Connection對象來控制的
1.開啟事務
void setAutoCommit(boolean autoCommit) throws SQLException;
- 設置事務是否自動提交,默認是自動提交
- 設置事務手動提交
conn.setAutoCommit(false);
2.提交事務
void commit() throws SQLException;
- 提交事務
conn.commit()
3.回滾事務
void rollback() throws SQLException;
- 回滾事務
conn.rollback()
- 事務控制的格式:
//創建一個Connection Connection conn = null; try{ //獲取Connection conn = JDBCUtils.getConnection(); //開啟事務 conn.setAutoCommit(false); //對數據庫進行操作 //操作成功,提交事務 conn.commit(); }catch(Exception e){ e.printStackTrace(); //回滾事務 try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally{ JDBCUtils.close(conn, null, null); }
- 註意:我們在同一個事務中使用的數據庫連接(Connection)必須是同一個,否則事務還是不作用!
所以此時原來的AcountDAO中的update方法要改為如下所示:
public class AcountDao { public void update(Connection conn,String name,double money){ //準備兩個變量 PreparedStatement ps = null; //準備SQL模板 String sql = "UPDATE t_account SET balance = balance + ? WHERE a_name = ?"; try { //獲取PreparedStatement ps = conn.prepareStatement(sql); //填充占位符 ps.setDouble(1, money); ps.setString(2, name); //執行SQL語句 ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ //此時也不能在這裏關閉數據庫連接了,而是在外邊統一關閉 JDBCUtil.close(null, ps, null); } } }
Spring框架——批處理(batch)和事務(Transaction)