JDBC深刻理解

 

JDBC深刻java

 

課程介紹

 1.預編譯語句對象mysql

 2.SQL注入問題面試

 3.用戶登陸sql

 4.事務數據庫

 5.鏈接池tomcat

 6.得到主鍵問題安全

預編譯語句對象服務器

2.1 回顧Statement執行SQL語句

2.1.1 操做流程

i. 加載驅動併發

ii. 得到鏈接ide

iii. 準備sql語句以及Statement對象

iv. 執行sql語句

v. 釋放資源

2.1.2 帶來的問題

i. 拼接sql語句容易出錯,麻煩;

ii. 存在sql注入問題;

2.1.3 sql注入問題

 

1. 使用Statement模擬sql注入問題

wKiom1ht_V3SMafYAABVQ07_YUg042.png-wh_50 

2. 使用PreparedStatement解決sql注入問題

3. 爲何PreparedStatement就解決了sql注入問題呢?

使用PrepareStatement 它的語句至關是一個模板,這個模板的基本結構咱們是不可以進行更改的(不能拚接字符串),因而,它很好的解決了sql注入的問題

 

 

 

2.2 使用PreparedStatement執行SQL語句

2.2.1 概念

PreparedStatement:Statement的子接口,表示預編譯SQL語句對象.

什麼是預編譯SQL語句?

 

預編譯語句PreparedStatement 是java.sql中的一個接口,它是Statement的子接口。

經過Statement對象執行SQL語句時,須要將SQL語句發送給數據庫,由數據庫首先進行編譯後再執行。

預編譯語句和Statement不一樣,在建立PreparedStatement 對象時就指定了SQL語句,該語句當即發送給數據庫進行編譯。

當該編譯語句被執行時,數據庫直接運行編譯後的SQL語句,而不須要像其餘SQL語句那樣首先將其編譯,再執行。

2.2.2 PreparedStatement使用

Api示例:

 

 wKiom1ht_TeS_OuHAAAITOP8uis631.png-wh_50

 

使用PreparedStatement的操做流程-完成update方法

 wKioL1ht_UnADWHTAABzPYsDH2A942.png-wh_50

2.2.3 注意事項

i. 庫連錯了,表指定錯了,列名指定錯了,sql語句拼錯了;

ii. 上面的sql語句中的 ? 表明就是一個任意類型的值,不需加 ' '

iii. PreparedStatement中是一個模板,而不是拼接字符串

iv. 調用executeUpdate方法的時候不須要傳入sql參數了,模板中已經有了;

v. set方法指定的列編號是從1開始

2.2.4 PreparedStatement特色

i. 代碼可讀性/維護性更高.

ii. PreparedStatement的執行性能更高.

iii. 安全性更高,防止SQL注入問題.

wKioL1ht_Ivj20JPAAAuSL-JUS4333.png-wh_50 

2.2.5 Statement和PreparedStatement區別(面試題)

第一           

數據庫在執行sql語句的時候若是使用PreparedStatement語句會有一點優點:

由於數據庫會preparedStatement   語句進行預編譯,預編譯的sql能夠複用

下次執行相同的sql語句時,數據庫端不會再進行預編譯了,而直接用數據庫的緩衝區,

提升數據訪問的效率(儘可能採用使用?號的方式傳遞參數).

第二  

極大地提升了安全性,防止sql注入。

第三

增長代碼的可讀性 可維護性

最後:在JDBC應用中,若是是稍有水平開發者,你就應該始終以PreparedStatement代替Statement.也就是說,

在任什麼時候候都不要使用Statement

用戶登陸功能

登陸界面wKioL1ht_AzCI131AAASnDqZAOc146.png-wh_50

有以下幾種登陸方式:

--------------------------------------------------------------------------------------------

1. 第一種

i. 在外面的DAO層應該有這樣的一條sql:

select * from student where username=? and password=?

wKiom1ht_EywXQOqAACJLj5kL6E512.png-wh_50 

ii. 劣勢 不可以獨立判斷出是用戶名仍是密碼錯誤

iii. 優點 簡單

安全性更高,思考爲何?

2. 第二種

i. 在外面的DAO層應該有這樣的一條sql:

select * from student where username=?

ii. 如何判斷重複的?

1. 先使用戶名從數據庫中得到數據

a) 若是沒有返回東西,提示用戶名錯誤

b) 若是獲得結果了,從結果中得到密碼和前臺傳入的密碼進行比較

c) 若是相等  登錄成功

d) 若是不相等,提示密碼錯誤wKioL1ht_F3CBK0YAAFI6rNlDDk858.png-wh_50

 

