問題:事務(wù)是什么牛欢,有什么用?
事務(wù)就是一個事情,組成這個事情可能有多個單元淆游,要求這些單元傍睹,要么全都成功,要么全都不成功犹菱。
在開發(fā)中焰望,有事務(wù)的存在,可以保證數(shù)據(jù)完整性已亥。
問題:事務(wù)怎樣操作
創(chuàng)建表:
create table account(
id int primary key auto_increment,
name varchar(20),
money double
);
insert into account values(null,'aaa',1000);
insert into account values(null,'bbb',1000);
insert into account values(null,'ccc',1000);
1.mysql下怎樣操作
方式1:
start transaction? 開啟事務(wù)
rollback 事務(wù)回滾
commit 事務(wù)提交
方式2:
show variables like '%commit%'; 可以查看當(dāng)前autocommit值
在mysql數(shù)據(jù)庫中它的默認(rèn)值是"on"代表自動事務(wù).
自動事務(wù)的意義就是:執(zhí)行任意一條sql語句都會自動提交事務(wù).
測試:將autocommit的值設(shè)置為off
1.set autocommit=off 關(guān)閉自動事務(wù)熊赖。
2.必須手動commit才可以將事務(wù)提交。
注意:mysql默認(rèn)autocommit=on? oracle默認(rèn)的autocommit=off;
2.jdbc下怎樣操作
java.sql.Connection接口中有幾個方法是用于可以操作事務(wù)
1.setAutocommit(boolean flag);
如果flag=false;它就相當(dāng)于start transaction;
2.rollBack()
事務(wù)回滾虑椎。
3.commit()
事務(wù)提交
事務(wù)特性(重點) ACID
? 原子性(Atomicity)
原子性是指事務(wù)是一個不可分割的工作單位震鹉,事務(wù)中的操作要么都發(fā)生,要么都不發(fā)生捆姜。
? 一致性(Consistency)
事務(wù)前后數(shù)據(jù)的完整性必須保持一致传趾。
? 隔離性(Isolation)
事務(wù)的隔離性是指多個用戶并發(fā)訪問數(shù)據(jù)庫時,一個用戶的事務(wù)不能被其它用戶的事務(wù)所干擾泥技,多個并發(fā)事務(wù)之間數(shù)據(jù)要相互隔離浆兰。
? 持久性(Durability)
持久性是指一個事務(wù)一旦被提交,它對數(shù)據(jù)庫中數(shù)據(jù)的改變就是永久性的珊豹,接下來即使數(shù)據(jù)庫發(fā)生故障也不應(yīng)該對其有任何影響
-------------------------------------
如果不考慮事務(wù)的隔離性簸呈,會出現(xiàn)什么問題?
1.臟讀 一個事務(wù)讀取到另一個事務(wù)的未提交數(shù)據(jù)。
2.不可重復(fù)讀
兩次讀取的數(shù)據(jù)不一致(update)
3.虛讀(幻讀)
兩次讀取的數(shù)據(jù)一一致(insert)
4.丟失更新
兩個事務(wù)對同一條記錄進(jìn)行操作店茶,后提交的事務(wù)蜕便,將先提交的事務(wù)的修改覆蓋了。
-----------------------------------------
演示以上問題贩幻,以及問題的解決方案
對于以上的問題轿腺,我們可以通過設(shè)置事務(wù)的隔離級別來解決。
1.事務(wù)的隔離級別有哪些?
1 Serializable:可避免臟讀丛楚、不可重復(fù)讀族壳、虛讀情況的發(fā)生。(串行化)
2 Repeatable read:可避免臟讀趣些、不可重復(fù)讀情況的發(fā)生仿荆。(可重復(fù)讀)不可以避免虛讀
3 Read committed:可避免臟讀情況發(fā)生(讀已提交)
4 Read uncommitted:最低級別,以上情況均無法保證。(讀未提交)
2.怎樣設(shè)置事務(wù)的隔離級別?
1.mysql中設(shè)置
1.查看事務(wù)隔離級別
select @@tx_isolation 查詢當(dāng)前事務(wù)隔離級別
mysql中默認(rèn)的事務(wù)隔離級別是? Repeatable read.
擴(kuò)展:oracle 中默認(rèn)的事務(wù)隔離級別是? Read committed
2.mysql中怎樣設(shè)置事務(wù)隔離級別
set session transaction isolation level 設(shè)置事務(wù)隔離級別
2.jdbc中設(shè)置
在jdbc中設(shè)置事務(wù)隔離級別
使用java.sql.Connection接口中提供的方法
void setTransactionIsolation(int level) throws SQLException
參數(shù)level可以取以下值:
level - 以下 Connection 常量之一:
Connection.TRANSACTION_READ_UNCOMMITTED赖歌、
Connection.TRANSACTION_READ_COMMITTED枉圃、
Connection.TRANSACTION_REPEATABLE_READ
Connection.TRANSACTION_SERIALIZABLE。
(注意庐冯,不能使用 Connection.TRANSACTION_NONE孽亲,因為它指定了不受支持的事務(wù)。)
--------------------------------------------------
3.演示
1.臟讀
一個事務(wù)讀取到另一個事務(wù)的為提交數(shù)據(jù)
設(shè)置A,B事務(wù)隔離級別為? Read uncommitted
set session transaction isolation level? read uncommitted;
1.在A事務(wù)中
start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';
2.在B事務(wù)中
start transaction;
select * from account;
這時展父,B事務(wù)讀取時返劲,會發(fā)現(xiàn),錢已經(jīng)匯完栖茉。那么就出現(xiàn)了臟讀篮绿。
當(dāng)A事務(wù)提交前,執(zhí)行rollback吕漂,在commit亲配, B事務(wù)在查詢,就會發(fā)現(xiàn)惶凝,錢恢復(fù)成原樣
也出現(xiàn)了兩次查詢結(jié)果不一致問題吼虎,出現(xiàn)了不可重復(fù)讀.
2.解決臟讀問題
將事務(wù)的隔離級別設(shè)置為 read committed來解決臟讀
設(shè)置A,B事務(wù)隔離級別為? Read committed
set session transaction isolation level? read committed;
1.在A事務(wù)中
start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';
2.在B事務(wù)中
start transaction;
select * from account;
這時B事務(wù)中,讀取信息時苍鲜,是不能讀到A事務(wù)未提交的數(shù)據(jù)的思灰,也就解決了臟讀。
讓A事務(wù)混滔,提交數(shù)據(jù) commit;
這時洒疚,在查詢,這次結(jié)果與上一次查詢結(jié)果又不一樣了坯屿,還存在不可重復(fù)讀油湖。
3.解決不可重復(fù)讀
將事務(wù)的隔離級別設(shè)置為Repeatable read來解決不可重復(fù)讀。
設(shè)置A,B事務(wù)隔離級別為? Repeatable read;
set session transaction isolation level? Repeatable read;
1.在A事務(wù)中
start transaction;
update account set money=money-500 where name='aaa';
update account set money=money+500 where name='bbb';
2.在B事務(wù)中
start transaction;
select * from account;
當(dāng)A事務(wù)提交后commit;B事務(wù)在查詢愿伴,與上次查詢結(jié)果一致肺魁,解決了不可重復(fù)讀电湘。
4.設(shè)置事務(wù)隔離級別 Serializable ,它可以解決所有問題
set session transaction isolation level Serializable;
如果設(shè)置成這種隔離級別隔节,那么會出現(xiàn)鎖表。也就是說寂呛,一個事務(wù)在對表進(jìn)行操作時怎诫,
其它事務(wù)操作不了。
--------------------------------------------------
總結(jié):
臟讀:一個事務(wù)讀取到另一個事務(wù)為提交數(shù)據(jù)
不可重復(fù)讀:兩次讀取數(shù)據(jù)不一致(讀提交數(shù)據(jù))---update
虛讀:兩次讀取數(shù)據(jù)不一致(讀提交數(shù)據(jù))----insert
事務(wù)隔離級別:
read uncommitted 什么問題也解決不了.
read committed 可以解決臟讀贷痪,其它解決不了.
Repeatable read 可以解決臟讀幻妓,可以解決不可重復(fù)讀,不能解決虛讀.
Serializable 它會鎖表,可以解決所有問題.
安全性:serializable > repeatable read > read committed > read uncommitted
性能 :serializable < repeatable read < read committed < read uncommitted
結(jié)論: 實際開發(fā)中劫拢,通常不會選擇 serializable 和 read uncommitted 肉津,
mysql默認(rèn)隔離級別 repeatable read 强胰,oracle默認(rèn)隔離級別 read committed
==========================================================================================
案例:轉(zhuǎn)賬匯款----使用事務(wù)
問題:service調(diào)用了dao中兩個方法完成了一個業(yè)務(wù)操作,如果其中一個方法執(zhí)行失敗怎樣辦?
需要事務(wù)控制
問題:怎樣進(jìn)行事務(wù)控制?
我們在service層進(jìn)行事務(wù)的開啟妹沙,回滾以及提交操作偶洋。
問題:進(jìn)行事務(wù)操作需要使用Connection對象,那么距糖,怎樣保證玄窝,在service中與dao中所使用的是同一個Connection.
在service層創(chuàng)建出Connection對象,將這個對象傳遞到dao層.
注意:Connecton對象使用完成后悍引,在service層的finally中關(guān)閉
而每一個PreparedStatement它們在dao層的方法中用完就關(guān)閉.
關(guān)于程序問題
1.對于轉(zhuǎn)入與轉(zhuǎn)出操作恩脂,我們需要判斷是否成功,如果失敗了趣斤,可以通過拋出自定義異常在servlet中判斷俩块,
進(jìn)行信息展示 。
----------------------------------------------------------問題:在設(shè)置dao層時浓领,public interface AccountDao {public void accountOut(String accountOut, double money) throws Exception;public void accountIn(String accountIn, double money) throws Exception;}那么我們自己去實現(xiàn)這個接口時典阵,怎樣處理,同一個Connection對象問題?使用ThreadLocalThreadLocal可以理解成是一個Map集合Mapset方法是向ThreadLocal中存儲數(shù)據(jù)镊逝,那么當(dāng)前的key值就是當(dāng)前線程對象.get方法是從ThreadLocal中獲取數(shù)據(jù)壮啊,它是根據(jù)當(dāng)前線程對象來獲取值。如果我們是在同一個線程中撑蒜,只要在任意的一個位置存儲了數(shù)據(jù)歹啼,在其它位置上,就可以獲取到這個數(shù)據(jù)座菠。關(guān)于JdbcUtils中使用ThreadLocal1.聲明一個ThreadLocalprivate static final ThreadLocaltl = new ThreadLocal();
2.在getConnection()方法中操作
Connection con = tl.get(); 直接從ThreadLocal中獲取狸眼,第一次返回的是null.
if (con == null) {
// 2.獲取連接
con = DriverManager.getConnection(URL, USERNAME, PASSWORD);
tl.set(con); //將con裝入到ThreadLocal中。
}
==========================================================================================
丟失更新
多個事務(wù)對同一條記錄進(jìn)行了操作浴滴,后提交的事務(wù)將先提交的事務(wù)操作覆蓋了拓萌。
查看圖.
問題:怎樣解決丟失更新問題?
解決丟失更新可以采用兩種方式:
1.悲觀鎖
悲觀鎖 (假設(shè)丟失更新一定會發(fā)生 ) ----- 利用數(shù)據(jù)庫內(nèi)部鎖機(jī)制,管理事務(wù)
提供的鎖機(jī)制
1.共享鎖
select * from table lock in share mode(讀鎖升略、共享鎖)
2.排它鎖
select * from table for update (寫鎖微王、排它鎖)
update語句默認(rèn)添加排它鎖
2.樂觀鎖
樂觀鎖 (假設(shè)丟失更新不會發(fā)生)------- 采用程序中添加版本字段解決丟失更新問題
create table product (
id int,
name varchar(20),
updatetime timestamp
);
insert into product values(1,'冰箱',null);
update product set name='洗衣機(jī)' where id = 1;
解決丟失更新:在數(shù)據(jù)表添加版本字段,每次修改過記錄后品嚣,版本字段都會更新炕倘,如果讀取是版本字段,
與修改時版本字段不一致翰撑,說明別人進(jìn)行修改過數(shù)據(jù) (重改)
===============================================================================================================
連接池 問題:連接池是什么罩旋,有什么用?
連接池:就是創(chuàng)建一個容器,用于裝入多個Connection對象,在使用連接對象時涨醋,從容器中獲取一個Connection瓜饥,? 使用完成后,在將這個Connection重新裝入到容器中浴骂。這個容器就是連接池压固。(DataSource)? 也叫做數(shù)據(jù)源.? 我們可以通過連接池獲取連接對象.優(yōu)點:節(jié)省創(chuàng)建連接與釋放連接 性能消耗 ---- 連接池中連接起到復(fù)用的作用 ,提高程序性能-----------------------------------------------------------------------------------自定義連接池
1.創(chuàng)建一個MyDataSource類靠闭,在這個類中創(chuàng)建一個LinkedList
2.在其構(gòu)造方法中初始化List集合帐我,并向其中裝入5個Connection對象。
3.創(chuàng)建一個public Connection getConnection();從List集合中獲取一個連接對象返回.
4.創(chuàng)建一個? public void readd(Connection) 這個方法是將使用完成后的Connection對象重新裝入到List集合中.
代碼問題:
1.連接池的創(chuàng)建是有標(biāo)準(zhǔn)的.
在javax.sql包下定義了一個接口 DataSource
簡單說愧膀,所有的連接池必須實現(xiàn)javax.sql.DataSource接口拦键,
我們的自定義連接池必須實現(xiàn)DataSource接口。
2.我們操作時檩淋,要使用標(biāo)準(zhǔn)芬为,怎樣可以讓 con.close()它不是銷毀,而是將其重新裝入到連接池.
要解決這個問題蟀悦,其本質(zhì)就是將Connection中的close()方法的行為改變媚朦。
怎樣可以改變一個方法的行為(對方法功能進(jìn)行增強(qiáng))
1.繼承
2.裝飾模式
1.裝飾類與被裝飾類要實現(xiàn)同一個接口或繼承同一個父類
2.在裝飾類中持有一個被裝飾類引用
3.對方法進(jìn)行功能增強(qiáng)。
3.動態(tài)代理
可以對行為增強(qiáng)
Proxy.newProxyInstance(ClassLoacer ,Class[],InvocationHandler);
結(jié)論:Connection對象如果是從連接池中獲取到的日戈,那么它的close方法的行為已經(jīng)改變了询张,不在是銷毀,而是重新裝入到連接池浙炼。
--------------------------------------------------------------------
1.連接池必須實現(xiàn)javax.sql.DataSource接口份氧。
2.要通過連接池獲取連接對象? DataSource接口中有一個? getConnection方法.
3.將Connection重新裝入到連接池? 使用Connection的close()方法。