JDBC深刻java
1 課程介紹
1.預編譯語句對象mysql
2.SQL注入問題面試
3.用戶登陸sql
4.事務數據庫
5.鏈接池tomcat
6.得到主鍵問題安全
2 預編譯語句對象服務器
i. 加載驅動併發
ii. 得到鏈接ide
iii. 準備sql語句以及Statement對象
iv. 執行sql語句
v. 釋放資源
i. 拼接sql語句容易出錯,麻煩;
ii. 存在sql注入問題;
1. 使用Statement模擬sql注入問題
2. 使用PreparedStatement解決sql注入問題
3. 爲何PreparedStatement就解決了sql注入問題呢?
使用PrepareStatement 它的語句至關是一個模板,這個模板的基本結構咱們是不可以進行更改的(不能拚接字符串),因而,它很好的解決了sql注入的問題
PreparedStatement:Statement的子接口,表示預編譯SQL語句對象.
什麼是預編譯SQL語句?
預編譯語句PreparedStatement 是java.sql中的一個接口,它是Statement的子接口。
經過Statement對象執行SQL語句時,須要將SQL語句發送給數據庫,由數據庫首先進行編譯後再執行。
預編譯語句和Statement不一樣,在建立PreparedStatement 對象時就指定了SQL語句,該語句當即發送給數據庫進行編譯。
當該編譯語句被執行時,數據庫直接運行編譯後的SQL語句,而不須要像其餘SQL語句那樣首先將其編譯,再執行。
Api示例:
使用PreparedStatement的操做流程-完成update方法
i. 庫連錯了,表指定錯了,列名指定錯了,sql語句拼錯了;
ii. 上面的sql語句中的 ? 表明就是一個任意類型的值,不需加 ' '
iii. PreparedStatement中是一個模板,而不是拼接字符串
iv. 調用executeUpdate方法的時候不須要傳入sql參數了,模板中已經有了;
v. set方法指定的列編號是從1開始
i. 代碼可讀性/維護性更高.
ii. PreparedStatement的執行性能更高.
iii. 安全性更高,防止SQL注入問題.
第一:
數據庫在執行sql語句的時候若是使用PreparedStatement語句會有一點優點:
由於數據庫會preparedStatement 語句進行預編譯,預編譯的sql能夠複用
下次執行相同的sql語句時,數據庫端不會再進行預編譯了,而直接用數據庫的緩衝區,
提升數據訪問的效率(儘可能採用使用?號的方式傳遞參數).
第二:
極大地提升了安全性,防止sql注入。
第三:
增長代碼的可讀性 和 可維護性
最後:在JDBC應用中,若是是稍有水平開發者,你就應該始終以PreparedStatement代替Statement.也就是說,
在任什麼時候候都不要使用Statement
3 用戶登陸功能有以下幾種登陸方式:
--------------------------------------------------------------------------------------------
1. 第一種
i. 在外面的DAO層應該有這樣的一條sql:
select * from student where username=? and password=?
ii. 劣勢 : 不可以獨立判斷出是用戶名仍是密碼錯誤
iii. 優點 : 簡單
安全性更高,思考爲何?
2. 第二種
i. 在外面的DAO層應該有這樣的一條sql:
select * from student where username=?
ii. 如何判斷重複的?
1. 先使用戶名從數據庫中得到數據
a) 若是沒有返回東西,提示用戶名錯誤
b) 若是獲得結果了,從結果中得到密碼和前臺傳入的密碼進行比較
c) 若是相等 登錄成功
iii. 劣勢 : 麻煩一點
iv. 優點 : 能夠單獨判斷是用戶名仍是密碼錯誤
用戶體驗更好,安全性沒那麼好,由於直接告訴了用戶是 用戶名錯誤 仍是密碼錯誤
4 事務
1. 需求:王麻子準備把1000元轉給女友小花
2. 數據準備-帳戶表account(id,name,balance)
3. 步驟
1) 查詢 王麻子 的帳戶餘額是否大於等於1000塊錢.
SELECT * FROM account WHERE name = '王麻子' AND balance >= 1000;
① 餘額小於1000: 系統提示:親,您的餘額不足,把錢賺夠了再轉吧!!!
② 餘額大於等於1000: 繼續後面的操做
2) 從 王麻子 的帳戶上減小1000塊錢.
UPDATE account SET balance = balance - 1000 WHERE name = '王麻子';
3) 在 小花 的帳戶上增長1000塊錢.
UPDATE account SET balance = balance + 1000 WHERE name = '小花';
4) 那麼問題來了:若在第二步和第三步操做之間,忽然停電!!!--->程序中斷(使用異常模擬.)
5) 模擬代碼:
把多個操做單元當作是一個總體,所有的操做單元成功都才視爲成功(commit),如有一個操做單元失敗則,視爲整個過程失敗(rollback)。
爲確保數據庫中數據的一致性,數據的操縱應當是離散的成組的邏輯單元:當每一個邏輯操做單元所有完成時,數據的一致性能夠保持,而當這個單元中的一部分操做失敗,整個事務應所有視爲錯誤,全部從起始點之後的操做應所有回退到開始狀態。
1. 原子性(Atomicity)
指整個事務是不能夠分割的工做單元。只有事務中全部的操做執行成功,纔算整個事務成功,
事務中任何一個SQL語句執行失敗,那麼已經執行成功的SQL語句也必須撤銷,數據庫狀態應該回到執行事務前的狀態。
2. 一致性(Consistency)
指數據庫事務不能破壞關係數據的完整性以及業務邏輯上的一致性。
例如對於銀行轉帳事務,無論事務成功仍是失敗,應該保證事務結束後兩個轉帳帳戶的存款總額是與轉帳前一致的。
3. 隔離性(Isolation):
指的是在併發環境中,當不一樣的事務同時操縱相同的數據時,每一個事務都有各自的完整數據空間。
即一個事務內部的操做及使用的數據對併發的其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。
4. 持久性(Durability)
指的是隻要事務成功結束,它對數據庫所作的更新就必須永久保存下來。
即便發生系統崩潰,從新啓動數據庫系統後,數據庫還能恢復到事務成功結束時的狀態。
try{
//開啓事務,設置爲的手動提交
操做1
操做2
操做3
......
//提交事務:
}catch(Exception e){
//回滾事務
}
1. 在JDBC中事務是默認自動提交的.
在執行DML語句的時候就已經提交事務了.
2. 事務只對DML語句有效,對於DQL(查詢)沒效果,查詢不會涉及到修改數據.
可是,之後在Spring設置的時候,每每把查詢也放到事務中.
注意:之後作開發,方式代碼沒有報錯,可是數據插入/修改/刪除不成功,首先判斷事務是否提交.
3. 回滾事務以後再釋放資源,釋放鎖機制(InnoDB:行鎖).
4. 在MySQL中,MyISAM不支持外鍵,不支持事務,InnoDB都支持.
在開發中,外鍵能夠不要,可是事務必須得要.
在Spring中有專門的事務管理器(TransactionManager):
5 得到主鍵問題(瞭解)新增一個產品 和 產品庫存
1. 從前臺網頁中錄入數據的時候
a) 錄入產品
b) 錄入庫存
2. 後臺新增數據到表 product 和 product_stock
a) 先新增一條產品數據,數據庫生成一個主鍵id
b) 再新增庫存記錄(product_stock引用了product表的外鍵)
須要得到新增的product的id,應該怎麼弄?
Statement中有方法:
int executeUpdate(String sql)
返回的是影響的行數
int executeUpdate(String sql, int autoGeneratedKeys)
返回仍是影響的行數
參數 : autoGeneratedKeys,表示能夠設置是否能夠去得到自動生成的主鍵
值: Statement.RETURN_GENERATED_KEYS
設置能夠獲取以後,如何獲取?
ResultSet getGeneratedKeys() 獲取因爲執行此 Statement 對象而建立的全部自動生成的鍵
注意:
分析:
1) 表中明明是有id列
2) 之前使用executeQuery查詢的時候,返回的ResultSet中就是數據庫表中的一條一條的數據,就是經過上面的方式來得到
3) getGeneratedKeys() 調用完畢以後返回的對象中只有鍵
4) 怎麼拿?調用ResultSet中經過索引來得到列數據的方法
Hibernate中能夠自動返回,(底層已經封裝OK)
6 鏈接池鏈接池:就是用來裝鏈接對象connection的容器
鏈接池的技術比較多,java僅僅是提供了DataSource的接口(就是鏈接池),不提供任何實現.由各大服務器廠商來提供DataSource實現(Tomcat,WebLogic).
示例:
假設一個場景,火車站排隊買票:
無論有沒有人,都須要留一個兩個窗口,--有個初始窗口
1. 初始容量(5)
火車站剛開始須要有幾個窗口
2. 若是人變多的時候,窗口不夠怎麼辦---須要增長窗口,可是這個窗口不是隨便增長
當人數比較多的時候,添加窗口;
最大數量(10)
3. 若是人變少的時候,只有幾個窗口 -- 這時候須要最小窗口
最小數量(2)
4. 若是有人佔着窗口不放,怎麼辦?
一我的若是佔着位置又不買票,好比佔了3分鐘,強制驅離不要佔用窗口
最大鏈接時間(3)
5. 票已經賣完的時候,還有人等待怎麼辦?
若是等待過長,斷開鏈接,請求超時;
請求超時
這就是鏈接池的理解;
普通的JDBC數據庫鏈接使用 DriverManager 來獲取,每次向數據庫創建鏈接的時候都要將 Connection 加載到內存中,再驗證用戶名和密碼(得花費0.05s~1s的時間)。
須要數據庫鏈接的時候,就向數據庫要求一個,執行完成後再斷開鏈接。這樣的方式將會消耗大量的資源和時間。
數據庫的鏈接資源並無獲得很好的重複利用.若同時有幾百人甚至幾千人在線,頻繁的進行數據庫鏈接操做將佔用不少的系統資源,嚴重的甚至會形成服務器的崩潰。
對於每一次數據庫鏈接,使用完後都得斷開。不然,若是程序出現異常而未能關閉,將會致使數據庫系統中的內存泄漏,最終將致使重啓數據庫。
這種開發不能控制被建立的鏈接對象數,系統資源會被毫無顧及的分配出去,如鏈接過多,也可能致使內存泄漏,服務器崩潰.
使用鏈接池和不使用鏈接池在代碼上的區別:
獲取鏈接:
使用鏈接池以前:
使用DriverManager來獲取Connection對象.
Connection conn = DriverManager.getConnection(url,username,password);
使用鏈接池以後:
直接找鏈接池(DataSource對象),取出Connection便可.
如何建立DataSource對象,在建立DataSource的時候,就會設置鏈接數據庫的url,user,password.
Connection conn = DataSource對象.getConnection();
接下來的代碼和之前相同.
釋放鏈接:
代碼:connection對象.close();
使用鏈接池以前:
直接和數據庫服務器創建鏈接關係,而斷開也是和數據庫服務器斷開鏈接.
使用鏈接池以後:
直接和鏈接池創建鏈接關係,而斷開也是把Connection對象還給鏈接池,供其餘客戶使用.
沒有真正的和數據庫斷開. 如此一來,一個Connection對象就獲得了充分的利用!!!
DBCP 鏈接池
C3P0 鏈接池
Druid 鏈接池 : 如今用的最多,是阿里巴巴開源的鏈接池。
排第一 Druid
排第二 tomcat 裏面有個鏈接池
準備: 拷貝jar: commons-dbcp-1.3.jar commons-pool-1.5.6.jar .
查閱文檔:commons-dbcp-1.3-src\doc\BasicDataSourceExample.java --------------------------------------------------------------------------- 解決DBCP的硬編碼問題: 應該把鏈接信息放到配置文件中去: 配置文件的名詞能夠任意. --------------------------------------------------------------------------- dbcp.properties #鏈接字符串 url=jdbc:mysql://localhost:3306/jdbcdemo #用戶名 username=root #密碼 password=admin #驅動的類路徑 driverClassName=com.mysql.jdbc.Driver #鏈接池啓動時的初始值 initialSize=1 #鏈接池的最大值 maxActive=50 #鏈接池的最大空閒數 maxIdle=20 --------------------------------------------------------------------------- 如何把dbcp.properties中的配置信息,設置到程序中去: |
直接讀取資源文件的方式:
注意:在db.properties裏的屬性名稱必須和set的屬性名字的對應上 |