wKiom1ht_HTQC7p7AACLFdBlBZQ731.png-wh_50 

 

iii. 劣勢 麻煩一點

iv. 優點 能夠單獨判斷是用戶名仍是密碼錯誤

用戶體驗更好,安全性沒那麼好,由於直接告訴了用戶是 用戶名錯誤 仍是密碼錯誤

 

 

 

事務

4.1 銀行轉帳案例分析

1. 需求:王麻子準備把1000元轉給女友小花

2. 數據準備-帳戶表accountid,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) 模擬代碼:

wKiom1ht-_Sg8LJqAABb9ws8wOY690.png-wh_50 

4.2 事務概述和處理

4.2.1 什麼是事務

把多個操做單元當作是一個總體,所有的操做單元成功都才視爲成功(commit),如有一個操做單元失敗則,視爲整個過程失敗(rollback)

爲確保數據庫中數據的一致性,數據的操縱應當是離散的成組的邏輯單元:當每一個邏輯操做單元所有完成時,數據的一致性能夠保持,而當這個單元中的一部分操做失敗,整個事務應所有視爲錯誤,全部從起始點之後的操做應所有回退到開始狀態。

4.2.2 事務的ACID屬性(面試可能問到)

1. 原子性Atomicity

指整個事務是不能夠分割的工做單元。只有事務中全部的操做執行成功,纔算整個事務成功,

事務中任何一個SQL語句執行失敗,那麼已經執行成功的SQL語句也必須撤銷,數據庫狀態應該回到執行事務前的狀態。 

2. 一致性Consistency

指數據庫事務不能破壞關係數據的完整性以及業務邏輯上的一致性。

例如對於銀行轉帳事務,無論事務成功仍是失敗,應該保證事務結束後兩個轉帳帳戶的存款總額是與轉帳前一致的。

3. 隔離性Isolation:

指的是在併發環境中,當不一樣的事務同時操縱相同的數據時,每一個事務都有各自的完整數據空間。

即一個事務內部的操做及使用的數據對併發的其餘事務是隔離的,併發執行的各個事務之間不能互相干擾。

4. 持久性Durability

指的是隻要事務成功結束,它對數據庫所作的更新就必須永久保存下來。

即便發生系統崩潰,從新啓動數據庫系統後,數據庫還能恢復到事務成功結束時的狀態。

4.2.3 事務的處理機制

try{

   //開啓事務,設置爲的手動提交

   操做1

   操做2

   操做3

 

   ......

   //提交事務:

}catch(Exception e){

   //回滾事務

}

4.2.4 JDBC事務細節

1. JDBC中事務是默認自動提交的.

  在執行DML語句的時候就已經提交事務了.

2. 事務只對DML語句有效,對於DQL(查詢)沒效果,查詢不會涉及到修改數據.

  可是,之後在Spring設置的時候,每每把查詢也放到事務中.

  注意:之後作開發,方式代碼沒有報錯,可是數據插入/修改/刪除不成功,首先判斷事務是否提交.

3. 回滾事務以後再釋放資源,釋放鎖機制(InnoDB:行鎖).

4. MySQL,MyISAM不支持外鍵,不支持事務,InnoDB都支持.

  在開發中,外鍵能夠不要,可是事務必須得要.

Spring中有專門的事務管理器(TransactionManager):

得到主鍵問題(瞭解)

5.1 得到主鍵的場景分析

新增一個產品 產品庫存

1. 從前臺網頁中錄入數據的時候

a) 錄入產品

b) 錄入庫存

2. 後臺新增數據到表 product product_stock

a) 先新增一條產品數據,數據庫生成一個主鍵id

b) 再新增庫存記錄(product_stock引用了product表的外鍵)

須要得到新增的productid,應該怎麼弄?

5.2 Statement拿到主鍵的方式

Statement中有方法:

int executeUpdate(String sql)

返回的是影響的行數

int executeUpdate(String sql, int autoGeneratedKeys)  

返回仍是影響的行數

參數 autoGeneratedKeys,表示能夠設置是否能夠去得到自動生成的主鍵

值: Statement.RETURN_GENERATED_KEYS

 

設置能夠獲取以後,如何獲取?

ResultSet getGeneratedKeys() 獲取因爲執行此 Statement 對象而建立的全部自動生成的鍵

 

注意

wKioL1ht-87humQwAABxz_cYiLA811.png-wh_50 

分析:

1) 表中明明是有id

