JDBC:
|-- 數(shù)據(jù)庫互聯(lián)。
|-- 由SUN公司所制定的用來訪問數(shù)據(jù)庫的規(guī)范。
|-- 數(shù)據(jù)庫數(shù)據(jù) <= 交互 => 應(yīng)用程序。
JDBC包含主要兩個部分:
|-- part 1.由SUN公司制定的規(guī)范接口
|-- java.sql --> 核心包
|-- javax.sql --> 擴(kuò)展包
|-- part 2.由數(shù)據(jù)庫廠商所提供的數(shù)據(jù)庫驅(qū)動程序,它們實(shí)現(xiàn)了part 1部分。
JDBC的驅(qū)動程序包含以下四種類型:
|-- 第一代驅(qū)動(JDBC - ODBC Bridge)
|-- 首先把JDBC的調(diào)用轉(zhuǎn)換成ODBC的調(diào)用,再用ODBC與具體的數(shù)據(jù)庫系統(tǒng)通信。
|-- 第二代驅(qū)動 (Native - API party - Java driver 本地API部分Java驅(qū)動)
|-- Conrerts JDBC calls into calls on the native client API of a specific RODMS.
|-- 缺點(diǎn):不同的數(shù)據(jù)庫需要安裝不同的客戶端(每臺電腦都要裝)。
|-- 第三代驅(qū)動 (JDBC - net pure Java driver 網(wǎng)絡(luò)純Java驅(qū)動)
|-- 提供網(wǎng)絡(luò)節(jié)點(diǎn),通過網(wǎng)絡(luò)節(jié)點(diǎn)訪問數(shù)據(jù)庫。
|-- Translates JDBC calls into a DBMS independent net protocol, which is then translated
to a DBMS protocol by a server.
|-- 第四代驅(qū)動 (Native protocol pure Java driver 本地協(xié)議純Java驅(qū)動)
|-- Conrerts JDBC calls directly into the network protocol used by DBMS.
JDBC核心API
java.sql
|-- Driver [Interface] 所有驅(qū)動必需實(shí)現(xiàn)的接口
|-- DriverManager [Class] 用來管理一組數(shù)據(jù)庫的驅(qū)動
|-- Connection [Interface] 與數(shù)據(jù)庫的一個連接對象
|-- Statement [Interface] 用于執(zhí)行一個靜態(tài)的SQL語句,并返回它所生成的結(jié)果對象
|-- PreparedStatement [Interface] 表示預(yù)編譯SQL語句對象(動態(tài))
|-- CallableStatement [interface] 表示用來調(diào)用存儲過程對象
|-- ResultSet [Interface] 存放查詢結(jié)果集的對象
|-- Type [Class]
|-- Blob [Interface] 二進(jìn)制大對象
|-- Clob [Interface] 字符大對象
|-- SQLException [Class] 數(shù)據(jù)庫最底層異常(已檢測異常)
|-- SQLWarning [Class] 警告
|-- DatabaseMetaData [Class] 基本數(shù)據(jù)的元數(shù)據(jù)
|-- ResultSetMetaDate [Class] 結(jié)果集的元數(shù)據(jù)
編寫JDBC程序步驟
step1:注冊驅(qū)動程序 [也就是把驅(qū)動加載到JVM中]
1.Class.forName("驅(qū)動類的全限定名");
如:Class.forName("oracle.jdbc.driver.OracleDriver");
Class.forName("com.mysql.jdbc.Driver");
2.使用new操作符來創(chuàng)建驅(qū)動類對象
如:Driver d = new oracle.jdbc.driver.OracleDriver();
//DriverManager.registerDriver(d);
3.使用系統(tǒng)屬性-Djava.drivers來指定驅(qū)動類
如:-Djava.drivers = com.mysql.jdbc.Driver;
step2:獲取數(shù)據(jù)庫連接 [Connection]
如:
String url = "主協(xié)議:此協(xié)議:thin:@ip地址:端口號:數(shù)據(jù)庫實(shí)例名";
String user = "XXX";
String pwd = "XXX";
Connection con = DriverManager.getConnection(url,user,pwd);
step3:創(chuàng)建執(zhí)行的SQL的環(huán)境 [也就是創(chuàng)建Statement]
如:Statement stm = con.createStatement();
step4:執(zhí)行SQL語句
如:String sql = "select ..........";
ResultSet rs = stm.executeQuary(sql);
step5:如果是select語句,則處理結(jié)果集 [ResultSet]
如:while(rs.next()){
rs.getXXX系列的方法();
}
step6:釋放資源 [close]
如:if(con != null) con.close();
if(stm != null) stm.close();
if(rs != null) rs.close();
下面就簡單的分析一下JDBC的這六個步驟:
1.獲取連接Connection對象分析
a.獲取數(shù)據(jù)庫的三大要素
|-- URL:不同的數(shù)據(jù)庫系統(tǒng)的URL不相同,但是至少有如下的內(nèi)容:
主協(xié)議:次協(xié)議:HOST(主機(jī)):PORT(端口):SID(庫名)
|-- USER:用戶名
|-- PASSWORD:密碼
b.DriverManager獲取連接的原理:
1
//驅(qū)動管理器類
2
class DriverManager
{
3
static Vector<Driver> drivers = new Vector<Driver>();
4
public static void registerDriver(Driver d)
{
5
driver.add(d);
6
}
7
/** *//**獲取連接的方法*/
8
public static Connection getConnection(String url,String user,String pwd) throws SQLException
{
9
Properties info = new Properties();
10
info.setProperty("user",user);
11
info.setProperty("password",pwd);
12
return getConnection(url,info);
13
}
14
public static Connection getConnection(String url,Properties info)throws SQLException
{
15
/**//*迭代多由的驅(qū)動,并且一次與給定的URL進(jìn)行匹配,如果成功則返回當(dāng)前驅(qū)動的Connection對象*/
16
Iterator<Driver> iter = drivers.iterator();
17
while(iter.hasNext())
{
18
Driver d = iter.next();
19
if(匹配(url,d))
{
20
return d.connect(url,info);
21
}else
{
22
continue;
23
}
24
}
25
/**//*到此都沒有返回,說明沒有匹配成功,則拋出SQLException異常*/
26
throw new SQLException("no suitable driver");
27
}
28
}
2. Statement 調(diào)用靜態(tài)SQL
PreparedStatement 來執(zhí)行動態(tài)SQL,可以為SQL動態(tài)綁定參數(shù)。
如:同構(gòu)SQL,SQL內(nèi)容一樣參數(shù)不同。
1
select id,first_name from s_emp
2
where dept_id = ? and name like ?;
3
4
insert into t_user values(?,?,?,?);
5
-- 上面的這些情況就可以用setXXX的方法(綁定參數(shù))來傳參數(shù)。
6
-- 例:
7
Connection con = ConnectionFactory.getConnection();
8
String sql = "XXXXXXXXXXXXXXXXXXXXXXXXXX";
9
PreparedStatement pstm = con.PreparedStatement(sql);
10
//綁定參數(shù)
11
pstm.setInt(1,XXX);
12
pstm.setString(2,XXX);
13
. . .
3.CallableStatement用來調(diào)用存儲過程[procedure]
1
String sql = "{call insert_user(?,?)}"
2
CallableStatement cstm = con.prepareCall(sql);
3
//綁定參數(shù)
4
cstm.setString(1,"user");
5
cstm.setString(2,"password");
6
//執(zhí)行過程,此返回值表示受影響的記錄條數(shù)
7
int res = cstm.executeUpdate();
8
4.執(zhí)行SQL
1
statement:
2

