java類和數(shù)據(jù)庫(kù)表的關(guān)系
類對(duì)應(yīng)表,字段對(duì)應(yīng)屬性讹剔,一行數(shù)據(jù)對(duì)應(yīng)一個(gè)對(duì)象把沼,所以類的屬性名要和表的字段名相同才能進(jìn)行增刪改查
? ? ? ?JDBC(Java Data Base Connectivity,java數(shù)據(jù)庫(kù)連接)是一種用于執(zhí)行SQL語(yǔ)句的Java API,可以為多種關(guān)系數(shù)據(jù)庫(kù)提供統(tǒng)一訪問(wèn)秩伞,它由一組用Java語(yǔ)言編寫的類和接口組成逞带。jdbc:java是通過(guò)jdbc技術(shù)實(shí)現(xiàn)對(duì)各種數(shù)據(jù)庫(kù)的訪問(wèn)的,換句話說(shuō),jdbc是java應(yīng)用程序與各種數(shù)據(jù)庫(kù)之間進(jìn)行對(duì)話的媒介,因此實(shí)際開(kāi)發(fā)中用JDBC鏈接數(shù)據(jù)庫(kù)后通過(guò)JDBC發(fā)送sql語(yǔ)句就可以操作數(shù)據(jù)庫(kù)纱新。
為什么使用JDBC
以前連接數(shù)據(jù)庫(kù)的方式:如果你想要連接某個(gè)數(shù)據(jù)庫(kù)掰担,就必須連接這個(gè)數(shù)據(jù)庫(kù)的API,
缺點(diǎn):不是跨平臺(tái)的怒炸,如果想換數(shù)據(jù)庫(kù)需要重新編寫代碼
如今的連接數(shù)據(jù)庫(kù)方式:不需要掌握特定數(shù)據(jù)庫(kù)的API带饱,程序員只需用JDBC API寫一個(gè)程序就夠了,它可向相應(yīng)數(shù)據(jù)庫(kù)發(fā)送SQL調(diào)用阅羹,同時(shí)勺疼,將Java語(yǔ)言和JDBC結(jié)合起來(lái)使程序員不必為不同的平臺(tái)編寫不同的應(yīng)用程序,只須寫一遍程序就可以讓它在任何平臺(tái)上運(yùn)行捏鱼,這也是Java語(yǔ)言“編寫一次执庐,處處運(yùn)行”的優(yōu)勢(shì)。
但是每一種數(shù)據(jù)庫(kù)JDBC的類庫(kù)是不同的导梆,所以每一種數(shù)據(jù)庫(kù)都提供了其專有的JDBC
三層應(yīng)用模型轨淌,在三層引用模型中,客戶端不直接調(diào)用數(shù)據(jù)庫(kù)看尼,而是調(diào)用中間層也就是JDBC完成數(shù)據(jù)庫(kù)的查詢工作
JDBC主要類和接口
一個(gè)Statement只能有一個(gè)打開(kāi)的結(jié)果集递鹉,所以當(dāng)你希望打開(kāi)兩個(gè)結(jié)果集,就要?jiǎng)?chuàng)建兩個(gè)statement對(duì)象
? ? ? ? ?一個(gè)Statement對(duì)象同時(shí)只能有一條語(yǔ)句結(jié)果集在活動(dòng).這是寬容性的,就是說(shuō)即使沒(méi)有調(diào)用ResultSet的close()方法,只要打開(kāi)第二個(gè)結(jié)果集就隱含著對(duì)上一個(gè)結(jié)果集的關(guān)閉.所以如果你想同時(shí)對(duì)多個(gè)結(jié)果集操作,就要?jiǎng)?chuàng)建多個(gè)Statement對(duì)象,如果不需要同時(shí)操作,那么可以在一個(gè)Statement對(duì)象上須序操作多個(gè)結(jié)果集.
可滾動(dòng)可更新的結(jié)果集ResultSet ? (了解)
不建議使用因?yàn)橛械臄?shù)據(jù)庫(kù)不支持藏斩,這就違背了數(shù)據(jù)庫(kù)的可移植性
默認(rèn)情況下結(jié)果集不可以滾動(dòng)躏结,所以需要建立一個(gè)不一樣的傳輸器對(duì)象
Statement stat=conn.Statement(type,concurrency)
PreparedStatement ps=conn.PreparedStatement(conmand,type,concurrency)
type:
ResultSet.TYPE_SCROLL_INSENSITIVE, ? ?可以滾動(dòng),但對(duì)數(shù)據(jù)庫(kù)變化不敏感(常用)
ResultSet.TYPE_SCROLL_SENSITIVE ? ?可滾動(dòng)狰域,對(duì)數(shù)據(jù)庫(kù)變化敏感
concurrency:
ResultSet.CONCUR_READ_ONLY ? ?并發(fā)訪問(wèn)只能夠讀取內(nèi)容媳拴,不可用于 更改表(常用)
ResultSet.CONCUR_UPDATETABLE ? ?并發(fā)訪問(wèn)可以通過(guò)更改結(jié)果集更改表
可滾動(dòng)不可更新結(jié)果集
(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)
ResultSet rs=ps.executeQuery()
此時(shí)這個(gè)結(jié)果集rs是滾動(dòng)的
常用方法 ?
rs.next() ?
?rs.last()?
?rs.isLast() ??
rs.getRow() ? 返回當(dāng)前記錄是第幾條記錄(當(dāng)前行行號(hào))
rs.previous() ?
rs.relative(n) ?向前或向后移動(dòng)多行 ?n為正數(shù)向前移動(dòng)黄橘,n為負(fù)數(shù)向后移動(dòng)
rs.absolute(int a) ?獲取指定位置的記錄
可滾動(dòng)可更新結(jié)果集(適用用戶任意修改得到的結(jié)果集數(shù)據(jù)的程序,大多數(shù)程序不建議用)
(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATETABLE)
行集(了解)
1.基于結(jié)果集的缺點(diǎn):在與用戶的整個(gè)交互過(guò)程中屈溉,必須始終與數(shù)據(jù)庫(kù)保持連接
后果:當(dāng)用戶長(zhǎng)時(shí)間離開(kāi)時(shí)塞关,數(shù)據(jù)庫(kù)連接長(zhǎng)時(shí)間被占用,而這屬于稀缺資源子巾;
解決:使用行集RowSet描孟,RowSet繼承了ResultSet接口,卻無(wú)需始終保持與數(shù)據(jù)庫(kù)的連接~
2.結(jié)果集不便于移動(dòng)砰左,因?yàn)閿?shù)據(jù)結(jié)構(gòu)復(fù)雜匿醒,且依賴于連接
解決:使用行集RowSet,RowSet適用于將查詢結(jié)果移動(dòng)到復(fù)雜應(yīng)用的其他層缠导,或者其他設(shè)備當(dāng)中
3.因?yàn)镽owSet繼承了ResultSet接口廉羔,所以ResultSet中的方法RowSet都能用,RowSet是對(duì)ResultSet離線化僻造、移動(dòng)化的增強(qiáng)
sql注入
select * from t_user where username='張三'? and password='2' or? '1'='1'
? ? ? ? 在使用Statement的時(shí)候憋他,由于這里屬于硬編碼,也就是只要滿足sql語(yǔ)句的查詢條件為true就能查詢出數(shù)據(jù)髓削,但是查詢條件不是我們給定的竹挡,這種情況就是sql注入
解決方法
使用Statement的子接口PreparedStatement,它可以對(duì)sql語(yǔ)句進(jìn)行預(yù)編譯
SQL語(yǔ)句被預(yù)編譯并存儲(chǔ)在PreparedStatement對(duì)象中立膛。然后可以使用此對(duì)象多次高效地執(zhí)行該語(yǔ)句揪罕。
預(yù)編譯
? ? ? ? ?通過(guò)Statement對(duì)象執(zhí)行sql語(yǔ)句時(shí),需要將sql語(yǔ)句發(fā)送給DBMS(數(shù)據(jù)庫(kù)管理系統(tǒng)),由DBMS首先進(jìn)行編譯再執(zhí)行(在創(chuàng)建通道的時(shí)候并不進(jìn)行sql的編譯工作宝泵,事實(shí)上也無(wú)法進(jìn)行編譯)好啰。而通過(guò)PreparedStatement不同,在創(chuàng)建PreparedStatement對(duì)象時(shí)就指定了sql語(yǔ)句儿奶,該語(yǔ)句立即發(fā)送給DBMS進(jìn)行編譯框往,當(dāng)該語(yǔ)句被執(zhí)行時(shí),DBMS直接運(yùn)行編譯后的sql語(yǔ)句闯捎,而不需要像其他sql語(yǔ)句那樣首先將被編譯椰弊。?
1.提高了效率,因?yàn)樵谶\(yùn)行前不需要再進(jìn)行一次編譯(批處理)瓤鼻,SQL語(yǔ)句被預(yù)編譯并存儲(chǔ)在PreparedStatement對(duì)象中秉版。然后可以使用此對(duì)象多次高效地執(zhí)行該語(yǔ)句。
2.提高了安全性(防止sql注入)娱仔,編譯的時(shí)候就明確了查詢條件沐飘,之后每一次傳入值游桩,只會(huì)把傳入的值和表里的字段進(jìn)行匹配牲迫。
3. PreparedStatement可以寫動(dòng)態(tài)參數(shù)化的查詢耐朴,用? ? 代替參數(shù)的時(shí)候不需要明確參數(shù)類型盹憎,所以在運(yùn)行的時(shí)候傳入任何類型的參數(shù)都可以
注:Statement主要用于執(zhí)行靜態(tài)SQL語(yǔ)句筛峭,即內(nèi)容固定不變的SQL語(yǔ)句。所以以后寫程序99%用PreparedStatement
為什么它這樣處理就能預(yù)防SQL注入提高安全性呢陪每?
? ? ? ? 其實(shí)是因?yàn)镾QL語(yǔ)句在程序運(yùn)行前已經(jīng)進(jìn)行了預(yù)編譯影晓,在程序運(yùn)行時(shí)第一次操作數(shù)據(jù)庫(kù)之前,SQL語(yǔ)句已經(jīng)被數(shù)據(jù)庫(kù)分析檩禾,編譯和優(yōu)化挂签,對(duì)應(yīng)的執(zhí)行計(jì)劃(也就是確定了要執(zhí)行的sql語(yǔ)句)也會(huì)緩存下來(lái)并允許數(shù)據(jù)庫(kù)以參數(shù)化的形式進(jìn)行查詢,當(dāng)運(yùn)行時(shí)動(dòng)態(tài)地把參數(shù)傳給PreprareStatement時(shí)盼产,即使參數(shù)里有敏感字符如 or '1=1'數(shù)據(jù)庫(kù)也會(huì)把它作為一個(gè)參數(shù)一個(gè)字段的值去表格中尋找與它相等的字段值然后進(jìn)行sql語(yǔ)句操作而不會(huì)作為一個(gè)SQL的條件指令饵婆,如此,就起到了SQL注入的作用了戏售!
Statement是調(diào)用者寫sql語(yǔ)句侨核,而PreparedStatement是調(diào)用者賦參數(shù)值,然后參數(shù)值去表中指定字段中進(jìn)行比較灌灾,
所以前者調(diào)用者可能在where后面寫一個(gè)永遠(yuǎn)為true的語(yǔ)句搓译,這樣就能把所有的說(shuō)句查詢出來(lái),當(dāng)這個(gè)語(yǔ)句是判斷賬號(hào)密碼是否正確的時(shí)候锋喜,因?yàn)橛肋h(yuǎn)為true些己,所以調(diào)用者跳過(guò)了賬號(hào)密碼判斷是否正確的步驟,直接登陸了進(jìn)去嘿般,而后者調(diào)用者只能賦參數(shù)轴总,必須去表中的指定字段中尋找,只有賬號(hào)密碼都滿足的時(shí)候博个,才登陸了進(jìn)去怀樟。
Statement是滿足條件就執(zhí)行sql操作,而PreparedStatement是只有找到與指定字段相等的值的時(shí)候才進(jìn)行sql操作
建立傳輸器
String sql=“select * from t_user where username=盆佣? and password=往堡?”
PreparedStatement ps=conn.prepareStatement(sql)
這個(gè)sql的意義是進(jìn)行一次預(yù)編譯,所謂預(yù)編譯就是提前判斷該sql語(yǔ)句是否正確共耍,其中 虑灰?的作用是占位符,占位符的索引是從1開(kāi)始
給占位符賦值
ps.setString(第一個(gè)問(wèn)號(hào)位置(1)痹兜,賦的值) ? ??
ps.setString(1,"張三")
ps.setString(2,“123”)
執(zhí)行sql語(yǔ)句
缺點(diǎn):每一次只能預(yù)編譯一條sql語(yǔ)句
單表查詢時(shí)穆咐,你可以把查詢的內(nèi)容存到一個(gè)對(duì)象里,
?多表聯(lián)查的問(wèn)題
? ? ? ? ?在java當(dāng)中進(jìn)行多表聯(lián)查的時(shí)候往往不知道返回什么類的數(shù)據(jù),這個(gè)時(shí)候我們可有定義一個(gè)包裝類來(lái)解決這樣的問(wèn)題
如果是多表查詢对湃,建立一個(gè)類崖叫,將兩個(gè)表的類放到這個(gè)類里,查詢的結(jié)果存到這個(gè)新構(gòu)成的類
批量操作
? ? ? ? ?無(wú)論是增加還是刪除還是修改每做一次操作都需要連接一次數(shù)據(jù)庫(kù)拍柒,這樣頻繁的與數(shù)據(jù)庫(kù)進(jìn)行交互的話會(huì)嚴(yán)重影響程序的性能心傀,這個(gè)時(shí)候我們就需要使用批量操作例如向數(shù)據(jù)庫(kù)中一次性的插入1000條數(shù)據(jù)
addBatch添加到緩沖區(qū)
executeBatch更新緩沖區(qū)
clearBatch清空緩沖區(qū)
獲取未知的主鍵
? ? ? ? 插入的數(shù)據(jù)如果存在外鍵約束 或者 ?這個(gè)主鍵是自動(dòng)生成的,你不知道這個(gè)字段值拆讯,而你又想得到這個(gè)值并把它插到另一個(gè)表里脂男,先要在提供約束的表里插入數(shù)據(jù),然后獲取主鍵值种呐,再將輸入連帶主鍵值插入到被約束的表
注:最好一個(gè)表不要有外鍵約束宰翅,而用邏輯判斷實(shí)現(xiàn)外鍵約束功能,因?yàn)橥怄I約束影響性能
ps.getGeneratedKeys()獲取插入語(yǔ)句插入完成后自動(dòng)生成的主鍵值
sql轉(zhuǎn)義
1.時(shí)間日期函數(shù)爽室,java里的Date和sql的Date表現(xiàn)形式不同堕油,如果你想要把java里的Date存到數(shù)據(jù)庫(kù)里
使用new java.sql.Date(new java.util.Date().getTime());進(jìn)行日期的轉(zhuǎn)義
所以在鏈接數(shù)據(jù)庫(kù)的時(shí)候建議導(dǎo)入sql包下的Date
如何解決,鍵盤輸入日期對(duì)象存到數(shù)據(jù)庫(kù)里呢肮之?
(1)鍵盤輸入字符串轉(zhuǎn)換為util包下的Date掉缺,使用了格式化類
SimpleDateFormat sdf=new SimpleDateFormat("yyyy,MM,dd");表示字符串是必須按照這個(gè)形式輸入的
java.util.Date date= sdf.parse(s); //將字符串s轉(zhuǎn)換為了util下的Date對(duì)象
(2)然后將util包下的Date轉(zhuǎn)換為sql包下的Date然后導(dǎo)入到數(shù)據(jù)庫(kù)
new Date(date.getTime());
2.數(shù)據(jù)庫(kù)中某個(gè)基本類型的字段值為null,當(dāng)把這個(gè)值傳給類時(shí)戈擒,類中的基本類型不可以為null眶明,就會(huì)報(bào)錯(cuò),所以數(shù)據(jù)庫(kù)和java相連的時(shí)候筐高,把基本類型設(shè)置為其包裝類類型(少用搜囱,因?yàn)閿?shù)據(jù)庫(kù)內(nèi)的null我們想讓它表示的是0)或者是用ifnull(可能為null的字段值,0)函數(shù)將null字段值替換為0
連接池(c3p0和dbcp) 速度塊
DateSource數(shù)據(jù)源用于替代DriverManger獲取數(shù)據(jù)庫(kù)連接柑土,主要用于連接池的使用(效率高)
? ? ? ?如果一個(gè)項(xiàng)目中需要多個(gè)連接蜀肘,如果一直獲取連接,斷開(kāi)連接稽屏,這樣比較浪費(fèi)資源扮宠,如果創(chuàng)建一個(gè)池,用池來(lái)管理Connection狐榔,這樣就可以重復(fù)使用Connection坛增。有了池我們就不用自己來(lái)創(chuàng)建Connection,而是通過(guò)池來(lái)獲取Connection對(duì)象薄腻。當(dāng)使用完Connection后收捣,調(diào)用Connection的close()方法也不會(huì)真的關(guān)閉Connection,而是把Connection“歸還”給池庵楷。池就可以再利用這個(gè)Connection對(duì)象了罢艾。這里我們常用的連接池有兩種楣颠,分別是:DBCP連接池和C3P0連接池,下邊是對(duì)兩種連接池的具體使用和比較咐蚯。
dbcp連接池(spring開(kāi)發(fā)組推薦使用dbcp)
引入包
commons-dbcpxxx.jar
commons-poolxxx.jar
? ? ? ? 在使用寫連接池工具類的時(shí)候童漩,可以通過(guò)配置文件來(lái)連接數(shù)據(jù)庫(kù),配置文件中記錄了連接數(shù)據(jù)庫(kù)的驅(qū)動(dòng)仓蛆、URL睁冬、用戶名和密碼等信息挎春,但要注意這里的文件后綴為:“.properties”看疙。把通過(guò)配置文件連接數(shù)據(jù)庫(kù)的部分寫在靜態(tài)代碼塊中,隨著類的加載只加載一次直奋。除了連接數(shù)據(jù)庫(kù)能庆,還要提供一個(gè)獲得數(shù)據(jù)源的方法和一個(gè)獲取連接的方法,寫成工具類和直接使用的代碼幾乎相同脚线,只是增強(qiáng)了代碼的復(fù)用性搁胆。
c3p0連接池(hibernate開(kāi)發(fā)組推薦使用c3p0;)穩(wěn)定
c3p0-0.9.1-pre5.jar如果這個(gè)包是9.2以后的還要另外導(dǎo)入mchange-commons-java
事務(wù):
銀行轉(zhuǎn)賬問(wèn)題,A邮绿,B各有兩千塊錢渠旁,
A要轉(zhuǎn)給B500,這是先從A的賬戶扣除500船逮,在給B賬戶增加500顾腊,如果在扣除A 500之后和增加B 500之前出現(xiàn)了問(wèn)題,那么就會(huì)產(chǎn)生A扣錢了挖胃,但是B卻沒(méi)增加錢的問(wèn)題杂靶,這時(shí)候就需要一個(gè)東西,讓扣錢和增錢只有都完成了這個(gè)操作才會(huì)被執(zhí)行
事實(shí)上mysql里每一條sql語(yǔ)句都是在運(yùn)行后被提交執(zhí)行的(每執(zhí)行一條sql都是執(zhí)行一條事務(wù))酱鸭,運(yùn)行不會(huì)改變數(shù)據(jù)庫(kù)只會(huì)顯示結(jié)果吗垮,而提交才改變了數(shù)據(jù)庫(kù),而mysql是自動(dòng)執(zhí)行提交的
try{
con.setAutoCommit(fasle); ?//設(shè)置mysql事務(wù)不是直接提交的
....
....
con.commit(); ? //多條sql語(yǔ)句都執(zhí)行完后一起提交凹髓,數(shù)據(jù)庫(kù)表才會(huì)正式更改
con.setAutoCommit(true) ?//將自動(dòng)提交改回
}catch(){
con.rollback(); ? ?//一旦其中出現(xiàn)錯(cuò)誤就會(huì)回滾烁登,都不提交,返回沒(méi)執(zhí)行sql語(yǔ)句執(zhí)行狀態(tài)
con.setAutoCommit(true) ?//將自動(dòng)提交改回
}
事務(wù)的特性
事務(wù)有以下四個(gè)標(biāo)準(zhǔn)屬性的縮寫ACID蔚舀,通常被稱為:
原子性: 確保工作單元內(nèi)的所有操作都成功完成防泵,否則事務(wù)將被中止在故障點(diǎn),和以前的操作將回滾到以前的狀態(tài)蝗敢。
一致性: 確保數(shù)據(jù)庫(kù)正確地改變狀態(tài)后捷泞,成功提交的事務(wù)。
隔離性: 使事務(wù)操作彼此獨(dú)立的和透明的寿谴。
持久性: 確保提交的事務(wù)的結(jié)果或效果的系統(tǒng)出現(xiàn)故障的情況下仍然存在锁右。
事務(wù)的隔離級(jí)別
并發(fā)讀問(wèn)題:
1.臟讀:一個(gè)事務(wù)讀到另一個(gè)事務(wù)未提交數(shù)據(jù),就是臟讀。(未提交不是實(shí)際更改咏瑟,后期可能回滾拂到,所以讀的數(shù)據(jù)可能是假的)
比如:
2.不可重復(fù)讀:一條事務(wù)讀取某數(shù)據(jù)兩次,第二次讀取后才提交码泞,兩次中間有一條事務(wù)對(duì)數(shù)據(jù)進(jìn)行更新兄旬,導(dǎo)致兩次讀的數(shù)據(jù)不一樣。(一條事務(wù)在未提交前對(duì)某數(shù)據(jù)的查詢結(jié)果應(yīng)該是一致的)重點(diǎn)為:字段值的改變
比如:
3.幻讀:對(duì)同一張表的兩次查詢不一致余寥,因?yàn)榱硪皇聞?wù)插入了一條數(shù)據(jù)(一條事務(wù)在未提交前對(duì)同一張表的查詢結(jié)果應(yīng)該是一致的)
重點(diǎn)為:數(shù)據(jù)數(shù)量的改變
比如:
不可重復(fù)讀和幻讀有什么區(qū)別呢领铐?
不可重復(fù)讀是讀到了另一個(gè)事務(wù)的更新(重點(diǎn)是數(shù)據(jù)內(nèi)容的修改)
幻讀是讀到了表的插入(重點(diǎn)是表中增加或刪除了一條數(shù)據(jù))
四大隔離級(jí)別:
四個(gè)等級(jí)的隔離級(jí)別,在除了隔離級(jí)別不同什么都相同的情況下處理結(jié)果是不同的宋舷,因?yàn)樗姆N隔離級(jí)別對(duì)并發(fā)數(shù)據(jù)的處理能力是不同的绪撵。
1.SERIALIZABLE(串行化)
因?yàn)槭谴谢圆粫?huì)出現(xiàn)任何問(wèn)題祝蝠,效率最差
2.REPEATABLE READ(可重復(fù)讀)MySql默認(rèn)
防止臟讀和不可重復(fù)讀音诈,不能防止幻讀
3.READ COMMITTED(讀已提交數(shù)據(jù))
防止臟讀
4.READ UNCOMMITTED(讀未提交數(shù)據(jù))
可能出現(xiàn)任何問(wèn)題,效率最好
JDBC設(shè)置隔離級(jí)別:
依舊通過(guò)Connection對(duì)象绎狭,使用方法setTransactionisolation[ int level]
參數(shù)可選為
事務(wù)與批處理結(jié)合應(yīng)用可以實(shí)現(xiàn)细溅,多條語(yǔ)句更新的時(shí)候,如果一條語(yǔ)句出現(xiàn)了問(wèn)題儡嘶,都不會(huì)進(jìn)行插入了