方立勋_30天掌握JavaWeb_JDBC、存储过程、事务(二)
使用JDBC處理大數據
在實際開發中,程序需要把大文本或二進制數據保存到數據庫。
基本概念:大數據也稱之為LOB(Large Objects),LOB又分為:
clob和blob
1. clob用于存儲大文本。
2. blob用于存儲二進制數據,例如圖像、聲音、二進制文等。
對MySQL而言只有blob,而沒有clob,mysql存儲大文本采用的是Text,Text和blob分別又分為:
- TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
- TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
使用JDBC處理大文本
對于MySQL中的Text類型,可調用如下方法設置:
PreparedStatement.setCharacterStream(index, reader, length);
//注意length長度須設置,并且設置為int型
對MySQL中的Text類型,可調用如下方法獲取:
reader = resultSet. getCharacterStream(i);
reader = resultSet.getClob(i).getCharacterStream();
string s = resultSet.getString(i);
使用JDBC處理二進制數據
對于MySQL中的BLOB類型,可調用如下方法設置:
PreparedStatement. setBinaryStream(i, inputStream, length);
對MySQL中的BLOB類型,可調用如下方法獲取:
InputStream in = resultSet.getBinaryStream(i);
InputStream in = resultSet.getBlob(i).getBinaryStream();
Oracle中大數據處理:
- Oracle定義了一個BLOB字段用于保存二進制數據,但這個字段并不能存放真正的二進制數據,只能向這個字段存一個指針,然后把數據放到指針所指向的Oracle的LOB段中, LOB段是在數據庫內部表的一部分。
- 因而在操作Oracle的Blob之前,必須獲得指針(定位器)才能進行Blob數據的讀取和寫入。
- 如何獲得表中的Blob指針呢? 可以先使用insert語句向表中插入一個空的blob(調用oracle的函數empty_blob() ),這將創建一個blob的指針,然后再把這個empty的blob的指針查詢出來,這樣就可得到BLOB對象,從而讀寫blob數據了。
Oracle中LOB類型的處理:
1、插入空blob insert into test(id,image) values(?,empty_blob());
2、獲得blob的cursor select image from test where id= ? for update; Blob b = rs.getBlob(“image”);
注意: 須加for update,鎖定該行,直至該行被修改完畢,保證不產生并發沖突。
3、利用 io,和獲取到的cursor往數據庫讀寫數據
注意:以上操作需開啟事務。
使用JDBC進行批處理
業務場景:當需要向數據庫發送一批SQL語句執行時,應避免向數據庫一條條的發送執行,而應采用JDBC的批處理機制,以提升執行效率。
- 實現批處理有兩種方式,第一種方式:
Statement.addBatch(sql) list - 執行批處理SQL語句
executeBatch()方法:執行批處理命令
clearBatch()方法:清除批處理命令
- 采用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’);
實現批處理的第二種方式:
PreparedStatement.addBatch()
采用PreparedStatement.addBatch()實現批處理
優點:發送的是預編譯后的SQL語句,執行效率高。
缺點:只能應用在SQL語句相同,但參數不同的批處理中。因此此種形式的批處理經常用于在同一個表中批量插入數據,或批量更新表的數據。
獲得數據庫自動生成的主鍵
示例:
Connection conn = JdbcUtil.getConnection();String sql = "insert into user(name,password,email,birthday) values('abc','123','abc@sina.com','1978-08-08')"; PreparedStatement st = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS );st.executeUpdate(); ResultSet rs = st.getGeneratedKeys(); //得到插入行的主鍵 if(rs.next())System.out.println(rs.getObject(1));注:此參數僅對insert操作有效。
JDBC調用存儲過程
什么是存儲過程?
A:未完待寫。
編寫存儲過程(參看mysql文檔)
得到CallableStatement,并調用存儲過程:
CallableStatement cStmt = conn.prepareCall(“{call demoSp(?, ?)}”);
設置參數,注冊返回值,得到輸出
cStmt.registerOutParameter(2, Types.VARCHAR);
cStmt.setString(1, “abcdefg”);
cStmt.execute();
System.out.println(cStmt.getString(2));
事務
事務的概念
事務指邏輯上的一組操作,組成這組操作的各個單元,要不全部成功,要不全部不成功。
例如: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代碼中使如下轉帳操作在同一事務中執行。
update from account set money=money-100 where name=‘a’;
update from account set money=money+100 where name=‘b’;
設置事務回滾點
Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
Conn.commit(); //回滾后必須要提交
事務的特性(ACID)
原子性(Atomicity)原子性是指事務是一個不可分割的工作單位,事務中的操作要么都發生,要么都不發生。
一致性(Consistency)事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。
隔離性(Isolation)事務的隔離性是多個用戶并發訪問數據庫時,數據庫為每一個用戶開啟的事務,不能被其他事務的操作數據所干擾,多個并發事務之間要相互隔離。
持久性(Durability)持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即使數據庫發生故障也不應該對其有任何影響。
著重講下:事務的隔離級別
多個線程開啟各自事務操作數據庫中數據時,數據庫系統要負責隔離操作,以保證各個線程在獲取數據時的準確性。
如果不考慮隔離性,可能會引發如下問題:
1. 臟讀:
指一個事務讀取了另外一個事務未提交的數據。
這是非常危險的,假設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元。
2. 不可重復讀:
在一個事務內讀取表中的某一行數據,多次讀取結果不同。
例如銀行想查詢A帳戶余額,第一次查詢A帳戶為200元,此時A向帳戶內存了100元并提交了,銀行接著又進行了一次查詢,此時A帳戶為300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是準的。和臟讀的區別是,臟讀是讀取前一事務未提交的臟數據,不可重復讀是重新讀取了前一事務已提交的數據。
很多人認為這種情況就對了,無須困惑,當然是后面的為準。我們可以考慮這樣一種情況,比如銀行程序需要將查詢結果分別輸出到電腦屏幕和寫到文件中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,導致文件和屏幕中的結果不一致,銀行工作人員就不知道以哪個為準了。
3. 虛讀(幻讀)
- 是指在一個事務內讀取到了別的事務插入的數據,導致前后讀取不一致。
- 如丙存款100元未提交,這時銀行做報表統計account表中所有用戶的總額為500元,然后丙提交了,這時銀行再統計發現帳戶為600元了,造成虛讀同樣會使銀行不知所措,到底以哪個為準。
事務隔離性的設置語句
數據庫共定義了四種隔離級別:
- Serializable:可避免臟讀、不可重復讀、虛讀情況的發生。(串行化)
- Repeatable read:可避免臟讀、不可重復讀情況的發生。(可重復讀)
- Read committed:可避免臟讀情況發生(讀已提交)。
- Read uncommitted:最低級別,以上情況均無法保證。(讀未提交)
set transaction isolation level 設置事務隔離級別
select @@tx_isolation 查詢當前事務隔離級別
事務隔離級別不是每個數據庫都有,如mysql有以上四種隔離級別,但oracle只有Serializable和Read committed級別。
演示不同隔離級別下的并發問題
1.當把事務的隔離級別設置為read uncommitted時,會引發臟讀、不可重復讀和虛讀
A窗口
set transaction isolation level read uncommitted;
start transaction;
select * from account;
—–發現a帳戶是1000元,轉到b窗口
select * from account
—–發現a多了100元,這時候a讀到了b未提交的數據(臟讀)
B窗口
start transaction;
update account set money=money+100 where name=’aaa’;
—–不要提交,轉到a窗口查詢
2.當把事務的隔離級別設置為read committed時,會引發不可重復讀和虛讀,但避免了臟讀
A窗口
set transaction isolation level read committed;
start transaction;
select * from account;
—–發現a帳戶是1000元,轉到b窗口
select * from account;
—–發現a帳戶多了100,這時候,a讀到了別的事務提交的數據,兩次讀取a帳戶讀到的是不同的結果(不可重復讀)
B窗口
start transaction;
update account set money=money+100 where name=’aaa’;
commit;
—–轉到a窗口
3.當把事務的隔離級別設置為repeatable read(mysql默認級別)時,會引發虛讀,但避免了臟讀、不可重復讀
A窗口
set transaction isolation level repeatable read;
start transaction;
select * from account;
—-發現表有4個記錄,轉到b窗口
select * from account;
—-可能發現表有5條記如,這時候發生了a讀取到另外一個事務插入的數據(虛讀)
B窗口
start transaction;
insert into account(name,money) values(‘ggg’,1000);
commit;
—–轉到 a窗口
4.當把事務的隔離級別設置為Serializable時,會避免所有問題
A窗口
set transaction isolation level Serializable;
start transaction;
select * from account;
—–轉到b窗口
B窗口
start transaction;
insert into account(name,money) values(‘ggg’,1000);
—–發現不能插入,只能等待a結束事務才能插入
總結
以上是生活随笔為你收集整理的方立勋_30天掌握JavaWeb_JDBC、存储过程、事务(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EL表达式取 Map、 List和数组的
- 下一篇: 【置顶】方立勋JavaWeb学习地址