3
executeQuery(selectSQL); => ResultSet
4
executeUpdate(dmlSQL); => int
5
execute(arbiSQL); => boolean
6

7
if(stm.execute(sql))
{
8
//為真,表示執(zhí)行select語句
9
ResultSet rs = stm.getResultSet();
10
}else
{
11
//為假,表示執(zhí)行非select語句
12
int res = stm.getUpdateCount();
13
}
5.處理結(jié)果集
1
ResultSet rs:
2
rs.next(); => boolean用于判斷結(jié)果集中是否還有可讀取的元素。
3
rs.getXXX(); get系列方法,用于讀去結(jié)果集中的元素。
6.釋放資源
1
Connection.close();
2
Statement.close();
3
ResultSet.close();
[JDBC Advanced Topics]異常處理、元數(shù)據(jù)以及事務(wù)。
1、異常 Exception
java.sql.SQLException 對底層數(shù)據(jù)庫異常的封裝,它是一個已檢查異常。所以應(yīng)用程序必須對它做處理。
.getErrorCode(); 獲取特定于數(shù)據(jù)庫供應(yīng)商的錯誤代碼。
java.sql.SQLWaring extends SQLExecption 提供關(guān)于數(shù)據(jù)庫訪問警告信息的異常。
例:
1
try
{
2
//此處省略
3
} catch(SQLException e)
{
4
while(e != null)
{
5
e.printStackTrace();
6
e = e.getNextExecption();
7
}
8
}
2、元數(shù)據(jù):用來描述其他數(shù)據(jù)的數(shù)據(jù)
JDBC中的元數(shù)據(jù):
ResultSetMetaDate: 結(jié)果集元數(shù)據(jù),它用來描述結(jié)果集本身。
ResultSet.getMetaDate(); 獲取當(dāng)前結(jié)果集的元數(shù)據(jù)。
DatabaseMetaDate: 數(shù)據(jù)庫元數(shù)據(jù),用來描述數(shù)據(jù)庫本身。
Connection.getMetaDate(); 獲取當(dāng)前數(shù)據(jù)庫的元數(shù)據(jù)。
例:
//利用元數(shù)據(jù)打印出結(jié)果集

