關於事物的概念及原理有好多好多,但我的理解就是一句話,要保持一個整體性,就是一個事,要分成幾個步驟去完成,但是只有這幾個步驟都做完了才是一個完整的事。這裡面最簡單的示例就是模擬銀行的取款與存款了,我直接給出這個示例代碼然後加以分析
1 @Test
2 public void test2(){
3 String payId="00001";
4 String recId="00002";
5 double mny=1000;
6 Connection conn=null;
7 try {
8 conn=DBUtil.getConnection();
9 String sql="select *from accounts_jiawen "
10 + "where id=?";
11 PreparedStatement ps=conn.prepareStatement(sql);
12 ps.setString(1, recId);
13 ResultSet rs=ps.executeQuery();
14 if(!rs.next()){
15 System.out.println("收款帳號不存在");
16 throw new SQLException("收款帳號不存在");
17 }
18 //記錄收款方的余額
19 double recMoney=rs.getDouble("money");
20
21 //設置手動管理事務
22 conn.setAutoCommit(false);
23
24 //2.驗證付款方余額夠不夠
25 sql="select * from accounts_jiawen "
26 + "where id=?";
27 ps=conn.prepareStatement(sql);
28 ps.setString(1, payId);
29 rs=ps.executeQuery();
30 rs.next();
31 double payMoney=rs.getDouble("money");
32 if(payMoney<mny){
33 System.out.println("余額不足");
34 throw new SQLException("余額不足");
35 }
36
37
38
39 //3.付款方余額減少n元
40 sql="update accounts_jiawen set "
41 + "money=? where id=?";
42 ps=conn.prepareStatement(sql);
43 ps.setDouble(1, payMoney-mny);
44 ps.setString(2, payId);
45 ps.executeQuery();
46
47 //假設此處有一個錯誤
48 // Integer.valueOf("abc");
49 //4.收款方加n元
50 sql="update accounts_jiawen set "
51 + "money=? where id=?";
52 ps=conn.prepareStatement(sql);
53 ps.setDouble(1, recMoney+mny);
54 ps.setString(2, recId);
55 ps.executeQuery();
56 //當轉賬流程正常結束時統一提交事物
57 conn.commit();
58 } catch (Exception e) {
59 //當轉賬過程發生異常時回滾事務
60 try {
61 conn.rollback();
62 } catch (SQLException e1) {
63 // TODO Auto-generated catch block
64 e1.printStackTrace();
65 throw new RuntimeException("回滾");
66 }
67 e.printStackTrace();
68 throw new RuntimeException("wrong",e);
69 }finally{
70 DBUtil.close(conn);
71 }
72 }
首先,我們模擬兩個銀行的賬號,一個給另一個匯款,首先我們要先查詢一下收款人的信息,這和符合世紀邏輯,因為我們要知道收款人是否存在,同樣是做一個select查詢語句,方法依然使用的是前述的ParperdStatement,設置占位符的方法,判斷收款人是否存在,並記錄下收款方的現在余額,以便在收到錢後做一個累加。
然後重點來了,在第22行,這就是手動管理一個事物,將自動管理事物關閉,這樣以上的代碼就不是意見完整的事,否則他就是完整的一件事,而不會等待下面的過程,但實際上收款與匯款整體才能算上一件事,因為這涉及到兩個人的賬戶金額變動問題,你不能只考慮其中一個人的金額變化,而將它設置為false以後上面的過程就會一起等待下面的過程。第二部是驗證付款方的余額,判斷是否夠轉。後面的過程就是收付雙方金額的變動,如果你不把事物設置為手動的話,在第四步上面模擬一個代碼打斷一下下面代碼的執行,那麼後面的金額將發生錯誤。這裡面還要說明的是conn.commit()是統一提交事務,而conn.rollback()為回滾
見名知意,就是集中到一起發送一組SQL,好處也就是效率更高,降低了數據庫和程序之間的網絡調用,直接給出示例代碼:
@Test
public void test3(){
Connection conn=null;
try {
conn=DBUtil.getConnection();
conn.setAutoCommit(false);
//批量發送數據的前提是他們的SQL一樣
String sql="insert into emp_jiawenzhe values("
+ " emp_jiawenzhe_w.nextval,?,?,?,?,?,?,?)";
PreparedStatement ps =conn.prepareStatement(sql);
for(int i=1;i<=108;i++){
ps.setString(1, "好漢"+i);
ps.setString(2, "打劫");
ps.setInt(3, 0);
ps.setDate(4, null);
ps.setDouble(5, 1000.0);
ps.setDouble(6, 8000.0);
ps.setInt(7, 3);
//將本條數據存到ps內
ps.addBatch();
//每30次批量發送一次數據
if(i%30==0){
ps.executeBatch();
//清除緩存的數據
ps.clearBatch();
}
}
//余下的數據單獨發送一次
ps.executeBatch();
conn.commit();
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
throw new RuntimeException("批量添加員工失敗",e);
}finally{
DBUtil.close(conn);
}
}
這是一個批量插入員工的示例,首先要說明的是,批量發送SQL,前提是他們的SQL要一致,就是干的都是同樣的一種事,在開始進行批處理之前,先要把事物的設置設置為手動提交,下一步就是建立SQL的變參數模型,比如說這裡面我建立的是一個插入的模型,下面的for循環完全是為了省事,做出了插入108條數據的寫法,當然你也可以逐一寫
下面我將說明一下addBatch()這個方法,我們在寫了108條數據並不是直接就寫到了數據庫中,他同樣是要調用ParperdStatement的發送方法,但這就不是excuteQuery,寫出來的東西要先放到緩存中,然後從緩存中拿到一部分數據發送數據庫,再將發送完了的SQL清除緩存,下面做的是每30條發送一次的代碼,調用excuteBatch發送,然後調用clearBatch清除緩存,當然最後剩下的要再一次調用excuteBatch一次性發送走,最後不要忘記提交,因為之前已經設置成了手動提交。
首先對於這塊我認為不太好理解,用起來似乎也沒有上面那麼簡單,接下來同樣是引用一個示例加以說明:
這個示例是這個樣子的,首先要有兩張表,一張是部門表,一張是員工表,這兩個表是關聯的,即主表部門表的主鍵deptno在從表的列中也有出現,並成為從表的外鍵,當對主表進行插入的時候,能將從表的信息一並插入
1 @Test
2 public void test4(){
3 //假設要添加的部門數據如下
4 String dname ="財務部";
5 String loc="北京";
6 //假設要添加員工的數據如下
7 String ename="張三";
8 String job="經理";
9 int mgr=0;
10 double sal=8000.0;
11 double comm=2000.0;
12
13 String ename2="李四";
14 String job2="經理";
15 int mgr2=0;
16 double sal2=5000.0;
17 double comm2=500.0;
18
19 Connection conn=null;
20 try {
21 conn=DBUtil.getConnection();
22 conn.setAutoCommit(false);
23 //先添加部門
24 String sql ="insert into depts_jiawen "
25 + " values(depts_seq_jiawen.nextval,?,?) " ;
26 //參數二是一個數組存的是希望被ps記住的字段名字
27 PreparedStatement ps=conn.prepareStatement(sql,new String[]{"deptno"});
28 ps.setString(1, dname);
29 ps.setString(2, loc);
30 ps.executeUpdate();
31
32 //從ps中獲取它之前記錄的字段值
33 //返回的結果集中只有一種數據
34 //存的就是記錄那些字段的值
35 ResultSet rs=ps.getGeneratedKeys();
36 rs.next();
37 int deptno=rs.getInt(1);
38
39 //再添加員工
40 sql="insert into emp_jiawenzhe values( "
41 + "depts_seq_jiawen.nextval,?,?,?,?,?,?,?) ";
42 ps=conn.prepareStatement(sql);
43 ps.setString(1, ename);
44 ps.setString(2, job);
45 ps.setInt(3, mgr);
46 ps.setDate(4, null);
47 ps.setDouble(5, sal);
48 ps.setDouble(6, comm);
49 ps.setInt(7, deptno);
50 ps.executeUpdate();
51
52 ps=conn.prepareStatement(sql);
53 ps.setString(1, ename2);
54 ps.setString(2, job2);
55 ps.setInt(3, mgr2);
56 ps.setDate(4, null);
57 ps.setDouble(5, sal2);
58 ps.setDouble(6, comm2);
59 ps.setInt(7, deptno);
60 ps.executeUpdate();
61
62 conn.commit();
63
64 } catch (SQLException e) {
65 try {
66 conn.rollback();
67 } catch (SQLException e1) {
68 // TODO Auto-generated catch block
69 e1.printStackTrace();
70 }
71 e.printStackTrace();
72 throw new RuntimeException("wrong",e);
73 }finally{
74 DBUtil.close(conn);
75 }
76 }
這裡面可以看到ParperdStatement中傳入了兩個參數,第一個和之前一樣,是要執行的SQL,而第二個參數是一個字符串數組,他是希望被記住的字段名字,我的理解就是那個外鍵字段放到裡面,為了後面取到這個外鍵的值傳入到從表中進行更新,然後調用了getGeneratedKeys()獲得了這些主鍵的結果集,再用int deptno=rs.getInt(1);得到對應字段的值,對於這塊我實在不能明朗的解釋清楚,目前我也只能說先這麼記住吧,而後面的插入從表就是一個批處理操作,與前述相同。
分頁就是對於一個更龐大的數據表當我們不希望看到整表時,可以分成幾段呈現,這個內容就是一個固定的模式,有固定的分頁公式,一目了然,用的時候直接拿過來使用就好了
示例代碼:
1 @Test
2 public void test5(){
3 int size=10;
4 int page=2;
5
6 Connection conn=null;
7 try {
8 conn=DBUtil.getConnection();
9 String sql="select * from( "
10 + "select e.*,rownum r from ( "
11 + "select * from emp_jiawenzhe "
12 + "order by empno "
13 + ") e "
14 + ") where r between ? and ?";
15 PreparedStatement ps=conn.prepareStatement(sql);
16 ps.setInt(1, (page-1)*size+1);
17 ps.setInt(2, size*page);
18 ResultSet rs=ps.executeQuery();
19 while (rs.next()) {
20 System.out.println(rs.getInt("empno")+","+rs.getString("ename"));
21
22 }
23 } catch (SQLException e) {
24
25 e.printStackTrace();
26 throw new RuntimeException("wrong",e);
27 }finally{
28 DBUtil.close(conn);
29 }
30 }
page和size分別是分幾頁,每頁有幾個,分頁的SQL寫法是數據庫內容,我將在數據庫基本SQL使用中提到,這裡面暫時先這麼寫著,後面的內容也就很清楚了!
未完待續!