2) 之前使用executeQuery查詢的時候,返回的ResultSet中就是數據庫表中的一條一條的數據,就是經過上面的方式來得到

3) getGeneratedKeys() 調用完畢以後返回的對象中只有鍵

4) 怎麼拿?調用ResultSet中經過索引來得到列數據的方法

 

wKioL1ht-93D3GCYAAB5kOKBkhQ232.png-wh_50 

 

 

 

 

5.3 PreparedStatement 拿到主鍵的方式

wKiom1ht-7nSexWhAABXwWNswXw650.png-wh_50 

 

Hibernate中能夠自動返回,(底層已經封裝OK)

鏈接池

6.1 什麼是鏈接池

鏈接池:就是用來裝鏈接對象connection的容器

鏈接池的技術比較多,java僅僅是提供了DataSource的接口(就是鏈接池),不提供任何實現.由各大服務器廠商來提供DataSource實現(Tomcat,WebLogic).

 

示例

假設一個場景,火車站排隊買票:

  無論有沒有人,都須要留一個兩個窗口,--有個初始窗口

1. 初始容量(5

   火車站剛開始須要有幾個窗口

2. 若是人變多的時候,窗口不夠怎麼辦---須要增長窗口,可是這個窗口不是隨便增長

 當人數比較多的時候,添加窗口;

   最大數量(10)

 

3. 若是人變少的時候,只有幾個窗口 -- 這時候須要最小窗口

   最小數量(2)

 

4. 若是有人佔着窗口不放,怎麼辦?

一我的若是佔着位置又不買票,好比佔了3分鐘,強制驅離不要佔用窗口

  最大鏈接時間(3)

 

5. 票已經賣完的時候,還有人等待怎麼辦?

  若是等待過長,斷開鏈接,請求超時;

  請求超時

 

這就是鏈接池的理解;

 

6.2 爲何使用數據庫鏈接池

普通的JDBC數據庫鏈接使用 DriverManager 來獲取,每次向數據庫創建鏈接的時候都要將 Connection 加載到內存中,再驗證用戶名和密碼(得花費0.05s1s的時間)

須要數據庫鏈接的時候,就向數據庫要求一個,執行完成後再斷開鏈接。這樣的方式將會消耗大量的資源和時間。

數據庫的鏈接資源並無獲得很好的重複利用.若同時有幾百人甚至幾千人在線,頻繁的進行數據庫鏈接操做將佔用不少的系統資源,嚴重的甚至會形成服務器的崩潰。

對於每一次數據庫鏈接,使用完後都得斷開。不然,若是程序出現異常而未能關閉,將會致使數據庫系統中的內存泄漏,最終將致使重啓數據庫。

這種開發不能控制被建立的鏈接對象數,系統資源會被毫無顧及的分配出去,如鏈接過多,也可能致使內存泄漏,服務器崩潰.

 

使用鏈接池和不使用鏈接池在代碼上的區別:

 

獲取鏈接:

使用鏈接池以前:

使用DriverManager來獲取Connection對象.

Connection  conn = DriverManager.getConnection(url,username,password);

使用鏈接池以後:

直接找鏈接池(DataSource對象),取出Connection便可.

如何建立DataSource對象,在建立DataSource的時候,就會設置鏈接數據庫的url,user,password.

Connection conn = DataSource對象.getConnection();

接下來的代碼和之前相同.

 

釋放鏈接:

代碼:connection對象.close();

使用鏈接池以前:

直接和數據庫服務器創建鏈接關係,而斷開也是和數據庫服務器斷開鏈接.

使用鏈接池以後:

直接和鏈接池創建鏈接關係,而斷開也是把Connection對象還給鏈接池,供其餘客戶使用.

    沒有真正的和數據庫斷開. 如此一來,一個Connection對象就獲得了充分的利用!!!

 

6.3 經常使用鏈接池

DBCP 鏈接池

C3P0  鏈接池

 

Druid 鏈接池 : 如今用的最多,是阿里巴巴開源的鏈接池。

 

排第一 Druid 

排第二  tomcat 裏面有個鏈接池

6.4 DBCP鏈接池實現

準備:

  拷貝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中的配置信息,設置到程序中去:

wKiom1ht-0nB0scqAACb***cJTo576.png-wh_50wKioL1ht-1-QD3f9AABLxTTt-O0943.png-wh_50 

 

直接讀取資源文件的方式:

wKiom1ht-xmAQR66AABXydUx6jw824.png-wh_50 

注意:db.properties裏的屬性名稱必須和set的屬性名字的對應上

相關文章
相關標籤/搜索