一、使用Batch批量處理數據庫
當需要向數據庫發送一批SQL語句執行時,應避免向數據庫一條條的發送執行,而應采用JDBC的批處理機制,以提升執行效率。;
1、實現批處理有兩種方式,第一種方式:
Statement.addBatch(sql) list
執行批處理SQL語句
executeBatch()方法:執行批處理命令
clearBatch()方法:清除批處理命令
例:
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JDBCManager.getConnection();
String sql1 = "insert into user(name,password,email,birthday)
values('kkk','123','abc@sina.com','1978-08-08')";
String sql2 = "update user set password='123456' where id=3";
st = conn.createStatement();
st.addBatch(sql1); //把SQL語句加入到批命令中
st.addBatch(sql2); //把SQL語句加入到批命令中
st.executeBatch();
} finally{
JDBCManager.DBclose(con,st,rs);
}
采用Statement.addBatch(sql)方式實現批處理:
優點:可以向數據庫發送多條不同的SQL語句。
缺點:
SQL語句沒有預編譯。
當向數據庫發送多條語句相同,但僅參數不同的SQL語句時,需重復寫上很多條SQL語句。例如:
Insert into user(name,password) values(‘aa’,’111’);
Insert into user(name,password) values(‘bb’,’222’);
Insert into user(name,password) values(‘cc’,’333’);
Insert into user(name,password) values(‘dd’,’444’);
2、實現批處理的第二種方式:
PreparedStatement.addBatch();
例:
conn = JDBCManager.getConnection();//獲取工具;
String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
st = conn.prepareStatement(sql);//預處理sql語句;
for(int i=0;i<50000;i++){
st.setString(1, "aaa" + i);
st.setString(2, "123" + i);
st.setString(3, "aaa" + i + "@sina.com");
st.setDate(4,new Date(1980, 10, 10));
st.addBatch();//將一組參數添加到此 PreparedStatement 對象的批處理命令中。
if(i%1000==0){
st.executeBatch();
st.clearBatch();清空此 Statement 對象的當前 SQL 命令列表。
}
}
st.executeBatch();將一批命令提交給數據庫來執行,如果全部命令執行成功,則返回更新計數組成的數組。返回數組的 int 元素的排序對應於批中的命令,批中的命令根據被添加到批中的順序排序
采用PreparedStatement.addBatch()實現批處理
優點:發送的是預編譯后的SQL語句,執行效率高。
缺點:只能應用在SQL語句相同,但參數不同的批處理中。因此此種形式的批處理經常用於在同一個表中批量插入數據,或批量更新表的數據。
二、獲得數據庫自動生成的主鍵:
Connection con=null;
PreparedStatement ps=null;
con = JDBCManager.getConnection();
String sql="insert into users(name,password) values(?,?)";
try {
ps = con.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);//獲取返回的主鍵;
ps.setString(1, "qq");
ps.setString(2, "123");
ps.executeUpdate();
ResultSet rs=ps.getGeneratedKeys();//返回一個結果集,保存着產生的key的結果集,
while(rs.next()){
System.out.println(rs.getObject(1));//結果集只有一個值;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCManager.DBclose(con, ps, null);
}
三、JDBC調用存儲過程
存儲過程的創建
create procedure 存儲過程名(參數)
存儲過程體
編寫一個存儲過程,查詢學生表中的所有信息。
delimiter $$
create procedure myproc1()
begin
select * form xs;
end $$
delimiter ;
執行:call myproc1();
參數
in :輸入參數
out: 輸出參數
inout:輸入輸出參數
要求:編寫一個存儲過程,通過學號查詢某學生的信息。
delimiter $$
create procedure select_student(in xh char(6))
begin
select * from xs where 學號=xh;
end $$
delimiter ;
執行:call select_student('081101');
要求:編寫一個存儲過程,統計學生的總人數
delimiter $$
create procedure count_xs(out number int)
begin
select count(*) into number from xs;
end $$
delimiter ;
執行:call count_xs(@rs);
查詢: select @rs;
create procedure 存儲過程名(in|out|inout 參數名 類型。。。。);
JDBC調用存數過程(創建好存儲過程體):
當值是輸入函數時:
例:
Connection con=null;
CallableStatement cs=null;
con=JDBCManager.getConnection();
try {
cs=con.prepareCall("{call pd(?,?)}");//存儲過程語句;
cs.setString(1, "yy");
cs.setString(2, "msn");
cs.execute();//執行
System.out.println("執行成功");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCManager.DBclose(con, cs, null);
}
當輸入和輸出同時:
例
Connection con=null;
CallableStatement cs=null;
con=JDBCManager.getConnection();
try {
cs=con.prepareCall("{call pcall(?,?)}");
cs.setInt(1, 10);
cs.registerOutParameter(2,Types.CHAR);//獲取一下注冊類型;
cs.execute();//執行
System.out.println(cs.getString(2));//獲取第二個String類型的參數值;
cs.execute();
System.out.println("執行成功");
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
JDBCManager.DBclose(con, cs, null);
}
四、事務的使用
事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功。
例如:A——B轉帳,對應於如下兩條sql語句
update from account set money=money+100 where name=‘b’;
update from account set money=money-100 where name=‘a’;
數據庫開啟事務命令
start transaction 開啟事務
Rollback 回滾事務
Commit 提交事務
當Jdbc程序向數據庫獲得一個Connection對象時,默認情況下這個Connection對象會自動向數據庫提交在它上面發送的SQL語句。若想關閉這種默認提交方式,讓多條SQL在一個事務中執行,可使用下列語句:
JDBC控制事務語句
Connection.setAutoCommit(false); start transaction
Connection.rollback(); rollback
Connection.commit(); commit
創建JDBC的事務主要分以下步驟
1.設置事務的提交方式為非自動提交:
conn.setAutoCommit(false);
2.將需要添加事務的代碼放入try,catch塊中。
3.在try塊內添加事務的提交操作,表示操作無異常,提交事務。
conn.commit();
4.在catch塊內添加回滾事務,表示操作出現異常,撤銷事務:
conn.rollback();
5.設置事務提交方式為自動提交:
conn.setAutoCommit(true);
事務的特性(ACID)
原子性(Atomicity)
原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
一致性(Consistency)
事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。
隔離性(Isolation)
事務的隔離性是多個用戶並發訪問數據庫時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作數據所干擾,多個並發事務之間要相互隔離。
持久性(Durability)
持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。
事務的隔離性:
臟讀:
指一個事務讀取了另外一個事務未提交的數據。
這是非常危險的,假設A向B轉帳100元,對應sql語句如下所示
1.update account set money=money+100 while name=‘b’;
2.update account set money=money-100 while name=‘a’;
當第1條sql執行完,第2條還沒執行(A未提交時),如果此時B查詢自己的帳戶,就會發現自己多了100元錢。如果A等B走后再回滾,B就會損失100元。
不可重復讀:
在一個事務內讀取表中的某一行數據,多次讀取結果不同。
例如銀行想查詢A帳戶余額,第一次查詢A帳戶為200元,此時A向帳戶存了100元並提交了,銀行接着又進行了一次查詢,此時A帳戶為300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是准的。
和臟讀的區別是,臟讀是讀取前一事務未提交的臟數據,不可重復讀是重新讀取了前一事務已提交的數據。
很多人認為這種情況就對了,無須困惑,當然是后面的為准。我們可以考慮這樣一種情況,比如銀行程序需要將查詢結果分別輸出到電腦屏幕和寫到文件中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,導致文件和屏幕中的結果不一致,銀行工作人員就不知道以哪個為准了。
虛讀(幻讀)
是指在一個事務內讀取到了別的事務插入的數據,導致前后讀取不一致。
如丙存款100元未提交,這時銀行做報表統計account表中所有用戶的總額為500元,然后丙提交了,這時銀行再統計發現帳戶為600元了,造成虛讀同樣會使銀行不知所措,到底以哪個為准。
數據庫共定義了四種隔離級別:
Serializable:可避免臟讀、不可重復讀、虛讀情況的發生。(串行化)(序列化)
Repeatable read:可避免臟讀、不可重復讀情況的發生。(可重復讀)
Read committed:可避免臟讀情況發生(讀已提交)。
Read uncommitted:最低級別,以上情況均無法保證。(讀未提交)
set transaction isolation level 設置事務隔離級別
select @@tx_isolation 查詢當前事務隔離級別
例:
Connection con=null;
PreparedStatement st=null;
String sql1="insert into users(name,password) values('bbbbb','bbbb')";
String sql2="delete from users where id=9";
try {
con=DBManager.getConnection();
con.setAutoCommit(false);//把自動提交方式變為人工
st=con.prepareStatement(sql1);
st.executeUpdate();
System.out.println("第一個語句成功了");
st=con.prepareStatement(sql2);
st.executeUpdate();
System.out.println("第二個語句成功了");
con.commit();
} catch (Exception e) {
try {
con.rollback();//出現異常進行回滾;
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
}finally{
try {
con.setAutoCommit(true);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DBManager.closeDB(con, st, null);
}