一、JDBC簡介
1、JDBC是SUN公司爲了簡化操作數據推出一套規範。數據庫廠商的驅動就是對JDBC的實現。
2、Java Data Base Connectivity(java數據庫連接),它主要由接口組成。
java.sql.* javax.sql.* JDK中數據庫的驅動jar包
二、JDBC的編碼步驟
前提:拷貝數據庫的驅動到構建路徑中(classpath)
1、註冊驅動
2、獲取與數據庫的鏈接
3、創建代表SQL語句的對象
4、執行SQL語句
5、如果是查詢語句,需要遍歷結果集
6、釋放佔用的資源
三、JDBC中常用的接口詳解
1、DriverManager作用
⑴註冊驅動:
方式一(不建議使用):DriverManager.registerDriver(new com.mysql.jdbc.Driver());
原因:①依賴具體驅動。②導致驅動註冊2遍,從源碼裏看(如下圖)。
方式二(建議):Class.forName("com.mysql.jdbc.Driver");
只依賴字符串,可以實現運行時依賴,不用編碼依賴(如下圖)。
⑵獲取與數據庫的鏈接
url:SUN和數據庫廠商間的協議。
MySql:jdbc:mysql://localhost:3306/test
Oracle: jdbc:oracle:thin:@localhost:1521/test
public static Connection getConnection(String url,String user,String password) throws SQLException
public static Connection getConnection(String url,Properties info) throws SQLException
public static Connection getConnection(String url) throws SQLException
2、Connection
所有的數據庫操作都是基於鏈接之上的。
Statement createStatement():創建向數據庫發送sql的statement對象。即Statement stmt = conn.createStatement();//創建向數據庫發送sql的statement對象
preparedStatement(sql): 創建向數據庫發送預編譯sql的Prepare Statement對象。
preparedCall(sql): 創建執行存儲過程的Callable Statement對象
setAutoCommit(boolean autoCommit):設置事務是否自動提交
commit():在鏈接上提交事務
rollback():在此鏈接上回滾事務
3、Statement
作用:代表SQL語句對象。可以向數據庫發送任何的SQL語句
ResultSet executeQuery(String sql):執行sql查詢,sql一般都是查詢語句,返回ResultSet
int executeUpdate(String sql):sql一般是DML(insert update delete)語句。返回int值,操作幾條記錄。
boolean execute(String sql):sql可以是任意的語句。返回值不是代表成功與否。如果是查詢語句,就有結果集,返回true。沒有返回結果集的,返回false。
addBatch(String sql):把多條sql語句放到一個批處理中。
executeBatch():向數據庫發送一批sql語句執行。
4、ResultSet
作用:封裝了查詢的結果集。封裝執行結果時,採用類似表格的方式。ResultSet對象維護了一個指向數據行的遊標,默認指向表格第一行數據內容的前面。
方法:X getX(String columnName或 int columnIndex) 其中X爲String,int,long等類型
補充,遊標的操作:
boolean next():遊標下移。返回值是有無記錄
boolean previous():遊標上移。
boolean absolute(int count):定位到指定的行。第一行是1。
void beforeFirst():移動遊標到第一行的前面。
void afterLast():移動遊標到最後一行的後面。
四、釋放佔用的資源
一定要釋放資源,所以寫到finally裏。
五、SQL注入
SQL注入攻擊指的是黑客通過構建特殊的命令作爲參數傳入Web程序,而這些命令大都是SQL語法裏的一些基本組合。當條件爲' or 1=1 and id ='可以當條件查詢。
如:select * from user where name='' or 'a'='a';
select * from user where name='' or 1=1
等價於 select * from user
SQL注入分爲平臺注入和代碼層注入。前者是由不安全的數據庫配置或數據庫平臺的漏洞導致;後者主要是由於程序員對輸入未進行細緻的過濾,從而執行了非法執行。
產生原因:
①不當的類型處理
②不安全的數據庫配置
③不合理的查詢集處理
④不當的錯誤處理
⑤轉義字符處理不合適
⑥多個提交處理不當
SQL注入的預防:
①用戶的表單輸入域:防止一些特殊字符。
②對密碼加密:MD5加密
③PreparedStatement預編譯(得到該對象時,就必須給他SQL語句)
支持參數佔位符: ? 一個問號代表着一個參數。
注:能用PreparedStatement就不要用Statement。
六、如何調用存儲過程
Mysql:
inputParam爲輸入參數,inOutParam既可以作爲輸出參數,又可以作爲輸入參數。
Oracle:
七、事務
1、事務指邏輯上的一組操作,組成這組操作的各個單元,要麼全部成功,要麼全部不成功。
MySQL:每一條語句都屬於獨立事務,默認自動管理的。
2、相關操作內容:
開啓事務:start transaction; //日後的語句都會處於同一個事務之中。
提交事務:commit;
回滾事務:rollback;
3、事務的特性(數據安全):
原子性:處於事務中的多條語句是不可分割的。(當做一個整體對待,操作事務要麼都發生,要麼都不發生)
一致性:事務必須使數據庫從一個一致性狀態變換到另外一個一致性狀態。比如:轉賬,轉賬前A+B=2000,轉賬後A+B=2000
隔離性:多線程併發。一個事務不能被其他線程中的事務所打擾。
持久性:事務一旦提交,永久保存起來。(保存到硬盤,而不是內存)
4、事務的隔離級別:屬於事務的。都已開啓了事務爲前提。
不考慮事務的隔離級別,會出現以下情況(是錯的)
- 髒讀:一個線程中的事務讀到了另外一個線程中未提交的數據。
- 不可重複讀:一個線程中的事務讀到了另外一個線程中已經提交的update的數據。
- 虛讀:一個線程中的事務讀到了另外一個線程中已經提交的insert的數據。
要想避免以上現象,通過更改事務的隔離級別來避免:()
- READ UNCOMMITTED “髒讀、不可重複讀、虛讀”都有可能發生。
- READ COMMITTED 避免髒讀的發生,“不可重複讀、虛讀”有可能發生。
- REPEATABLE READ 避免“髒讀、不可重複讀”的發生,虛讀有可能發生。
- SERIALIZABLE 避免“髒讀、不可重複讀、虛讀”的發生。
級別依次升高,效率依次降低,數據越安全。
MySQL:默認REPEATABLE READ
ORACLE:默認READ COMMITTED
⑴MySQL:
select @@tx_isolation;//查看當前會話的隔離級別
set transaction isolation level 級別;// 設置當前的事務隔離級別
⑵Oracle:
1.查看系統默認事務隔離級別,也是當前會話隔離級別
--首先創建一個事務
declare
trans_id Varchar2(100);
begin
trans_id := dbms_transaction.local_transaction_id( TRUE );
end;
--查看事務隔離級別
SELECT s.sid, s.serial#,
CASE BITAND(t.flag, POWER(2, 28))
WHEN 0 THEN 'READ COMMITTED'
ELSE 'SERIALIZABLE'
END AS isolation_level
FROM v$transaction t
JOIN v$session s ON t.addr = s.taddr AND s.sid = sys_context('USERENV', 'SID');
八、連接池
- 連接池:是創建和管理一個連接的緩衝池的技術。
- 編寫連接池:
用一個實現了javax.sql.DataSource類的實例時,用戶如果調用Connection.close()方法,會把鏈接關閉,失去了連接池的意義,而我們需要實現完實例後,放回連接池裏,方便下一次的使用,而不是立馬關閉。所以使用包裝設計模式,而包裝設計模式的核心是保持被包裝對象的原有信息,又可以對某個或某些方法進行改寫。
⑴流程如下,即用包裝(裝飾)設計模式:
①編寫一個類,實現與被包裝類(即數據庫驅動對Connection的實現)相同的接口。(使這個類和數據庫的驅動實現有着相同的行爲)
②定義一個變量,引用被包裝類的實例。
③定義構造方法,傳入被包裝類的實例。
④對於要改寫的方法,編寫自己的代碼即可。
⑤對於不需要改寫的方法,調用原有對象的對應方法。
⑵爲何不用繼承的方法來改寫close()方法:
①因爲數據庫的種類比較多,要寫繼承的話會寫死,因爲他是繼承相對應數據庫的架包裏的方法。
②數據庫驅動對Connection接口的實現類,不允許被繼承
③假設能繼承成功了,丟失了原有對象的信息。
3.連接池的動態代理(Aspect-Oriented Programming,簡稱AOP,即默認適配器設計模式)
3.1動態代理技術介紹
3.1.1代理模式:
就是爲其他對象提供一種代理以控制對這個對象的訪問。即明星與經紀人關係,經紀人即代理,聯繫代理,由代理轉交給明星。
3.1.2基於接口的動態代理:Proxy;如果一個類沒實現任何的接口,此種代理就不能使用了。
說明:Proxy創建動態代理類的實例提供了靜態方法,也是所有動態代理類的父類的方法創建。是JDK自帶的。
InvocationHandler handler = new MyInvocationHandler(...);
Class proxyClass = Proxy.getProxyClass(Foo.class.getClassLoader(), new Class[] { Foo.class });
Foo f = (Foo) proxyClass.getConstructor(new Class[] { InvocationHandler.class }).
newInstance(new Object[] { handler });
或者更簡單的:
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class },handler);
3.2基於接口的連接池動態代理:Proxy
3.3基於子類的動態代理:CGLIB;子類可以擴展父類的功能
前提:被代理類的要求
a、不能是final的
b、必須是public的
比如普通的JavaBean就可能沒有實現任何的接口。
代理類是被代理類的子類。
4.開源第三方數據源使用(DBCP、C3P0和JDNI)
4.1 JDNI(Java Naming and DIrectory Interface)
⑴拷貝數據庫的驅動到Tomcat\lib目錄下
⑵在web應用的META-INF目錄下建立一個名稱爲context.xml的配置文件
⑶獲取JNDI容器中的資源
Lookup裏的地址後面需要跟第二步裏面起的一致。
補充:JDNI的包放在javax.naming.*裏,不要在main方法中獲取數據源,獲取不到。
4.2 DBCP(APache組織實現的。DBCP:DataBase Connection Pool)
需要架包:commons-dbcp-1.4.jar和commons-pool-1.5.6.jar
4.3 C3P0
需要架包:c3p0-0.9.1.2.jar