批處理(batch) >好比快遞員【不能一件一件的送快遞】 - 批處理指的是一次操作中執行多條SQL語句- 批處理相比於一次一次執行效率會提高很多 - 批處理主要是分兩步: 1.將要執行的SQL語句保存 2.執行SQL語句 - Statement和PreparedStatement都支持批處理操作, ...
批處理(batch)------------>好比快遞員【不能一件一件的送快遞】
- 批處理指的是一次操作中執行多條SQL語句
- 批處理相比於一次一次執行效率會提高很多
- 批處理主要是分兩步:
1.將要執行的SQL語句保存
2.執行SQL語句
- Statement和PreparedStatement都支持批處理操作,這裡我們只需要掌握PreparedStatement的批處理方式:
- 方法:
void addBatch()
- 將要執行的SQL先保存起來,先不執行
- 這個方法在設置完所有的占位符之後調用
int[] executeBatch()
- 這個方法用來執行SQL語句,這個方法會將批處理中所有SQL語句執行 - mysql預設批處理是關閉的,所以我們還需要去打開mysql的批處理: rewriteBatchedStatements=true
url="jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true"
我們需要將以上的參數添加到mysql的url地址中- 註意:低版本的mysql-jdbc驅動也不支持批處理,一般都是在修改的時候使用批處理,查詢的時候不使用! 案例演示: 1.創建一張新的數據表 2.反覆打開資料庫客戶端,插入語句【相當於每次獲取一個connection連接,執行executeUpdate語句】效率不高 3.引出批處理--->執行效率高,資源利用率好!
public class TestBatch { @Test public void test() { Connection conn = null; PreparedStatement ps = null; String sql ="insert into test (name) values(?)"; //Time:368236 //Time:1393 try { //獲取連接 conn = JDBCUtil.getConnection(); ps = conn.prepareStatement(sql); //設置參數 for(int i = 0; i < 5000 ;i++){ //填充占位符 ps.setString(1,"emp_"+i); //添加到批處理方法中,調用無參的,有參的是Statement來調用的! ps.addBatch(); } long start = System.currentTimeMillis(); //執行批處理 ps.executeBatch(); long end = System.currentTimeMillis(); System.out.println("Time:"+(end-start)); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ JDBCUtil.close(conn); } } }
事務: 演示銀行轉賬的功能: 1.創建一張表示賬號的表 2.向表中插入幾個用戶 3.在資料庫執行sql語句,實現轉賬 4.從java代碼中演示上面的案例: (1)創建Dao類
public class AccountDao { public void update(Integer id,Integer count){ Connection conn = null; PreparedStatement ps = null; try { String sql="UPDATE t_account SET balance = balance + ? WHERE id = ?"; conn = JDBCUtil.getConnection(); ps = conn.prepareStatement(sql); ps.setInt(1, count); ps.setInt(2, id); ps.executeUpdate(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { JDBCUtil.close(conn); } } }
(2).測試該DAO
@Test public void test() { //孫悟空向豬八戒轉賬100元 dao.update(1, -100); dao.update(2, 100); }上面是可以正常執行的 但是如果上面的程式在suwukong減去100元之後,shaheshang加錢之前,出現了異常 第二個 update() 就不會執行,導致數據不一致,一個減少了一個沒增加
@Test public void test() { //孫悟空向豬八戒轉賬100元 dao.update(1, -100); int a = 10/0;//異常 dao.update(2, 100); }
這時候就需要用到 事務!
事務的特性(ACID): - 原子性(atomicity) 一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。 - 一致性(consistency) 事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。 - 隔離性(isolation) 一個事務的執行不能被其他事務干擾。 即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。 - 持久性(durability) 持久性也稱永久性(permanence),指一個事務一旦提交,它對資料庫中數據的改變就應該是永久性的。 接下來的其他操作或故障不應該對其有任何影響。 操作事務的基本步驟: 1.開啟事務 - 開啟事務以後,我們只後的所有操作將都會在同一個事務當中 2.操作資料庫 - 開啟事務以後再去操作資料庫,所有操作將不會直接提交到資料庫中 3.提交事務 - 將修改應用到資料庫 4.回滾事務 - 資料庫操作過程中出現異常了,回滾事務,回滾事務以後,資料庫變成開啟事務之前的狀態 提交事務與回滾事務只能執行其中一條 mysql中的事務控制 #開啟事務 START TRANSACTION #回滾事務 ROLLBACK #提交事務 COMMIT ROLLBACK 要與 START TRANSACTION 連用,單獨使用沒用 JDBC中的事務主要通過Connection對象來控制的 1.開啟事務 void setAutoCommit(boolean autoCommit) throws SQLException; - 設置事務是否自動提交,預設是自動提交 - 設置事務手動提交 conn.setAutoCommit(false); 2.提交事務
public class TestAccount { private AccountDao dao = new AccountDao(); @Test public void test() { Connection conn = null; PreparedStatement ps = null; conn = JDBCUtil.getConnection(); //預設true自動提交,false手動提交 try { //開啟事務需要手動提交 conn.setAutoCommit(false); //孫悟空向豬八戒轉賬100元 dao.update(conn,1, -100); int a = 10/0;//異常 dao.update(conn,2, 100); //提交事務 conn.commit(); } catch (SQLException e) { // TODO Auto-generated catch block try { //出現異常就回滾,保持數據一致性 conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }但是結果依舊是一個減少一個沒增加 因為兩次update調用了兩個Connection,本身還有個Connection,一共三個Connection 註意:我們在同一個事務中使用的資料庫連接(Connection)必須是同一個,否則事務還是不作用! 所以將conn 傳給 update() 此時原來的AccountDAO中的update方法要改為如下所示:
public class AccountDao { public void update(Connection conn,Integer id,Integer count){ PreparedStatement ps = null; try { String sql="UPDATE t_account SET balance = balance + ? WHERE id = ?"; ps = conn.prepareStatement(sql); ps.setInt(1, count); ps.setInt(2, id); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); }finally { //此時也不能在這關閉資料庫,要在外邊統一關閉 JDBCUtil.close(null); } } }
事務與批處理結合 一般是將事務與批處理結合起來寫,更快
public class TestAccount { private AccountDao dao = new AccountDao(); @Test public void test() { Connection conn = null; PreparedStatement ps = null; conn = JDBCUtil.getConnection(); try { //預設true自動提交,false手動提交 //開啟事務需要手動提交 conn.setAutoCommit(false); //批處理 String sql ="insert into test (name) values(?)"; ps = conn.prepareStatement(sql); //設置參數 for(int i = 0; i < 5000 ;i++){ //填充占位符 ps.setString(1,"emp_"+i); //添加到批處理方法中,調用無參的,有參的是Statement來調用的! ps.addBatch(); } long start = System.currentTimeMillis(); //執行批處理 ps.executeBatch(); long end = System.currentTimeMillis(); System.out.println("Time:"+(end-start)); //提交事務 conn.commit(); } catch (SQLException e) { try { conn.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } }finally { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }