本文查閱方法:
一、查閱目錄 —— 查閱本文目錄,肯定想要查閱的目錄標題
二、快捷「查找」 —— 在當前瀏覽器頁面,按鍵 「Ctrl+F」 按鍵組合,開啓瀏覽器的查找功能,
在查找搜索框中 輸入須要查閱的 目錄標題,即可以直接到達 標題內容 的位置。
三、學習小結 —— 文中的學習小結內容,是筆者在學習以後總結出的,開發時可直接參考其進行應用開發的內容, 進一步加快了本文的查閱 速度。(水平有限,僅供參考。)
java
本文目錄mysql
學習小結sql
一、使用JDBC處理大數據數據庫
二、使用JDBC處理大文本瀏覽器
三、使用JDBC處理二進制數據併發
四、Oracle中大數據處理oracle
五、使用JDBC進行批處理框架
六、採用Statement.addBatch(sql)方式實現批處理的優缺點函數
七、實現批處理的第二種方式:PreparedStatement.addBatch() 學習
八、採用PreparedStatement.addBatch()實現批處理的優缺點
九、得到MySQL數據庫自動生成的主鍵
十、JDBC調用存儲過程
十一、事務的概念
十二、JDBC控制事務語句
1三、事務的四大特性(ACID)
1四、事務的隔離級別
1五、事務的隔離性可避免問題—— 髒讀:
1六、事務的隔離性可避免問題—— 不可重複讀
1七、事務的隔離性可避免問題—— 虛讀(幻讀)
1八、事務隔離性的設置語句
1九、在MySQL客戶端窗口界面演示事務的四種隔離級別。
相關學習
JDBC 學習筆記(一)—— 基礎知識 + 分頁技術
連接地址:http://even2012.iteye.com/blog/1886946
JDBC 學習筆記(二)—— 大數據+存儲過程+批處理+事務
連接地址:http://even2012.iteye.com/blog/1886950
JDBC 學習筆記(三)—— 數據源(數據庫鏈接池):DBCP數據源、C3P0 數據源以及自定義數據源技術
連接地址:http://even2012.iteye.com/blog/1886953
JDBC 學習筆記(四)—— 自定義JDBC框架+Apache—DBUtils框架+事務管理+操做多表
連接地址:http://even2012.iteye.com/blog/1886956
學習小結
一、使用JDBC處理大數據
在實際開發中,程序須要把大文本或二進制數據保存到數據庫。
基本概念:大數據也稱之爲LOB(Large Objects),LOB又分爲:clob和blob
(a)clob用於存儲大文本。(mysql 中採用Text)
(b)blob用於存儲二進制數據,例如圖像、聲音、二進制文等。
對MySQL而言只有blob,而沒有clob,mysql存儲大文本採用的是Text,其體系中的Text和blob分別又分爲:
(a)Text ——TINYTEXT、TEXT、MEDIUMTEXT和LONGTEXT
(b)blob ——TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB
二、使用JDBC處理大文本
(1) 保存數據—— 對於MySQL中的Text類型,可調用以下方法設置:
PreparedStatement.setCharacterStream(i, reader, length);
//注意length長度須設置,而且設置爲int型
(2) 獲取數據—— 對於MySQL中的Text類型,可調用以下方法獲取:
(a) reader = resultSet. getCharacterStream(i);
(b) reader = resultSet.getClob(i).getCharacterStream();
(c) string s = resultSet.getString(i);
Demo樣例:
public class Demo1 {
/**讀寫大文本
create table testclob
(
id varchar(40) primary key,
resume text
);
*/
public void insert() throws SQLException, FileNotFoundException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into testclob(id,resume) values(?,?)";
st = conn.prepareStatement(sql);
st.setString(1, "1");
File file = new File("src/1.txt");
FileReader reader = new FileReader(file);
st.setCharacterStream(2, reader, (int) file.length());
int num = st.executeUpdate();
if(num>0){
System.out.println("插入成功!!");
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
public void read() throws SQLException, IOException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select id,resume from testclob where id='1'";
st = conn.prepareStatement(sql);
rs = st.executeQuery();
if(rs.next()){
//String resume = rs.getString("resume");
Reader reader = rs.getCharacterStream("resume");
FileWriter writer = new FileWriter("c:\\1.txt");
try {
int len = 0;
char buffer[] = new char[1024];
while ((len = reader.read(buffer)) > 0) {
writer.write(buffer, 0, len);
}
} finally {
if (reader != null) {
reader.close();
}
writer.close();
}
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
三、使用JDBC處理二進制數據
(1) 保存數據—— 對於MySQL中的BLOB類型,可調用以下方法設置:
PreparedStatement. setBinaryStream(i , inputStream, length);
(2) 獲取數據—— 對MySQL中的BLOB類型,可調用以下方法獲取:
(a) InputStream in = resultSet.getBinaryStream(i);
(b) InputStream in = resultSet.getBlob(i).getBinaryStream();
Demo樣例:
public class Demo2 {
/*
create table testblob
(
id varchar(40) primary key,
image blob
);
*/
public void insert() throws SQLException, FileNotFoundException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into testblob(id,image) values(?,?)";
st = conn.prepareStatement(sql);
st.setString(1, "1");
File file = new File("src/1.jpg");
FileInputStream in = new FileInputStream(file);
st.setBinaryStream(2, in, (int) file.length());
st.executeUpdate();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
public void read() throws SQLException, IOException{
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "select id,image from testblob where id='1'";
rs = conn.prepareStatement(sql).executeQuery();
if(rs.next()){
InputStream in = rs.getBinaryStream("image");
OutputStream out = new FileOutputStream("c:\\1.jpg");;
try {
int len = 0;
byte buffer[] = new byte[1024];
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
} finally {
if (in != null)
in.close();
if (out != null)
out.close();
}
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
四、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往數據庫讀寫數據
注意:以上操做需開啓事務。
備註:本文關於Oracle中LOB類型數據處理的操做僅供參考,詳細內容參見 有關Oracle的博文。
五、使用JDBC進行批處理
業務場景:當須要向數據庫發送一批SQL語句執行時,應避免向數據庫一條條的發送執行,而應採用JDBC的批處理機制,以提高執行效率。
實現批處理有兩種方式,
第一種方式: Statement.addBatch(sql) (實際上是將sql語句 放在了一個 list 集合中。)
第二種方式: PreparedStatement.addBatch() (實際上是將sql語句 放在了一個 list 集合中。)
執行批處理SQL語句
executeBatch()方法:執行批處理命令
clearBatch()方法:清除批處理命令(其實是清除 List集合中的SQL語句,不然會形成內存溢出。)
Demo樣例:第一種方式:Statement.addBatch(sql)
@Test
public void test1() throws SQLException{
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtil.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();
st.clearBatch();
} finally{
JdbcUtil.free(conn, 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’);
七、實現批處理的第二種方式:PreparedStatement.addBatch()
Demo樣例:第二種 方式
@Test
public void test2() throws SQLException{
conn = JdbcUtil.getConnection();
String sql = "insert into user(name,password,email,birthday) values(?,?,?,?)";
st = conn.prepareStatement(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();
if(i%1000==0){ //爲防止(list集合) 內存溢出:設定每累加1000條數據就向數據庫發送一次
st.executeBatch();
st.clearBatch();
}
}
st.executeBatch(); //當剩餘的條數小於1000條時就不會被髮送到數據庫,因此此處要在發送一次。
}
八、採用PreparedStatement.addBatch()實現批處理的優缺點
優勢:發送的是預編譯後的SQL語句,執行效率高。
缺點:只能應用在SQL語句相同,但參數不一樣的批處理中。所以此種形式的批處理常常用於在同一個表中批量插入數據,或批量更新表的數據。
九、得到MySQL數據庫自動生成的主鍵
示例:
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操做有效。
Demo樣例:
public class Demo4 {
/**
獲取自動生成的主鍵
create table test
(
id int primary key auto_increment,
name varchar(40)
);
*/
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
String sql = "insert into test(name) values('aaa')";
st = conn.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
st.executeUpdate();
rs = st.getGeneratedKeys();
if(rs.next()){
System.out.println(rs.getInt(1));
}
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
十、JDBC調用存儲過程
編寫存儲過程(參看mysql文檔)
本文重在JDBC對存儲過程的調用,關於其知識內容,將在《Oracle數據庫 知識》博文中詳細介紹
存儲過程Demo樣例:
CREATE PROCEDURE demoSp (IN inputParam VARCHAR(255) ,INOUT inOutParam varchar(255) )
BEGIN
SELECT CONCAT ( 'ZYXW---- ', inputParam ) into inOutParam ;
END
在Java中,JDBC對存儲過程的調用 (這纔是本文重點):
(1) 獲得CallableStatement,並調用存儲過程:
CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");
(2) 設置參數,註冊返回值,獲得輸出
cStmt.setString(1, "abcdefg");
cStmt.registerOutParameter(2, Types.VARCHAR); //類型參數值參見JDK中:java.sql.Types
cStmt.execute();
System.out.println(cStmt.getString(2));
小常識:「存儲過程」在金融證券 行業中應用的很是普遍,而且將會保密其表結構和字段,徹底使用公開的存儲過程來實現表數據的調用。
Demo樣例:
public class Demo5 {
/**
調用存儲過程
* @throws SQLException
*/
public static void main(String[] args) throws SQLException {
Connection conn = null;
CallableStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
st = conn.prepareCall("{call demoSp(?,?)}");
st.setString(1, "aaaaa");
st.registerOutParameter(2, Types.VARCHAR);
st.execute();
System.out.println(st.getString(2));
}finally{
JdbcUtils.release(conn, st, rs);
}
}
}
十一、事務的概念
事務指邏輯上的一組操做,組成這組操做的各個單元,要麼所有都成功,要麼所有不成功。
例如:A——B轉賬,對應於以下兩條sql語句
update from account set money=money+100 where name=‘b’;
update from account set money=money-100 where name=‘a’;
數據庫默認事務是自動提交的,也就是發一條sql它就執行一條。若是想多條sql放在一個事務中執行,則須要使用以下語句。
數據庫開啓事務命令
start transaction 開啓事務
Rollback 回滾事務
Commit 提交事務
Demo:
Start transaction
…
…
commit
Demo樣例:
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try{
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false); //start transaction; 開啓事務
String sql1 = "update account set money=money-100 where name='aaa'";
String sql2 = "update account set money=money+100 where name='bbb'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
int x = 1/0; // 人爲製造異常,驗證事務
st = conn.prepareStatement(sql2);
st.executeUpdate();
conn.commit(); // Commit 提交事務
}finally{
JdbcUtils.release(conn, st, rs);
}
}
十二、JDBC控制事務語句
當Jdbc程序向數據庫得到一個Connection對象時,默認狀況下這個Connection對象會自動向數據庫提交在它上面發送的SQL語句。若想關閉這種默認提交方式,讓多條SQL在一個事務中執行,可以使用下列語句:
(1) JDBC控制事務語句
Connection.setAutoCommit(false); //至關於 start transaction
Connection.rollback(); //至關於 rollback
Connection.commit(); //至關於 commit
(2) 設置事務回滾點
Savepoint sp = conn.setSavepoint();
Conn.rollback(sp); // 會滾到指定的位置,該位置以前的操做任然有效執行,由於須要被提交
Conn.commit(); //回滾後必需要提交
Demo樣例:事務回滾點——演示銀行轉賬案例
public static void main(String[] args) throws SQLException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
Savepoint sp = null;
try{
conn = JdbcUtils.getConnection(); //mysql repeatable read
conn.setAutoCommit(false); //start transaction;
String sql1 = "update account set money=money-100 where name='aaa'";
String sql2 = "update account set money=money+100 where name='bbb'";
String sql3 = "update account set money=money+100 where name='ccc'";
st = conn.prepareStatement(sql1);
st.executeUpdate();
sp = conn.setSavepoint();
st = conn.prepareStatement(sql2);
st.executeUpdate();
int x = 1/0;
st = conn.prepareStatement(sql3);
st.executeUpdate();
conn.commit();
}catch (Exception e) {
e.printStackTrace();
conn.rollback(sp);
conn.commit(); //手動回滾後,必定要記得提交事務
}finally{
JdbcUtils.release(conn, st, rs);
}
}
1三、事務的四大特性(ACID)
(1) 原子性(Atomicity) 原子性是指事務是一個不可分割的工做單位,事務中的操做要麼都發生,要麼都不發生。
(2) 一致性(Consistency) 事務先後,數據的完整性必須保持一致。
(3) 隔離性(Isolation) 事務的隔離性是多個用戶併發訪問數據庫時,數據庫爲每個用戶開啓的事務,不能被其餘事務的操做數據所幹擾,多個併發事務之間要相互隔離。
(4) 持久性(Durability) 持久性是指一個事務一旦被提交,它對數據庫中數據的改變就是永久性的,接下來即便數據庫發生故障也不該該對其有任何影響。
1四、事務的隔離級別
多個線程開啓各自事務操做數據庫中數據時,數據庫系統要負責隔離操做,以保證各個線程在獲取數據時的準確性。
若是不考慮隔離性,可能會引起問題:髒讀、不可重複讀、虛讀(幻讀) 。
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元。
1六、事務的隔離性可避免問題—— 不可重複讀
不可重複讀:在一個事務內讀取表中的某一行數據,屢次讀取結果不一樣。【針對同一行數據】
不可重複讀和髒讀的區別:髒讀是讀取前一事務未提交的髒數據,不可重複讀是從新讀取了前一事務已提交的數據。
例如銀行想查詢A賬戶餘額,第一次查詢A賬戶爲200元,此時A向賬戶存了100元並提交了,銀行接着又進行了一次查詢,此時A賬戶爲300元了。銀行兩次查詢不一致,可能就會很困惑,不知道哪次查詢是準的。
不少人認爲這種狀況就對了,無須困惑,固然是後面的爲準。咱們能夠考慮這樣一種狀況,好比銀行程序須要將查詢結果分別輸出到電腦屏幕和寫到文件中,結果在一個事務中針對輸出的目的地,進行的兩次查詢不一致,致使文件和屏幕中的結果不一致,銀行工做人員就不知道以哪一個爲準了。
1七、事務的隔離性可避免問題—— 虛讀(幻讀)
虛讀(幻讀) :是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。【針對整張表】
如丙存款100元未提交,這時銀行作報表統計account表中全部用戶的總額爲500元,而後丙提交了,這時銀行再統計發現賬戶爲600元了,形成虛讀一樣會使銀行不知所措,到底以哪一個爲準。
1八、事務隔離性的設置語句
數據庫共定義了四種隔離級別:
(1) Serializable:可避免髒讀、不可重複讀、虛讀狀況的發生。(串行化,近似於單線程操做。)
(2) Repeatable read:可避免髒讀、不可重複讀狀況的發生。(可重複讀)
(3) Read committed:可避免髒讀狀況發生(讀已提交)。
(4) Read uncommitted:最低級別,以上狀況均沒法保證。(讀未提交)
set transaction isolation level 設置事務隔離級別
select @@tx_isolation 查詢當前事務隔離級別
Demo樣例:Java中設定事務的隔離級別。
public static void main(String[] args) throws SQLException, InterruptedException {
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
Savepoint sp = null;
try{
conn = JdbcUtils.getConnection(); //mysql repeatable read
conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
conn.setAutoCommit(false); //start transaction;
String sql = "select * from account";
conn.prepareStatement(sql).executeQuery();
Thread.sleep(1000*20); // 等待另外一個程序運行一樣的代碼以校驗事務
conn.commit();
}finally{
JdbcUtils.release(conn, st, rs);
}
}
1九、在MySQL客戶端窗口界面掩飾事務的四種隔離級別。
(1) 演示髒讀發生
a窗口
set transaction isolation level read uncommitted;
start transaction;
select * from account;
-------------發現a賬戶是1000元,轉到b窗口
select * from account;
-------------發現a賬戶是1100元,發生了髒讀(這個事務讀取到了別的事務未提交的數據)
b窗口
start transaction;
update account set money=money+100 where name='aaa';
-------------事務在不提交的狀況下,轉到a窗口進行查詢
(2) 避免髒讀,並演示不可重複讀問題的發生
a窗口
set transaction isolation level read committed;
start transaction;
select * from account;
-------------發現a賬戶是1000元,轉到b窗口
select * from account;
-------------發現a賬戶是1000元,這時咱們發現read committed這種級別能夠避免髒讀
-------------轉到b窗口
select * from account;
-------------發現a賬戶是1100元,這時就發生不可重複讀(指這個事務讀取到了別的事務提交的數據)
b窗口
start transaction;
update account set money=money+100 where name='aaa';
-------------事務在不提交的狀況下,轉到a窗口進行查詢
commit;
-------------轉到a窗口
(3)避免髒讀、不可重複讀,並演示虛讀問題的發生
a窗口
set transaction isolation level repeatable read;
start transaction;
select * from account;
--------------------發現a賬戶是1000元,而且表的總紀錄數是3條,這時轉到b窗口
select * from account
--------------------發現a賬戶是1000元,這說明repeatable read這種級別可避免髒讀
-------------------轉到b窗口
select * from account
---------------------發現a賬戶是1000元,這說明repeatable read這種級別還能夠避免不可重複讀
---------------------轉到b窗口
select * from account
---------------------發現表中可能會多出一條ddd的記錄,這就發生了虛讀,也就是在這個事務內讀取了別的事務插入的數據(幻影數據)
b窗口
start transaction;
update account set money=money+100 where name='aaa';
---------------------轉到a窗口
commit;
---------------------轉到a窗口
start transaction;
insert into account(name,money) values('ddd','1000');
commit;
--------------------轉到a窗口
敬請評論(1)若您以爲本文 有用處 —— 請留言評論,以堅決其餘 IT童鞋 閱讀本文的信心。(2)若您以爲本文 沒用處 —— 請留言評論,筆者將會改進不足,以便爲你們整理更加好用的筆記。