public void executeSQL(String sqlcommand)
{
Connection con = null;
Statement stm = null;
ResultSet rs = null;

try
{
//這里的ConnectionFactory是寫的一個連接工廠,這里就不做介紹了。
con = ConnectionFactory.getConnection();
stm = con.createStatement();
boolean flag = stm.execute(sqlcommand);

if(flag)
{
rs = stm.getResultSet();
ResultSetMetaDate rsmd = rs.getMetaDate();
int columns = rsmd.getColumnCount();

for(int i=1;i<columns;i++)
{
String columnName = rsmd.getColumnLabel(i);
System.out.print(columnName+"\t");
}
System.out.println();

while(rs.next())
{

for(int i=1;i<columns;i++)
{
Object content = rs.getObject(i);
System.out.print(content+"\t");
}
System.out.print();

}else
{
int results = stm.getUpdateCount();
System.out.println(results+" resultes was update
");
}
}

}catch(SQLExecption e)
{
//此處省略若干
}
}
3、DML的操作 [JDBC的事務(wù)] 以事務(wù)為單位
JDBC事務(wù):
定義:一組相關(guān)的操作,不可分割,一起成功,一起失敗。
相關(guān)操作:
Connection:
commit(); JDBC事務(wù)提交
rollback(); JDBC事務(wù)回滾
JDBC的事務(wù)默認(rèn)情況下是自動提交的,每執(zhí)行一次SQL命令,就提交commit一次。[AutoCommit]
如:
轉(zhuǎn)帳(){
...
update t_account1 set balance = balance-100;
update t_account2 set balance = balance+100;
...
}
如何讓以上兩句SQL語句,一起執(zhí)行,一起失敗。所以就需要把他們放在一個事務(wù)中。
如:
轉(zhuǎn)帳(){
...
con.setAutoCommit(false);//關(guān)閉自動事務(wù)提交
update t_account1 set balance = balance-100;
update t_account2 set balance = balance+100;
如果以上兩句都成功,則:
con.commit();//提交事務(wù)
如果以上任意一個失敗,則:
con.rollback();//事務(wù)回滾
}
獲取主鍵方式(主鍵值生成策略)
主鍵值不應(yīng)該由UI界面來收集,而應(yīng)該由應(yīng)用程序自動生成,常見的策略如下:
a. max(id)方法 select max(id) from table_name;
此方法當(dāng)有多線程并發(fā)時,就會產(chǎn)生問題。
b. sequence 序列
select sequence.nextval from dual;
此方法只適用支持序列的數(shù)據(jù)庫產(chǎn)品,所以不是很通用。
c. 維護(hù)單行單列得表
1
create table id_table(next_id number not null);
2
insert into id_table next_id values(1);
3
commit;
4
--使用以下SQL自動維護(hù)
5
select next_id from id_table for update;
6
update id_table set next_id = next_id+1;
7
commit;
并發(fā)控制:多個事務(wù)同時操作同一個對象
有三種并發(fā)情況,如下:
1、臟讀 Dirty reads 讀到其他事務(wù)沒有提交的數(shù)據(jù)。注:Oracle不支持
2、不可重復(fù)讀 Repeatable reads 同一個事務(wù)在兩個不同的時刻讀取不同結(jié)果
如:
1
T1:select score from t_score where id= 48;
2
T2:update t_score set score = score+10 where id = 48;
3
T1:select score from t_score where id= 48;
解決重復(fù)讀問題:
讓T1在查詢語句中加入for update子句,從而把選定的記錄加上排他鎖;那么T1事務(wù)完成之前,其他事務(wù)是不能來獲取鎖資源的。
3、幻影讀 Phanton reads 記錄數(shù)量變化,而不是行記錄的列值
1
T1:elcet count(*) from t_class where classname = '******';
2
T2:nsert into t_class values(
.);
3
commit();
4
T1:selcet count(*) from t_class where classname = '******';
5
解決幻影讀問題:
鎖表:lock table table_name in mode;
事務(wù)隔離級別:SUN在JDBC規(guī)范中制定了5個級別,分別用來滿足不同的并發(fā)性。
Connection:
0 .TRANSACTION_NONE 無
1 .TRANSACTION_READ_UNCOMMITED 可以臟讀
2 .TRANSACTION_READ_COMMITED 不可以臟讀
4 .TRANSACTION_REPEATABLE 不可以重復(fù)讀
8 .TRANSACTION_SERIALIZABLE 為每個事務(wù)都開辟一個空間
事務(wù)的隔離界別設(shè)置得越高,則并發(fā)性能越差
注:不同數(shù)據(jù)庫,對隔離級別的支持也是不一樣的。
如:oracle就不支持0和1兩種。
如何設(shè)置隔離級別?
如:con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITED);
這個也是默認(rèn)事務(wù)隔離級別。