JDBC原理

.JDBC原理概述java

 

1,JDBC是一套協議,是JAVA開發人員和數據庫廠商達成的協議,也就是由Sun定義一組接口,由數據庫廠商來實現,並規定了JAVA開發人員訪問數據庫所使用的方法的調用規範。mysql

 

2,JDBC的實現是由數據庫廠商提供,以驅動程序形式提供。算法

 

3,JDBC在使用前要先加載驅動。 sql

JDBC對於使用者要有一致性,對不一樣的數據庫其使用方法都是相同的。數據庫

 

驅動開發必需要實現Driver接口。 數組

數據庫驅動的實現方式 緩存

JDBC-ODBC橋接式 安全

JDBC網絡驅動,這種方式是經過中間服務器的協議轉換來實現的 服務器

JDBC+本地驅動,這種方式的安全性比較差。 網絡

JDBC驅動,由數據庫廠商實現。

 

.JDBCAPI

 

java.sql包和javax.sql包 

Driver接口(驅動),在加載某一 Driver 類時,它應該建立本身的實例並向 DriverManager 註冊該實例。這意味着用戶能夠經過調用如下程序加載和註冊一個驅動程序 

Class.forName("oracle.jdbc.driver.OracleDriver") 

DriverManager類(驅動管理器),它能夠建立鏈接,它自己就是一個建立Connection的工廠(Factory)。 

Connection接口,會根據不一樣的驅動產生不一樣的鏈接 

Statement接口,發送sql語句 

ResultSet接口(結果集),是用來接收select語句返回的查詢結果的。其實質相似於集合。

 

.JDBC應用步驟 

1,註冊加載一個driver驅動 

2,建立數據庫鏈接(Connection) 

3,建立一個Statement(發送sql) 

4,執行sql語句 

5,處理sql結果(select語句)

6,關閉Statement 

7,關閉鏈接Connection

 

注意:67兩個步驟是必需要作的,由於這些資源是不會自動釋放的,必需要本身關閉

 

訪問Oracle的數據庫的驅動名字叫ojdbc14.jar,要使用這個驅動程序,要先將他加到環境變量CLASSPATH中。

 

註冊加載驅動driver,也就是強制類加載 

Class.forName(Driver包名.Driver類名)

 

Driver d=new Driver類();//注意:這個方法不能用參數來構造 

DriverManager.registerDriver(d);

 

Oracle的Driver的全名oracle.jdbc.driver.OracleDriver 

mysql的Driver的全名com.mysql.jdbc.Driver 

SQLServer的Driver的全名com.microsoft.jdbc.sqlserver.SQLServerDriver

 

建立鏈接 

DriverManager.getConnection(String url,String username,String password); 

Connection鏈接是經過DriverManager的靜態方法getConnection(.....)來獲得的,這個方法的實質是把參數傳到實際的Driver中的connect()方法中來得到數據庫鏈接的。 

Oracle的URL值是由鏈接數據庫的協議和數據庫的IP地址及端口號還有要鏈接的數據庫的庫名(DatebaseName

 

Oracle URL的格式 

jdbc:oracle:thin:(協議)@XXX.XXX.X.XXX:XXXXIP地址及端口號):XXXXXXX(所使用的庫名) 

例:jdbc:oracle:thin:@192.168.0.20:1521:tarenadb

 

MySql URL的寫法 

例: jdbc:mysql://localhost:3306/tarena 

SQLServer URL的寫法 

例:jdbc:microsoft:sqlserver://localhost:1433/test

 

java -Djdbc.drivers=驅動的完整類名

 

使用虛擬機參數,加載驅動 -D表示爲虛擬機參數賦值 

java -Djdbc.drivers=oracle.jdbc.driver.OracleDriver:com.mysql.jdbc.Driver 

 

.JDBC基本方法 

DriverManager:若是有多個驅動可用的話,DriverManager會根據URL選擇其中一個可用的驅動

 

Driver:能夠選擇固定的驅動 

Driver driver = new oracle.jdbc.driver.OracleDriver(); 

String user = "sd0613"; 

String password = "sd0613"; 

Properties prop = new Properties(); 

prop.setProperty("user",user); 

prop.setProperty("password",password); 

driver.connect(url,properties); 

 

executeQuery(sqlString);//返回結果集 

executeUpdate(sqlString);//返回值爲該次操做影響的記錄條數,create table返回

execute(sqlString); 

//適用於不知道具體的操做是什麼,返回值是boolean類型的 

//若是返回值是true,表明執行查詢操做;不然表明執行更新操做

 

ResultSet 

next()方法

1.判斷是否存在下一條記錄 

2.將遊標移向下一條記錄 

getXXX(字段名或字段序號)//注意:字段序號從1開始 

 

關閉問題

使用Connection對象得到一個StatementStatement中的executeQuery(String sql) 方法可使用select語句查詢,而且返回一個結果集 ResultSet經過遍歷這個結果集,能夠得到select語句的查詢結果,ResultSetnext()方法會操做一個遊標從第一條記錄的前邊開 始讀取,直到最後一條記錄。executeUpdate(String sql) 方法用於執行DDLDML語句,能夠updatedelete操做。 

注意:要按先ResultSet結果集,後Statement,最後Connection的順序關閉資源,由於StatementResultSet是須要鏈接時纔可使用的,因此在使用結束以後有可能其餘的Statement還須要鏈接,因此不能先關閉

1、Statement 

execute(sql); 當不知道執行的SQL語句是什麼類型的時候執行 ,返回值是boolean 

executeQuery(sql); 執行查詢語句 

executeUpdate(sql); 執行更新語句

 

2、PreparedStatement 

可使用參數替代sql語句中的某些參數使用 "?"代替,他先將帶參數的sql語句發送到數據庫,進行編譯,而後PreparedStatement會將參數發送給數據庫。 

在使用PreparedStatement時,在設置相應參數時,要指明參數的位置和類型,以及給出參數值 

根據不一樣的參數類型使用不一樣的setXXX(參數的位置,參數值)來設置參數

 

例: 

public void insert(Student s){ 

Connection con=ConnectionFactory.getConnection();//創建鏈接 

String sql="insert into student(id,name) values(?,?)"; 

PreparedStatement ps=null; 

try { 

ps=con.prepareStatement(sql);//建立一個PreparedStatement 

int index=1; 

ps.setInt(index++,s.getStuId()); //爲參數賦值 

ps.setString(index++,s.getName()); 

ps.executeUpdate(); 

} catch (SQLException e) { 

e.printStackTrace(); 

}finally{ 

if(ps!=null) 

try { 

ps.close(); 

} catch (SQLException e) { 

e.printStackTrace(); 

if(con!=null) 

try { 

con.close(); 

} catch (SQLException e) { 

e.printStackTrace(); 

}

 

CallableStatement是能夠用非sql語句來訪問數據庫,他是經過調用存儲過程(PL/SQL)來訪問數據庫的。能夠直接使用鏈接來調用 prepareCall(...)方法,來執行這個存儲過程,"..."是存儲過程的名字。

 

對於系統時間要去數據庫時間 

TimeStamp 和 Date均可以保存時間 

TimeStamp能夠保存時、分、秒的數據,Date只保存日期年月的信息。

 

SQLException是檢查異常必須處理要麼throws ,要麼try{}catch(){} 

getErrorCode()能夠得到錯誤碼,能夠對錯誤進行查詢。

 

3、源數據 

JDBC中有兩種源數據,一種是數據庫元數據,另外一種是ResultSet元數據。

 

源數據就是描述存儲用戶數據的容器的數據結構。

 

ResultSet rs=ps.executeQuery(sql); 

ResultSetMetaData m=rs.getMetaData();

 

getColumnCount(),得到實際列數 

getColumnName(int colnum),得到指定列的列名 

getColumnType(int colnum),得到指定列的數據類型 

getColumnTypeName(int colnum),得到指定列的數據類型名

 

//打印結果集 

public static void printRS(ResultSet rs)throws SQLException{ 

ResultSetMetaData rsmd = rs.getMetaData(); 

while(rs.next()){ 

for(int i = 1 ; i < = rsmd.getColumnCount() ; i++){ 

String colName = rsmd.getColumnName(i); 

String colValue = rs.getString(i); 

if(i>1){ 

System.out.print(","); 

System.out.print(name+"="+value); 

System.out.println(); 

}

 

4、數據庫源數據

 

DatabaseMetaData 

getURL(),得到鏈接數據庫的URL 

getDatabaseProductName() 得到數據庫產品的名稱 

getDriverVersion() 得到JDBC驅動程序的String形式的版本號 

getTables()得到數據庫中該用戶的全部表 

getUserName() 得到數據庫用戶名。

 

5、異常的處理 

try{} 

catch(SQLException){} 

try{} 

catch(Exception){}

 

.事務(Transaction) 

原子操做:不可再分的操做,一個操做不能再分紅比它更細小的操做

事務是針對原子操做的,要求原子操做不可再分,而且必須同時成功同時失敗。 

事務就是把一些非原子操做,變成原子操做,由應用服務器來提出要求,由數據庫服務器來執行操做.

 

JDBC中默認是自動提交的,若是要想使用事務,須要按如下步驟執行

1.要調用con.setAutoCommite(false)方法,把自動提交(commit)置爲false。 

2.進行正常的數據庫操做 

3.若是操做成功了能夠選擇con.commit(),或者操做失敗時選擇con.roolback(); 

注意:打開事務就要關閉自動提交,當不須要再使用事務的時候調用setAutoCommite(true). 

 

.事務併發產生的問題 

三種併發產生的後果

1,髒讀:一個事務讀取到了另一個事務沒有提交的數據。 

2,重複讀:一個事務讀取到了另一個事務提交的數據。它是要保持在同一時間點上讀取到的數據相同,但願在一段時間內的數據是不變的。 

3,幻讀:一個事務讀取到了另一個事務提交的數據。用一樣的操做讀取兩次,獲得的記錄數不相同。

 

.事務隔離級別 

五種控制級別

TRANSACTION_NONE不使用事務。 

TRANSACTION_READ_UNCOMMITTED 容許髒讀。 

TRANSACTION_READ_COMMITTED防止髒讀,最經常使用的隔離級別,而且是大多數數據庫的默認隔離級別 

TRANSACTION_REPEATABLE_READ能夠防止髒讀和不可重複讀, 

TRANSACTION_SERIALIZABLE能夠防止髒讀,不可重複讀取和幻讀,(事務串行化)會下降數據庫的效率

 

以上的五個事務隔離級別都是在Connection類中定義的靜態常量,使用setTransactionIsolation(int level) 方法能夠設置事務隔離級別。 

:con.setTransactionIsolation(Connection.REPEATABLE_READ);

 

.JDBC2.0新特性 

1.可滾動特性和可更新特性 

JDBC1.0中是指遊標的移動的方向和方式是單向,單步(相對)移動,功能比較簡單

JDBC2.0中游標能夠雙向,相對或者絕對移動

可滾動結果集:這種結果集不但能夠雙向滾動,相對定位,絕對定位,而且還能夠修改數據信息。

 

1)滾動特性 

定位函數

boolean absolute(int row),定位到指定的記錄位置。定位成功返回true,不成功返回false。 

void afterLast() ,把遊標移動到最後一條記錄的後面(邏輯位置)。 

void beforeFirst() ,把遊標移動到第一條記錄的前面(邏輯位置)。 

//因爲第一條記錄的前面和最後一條記錄的後面這兩個位置確定存在,因此無需判斷是否存在,返回值設爲void. 

boolean first(),把遊標定位到第一條記錄。 

boolean last(),把遊標定位到最後一條記錄。 

//當結果集爲空的時候,這兩個方法會返回false. 

boolean next(),此方法是使遊標向下一條記錄移動。 

boolean previous() ,此方法可使遊標向上一條記錄移動,前提是前面還有記錄。 

boolean relative(int rows) ,相對定位方法,參數值可正可負,參數爲正,遊標從當前位置向後移動指定值條記錄,參數爲負,遊標從當前位置向前移動指定值條記錄。

 

判斷函數

ifBeforeFirst()判斷是否在在第一條記錄以前

ifAfterLast()判斷是否在在最後一條記錄以後

ifFirst()判斷是否爲第一條記錄

ifLast()判斷是否爲最後一條記錄.

 

要使用可滾動結果集時,須要一次設置更新特性與滾動特性,不能分開.

 

1.更新特性常量

CONCUR_READ_ONLY 只讀結果集(默認

CONCUR_UPDATABLE 可更新結果集

 

2.滾動特性常量

TYPE_FORWARD_ONLY ,該常量表示指針只能向前移動的 ResultSet 對象的類型。(默認

TYPE_SCROLL_INSENSITIVE ,該常量指示可滾動但一般不受其餘更改影響的 ResultSet 對象的類型。 

TYPE_SCROLL_SENSITIVE ,該常量指示可滾動而且一般受其餘更改影響的 ResultSet 對象的類型。 

//敏感:數據庫改變,結果集改變

語法

Statement st=null; 

st=con.createStatement(ReusltSet.TYPE_SCROLL_INSENSITIVE,ResuleSet.CONCUR_UPDATABLE) 

在建立Statement的時候就要指定這兩個參數,使用Statement,第一個參數表明滾動特性常量,第二個表明更新特性常量

 

2)可更新特性 

a.moveToInsertRow();記錄當前遊標位置,將遊標移到和結果集結構相似的緩衝區

b.使用updateXxx(int column,columnType value)方法來更新指定列數據

c.使用insertRow() 方法插入記錄

d.將遊標指回原位,moveToCurrentRow() 

 

可否使用JDBC2.0 ResultSet的新特性,要看使用的數據庫驅動是否支持

還有隻能用於單表且表中有主鍵字段(可能會是聯合主鍵),不可以有錶鏈接,會取 

可更新操做必須知足如下條件

a.查詢只能引用一張表

b.不能包含任何鏈接操做

c.必須把完整的主鍵查到結果集裏面

d.保證全部字段爲非空字段而且沒有默認值。

 

.數據庫元數據

DatabaseMetaData dbmd = con.getMetaData();//獲得數據庫元數據 

dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY, 

ResultSet.CONCUR_UPDATABLE);//判斷是否支持可更新操做

 

.批量更新 

優點

1.節省傳遞時間 

2.併發處理

 

PreparedStatement: 

1.addBatch() 將一組參數添加到 PreparedStatement對象內部 

2.executeBatch() 將一批參數提交給數據庫來執行,若是所有命令執行成功,則返回更新計數組成的數組。

 

Statement: 

addBatch(String sql)方法會在批處理緩存中加入一條sql語句 

executeBatch()執行批處理緩存中的全部sql語句。

 

注意:PreparedStatement中使用批量更新時,要先設置好參數後再使用addBatch()方法加入緩存。 

批量更新中只能使用更新或插入語句

 

.SQL3中的數據類型 

Array:數組 

Sturct:結構 

大對象

Blob:大的二進制數據文件對象。 

Clob:大的文本文件對象。 

優勢

1.理論上大小沒有上限,受制於數據庫表空間的大小

2.流式讀取.

 

使用大對象的步驟

1.先插入一個空的佔位對象empty_blob()(oracle的函數):insert into t_blob values(?,?,empty_blob()); 

2.得到大對象:select blob_data from t_blob where name = ? for update; 

3.獲取流進行寫入:blob.setBinaryStream(0); 

4.經過流來獲取blob中存儲的數據:blob.getBinaryStream()

 

1、IDHigh/Low算法 

高位數字分別與低位數字相匹配,獲得的數字是惟一的 

減小與數據庫的交互

 

2、ORM 

1、類映射成表 

類名與表名對應 

2、屬性定義映射成列,類型之間必須是兼容的 

3、類關係映射成表關係

 

一對一雙向關係 

內存中都保存對方的一個引用 

數據庫中,表bid是主鍵,也是外鍵,引用a表的id主鍵 -- share pk 

b中有一個字段aid是外鍵,引用a表的主鍵,而且有惟一約束  -- pk+fk 

共享主鍵: 

create table car_pk ( 

id number(10,0) not null, 

name varchar2(15), 

serial varchar2(30), 

manufacturer varchar2(50), 

producedate date, 

primary key (id) 

);

 

create table engine_pk ( 

id number(10,0) not null, 

model varchar2(20), 

manufacturer varchar2(50), 

producedate date, 

primary key (id) 

);

 

alter table engine_pk 

add constraint fk_engine_car_pk 

foreign key (id) 

references car_pk(id); 

 

外鍵+惟一約束 

create table car_fk ( 

id number(10,0) not null, 

name varchar2(15) not null, 

serial varchar2(30) not null, 

manufacturer varchar2(50) not null, 

producedate date, 

primary key (id) 

);

 

create table engine_fk ( 

id number(10,0) not null, 

model varchar2(20) not null, 

manufacturer varchar2(50) not null, 

producedate date, 

carid number(10,0) unique, 

primary key (id) 

);

 

alter table engine_fk 

add constraint fk_engine_car_fk 

foreign key (carid) 

references car_fk(id); 

 

實體對象:在內存中有id屬性的 

值對象:沒有id的,依賴其餘對象存在

 

一對多關係 

一的一方保存多一方的一個集合,最好使用set,保證無重複元素 

多的一方保存一一方的一個對象的引用 

public class Order implements Serializable{ 

private int id; 

private String owner; 

private String phone; 

private String address; 

private Set<Item> items = new HashSet<Item>(); 

public class Item implements Serializable{ 

private int id; 

private String product; 

private int amount; 

private Order order; 

 

create table ec_item ( 

id number(10,0) not null, 

product varchar2(15) not null, 

amount number(10,0) not null, 

orderid number(10,0) not null, 

primary key (id) 

);

 

create table ec_order ( 

id number(10,0) not null, 

owner varchar2(15) not null, 

phone varchar2(15) not null, 

address varchar2(50), 

primary key (id) 

);

 

alter table ec_item 

add constraint fk_item_order 

foreign key (orderid) 

references ec_order(id); 

 

 

多對多 

雙方都保存對方的多個引用 

例子:學生選課 

public class TarenaCourse implements Serializable{ 

private int id; 

private String name; 

private int period; 

private Set<TarenaStudent> students = new HashSet<TarenaStudent>(); 

public class TarenaStudent implements Serializable{ 

private int id; 

private String name; 

private Date birthday; 

private Set<TarenaCourse> courses = new HashSet<TarenaCourse>(); 

}

 

create table student ( 

id number(10,0) not null, 

name varchar2(15) not null, 

birthday date, 

primary key (id) 

);

 

create table student_course ( 

sid number(10,0) not null, 

cid number(10,0) not null, 

primary key (sid, cid) 

);

 

create table course ( 

id number(10,0) not null, 

name varchar2(15) not null, 

perion number(10,0), 

primary key (id) 

);

 

alter table student_course 

add constraint fk_student 

foreign key (sid) 

references student(id);

 

alter table student_course 

add constraint fk_course 

foreign key (cid) 

references course(id);

 

經過學生姓名找課程 

select c.name from cource c,student s,student_course sc 

where c.id=sc.cid and s.id=sc.sid 

and s.name = 's1' 

 

3、繼承關係 

public abstract class Computer implements Serializable{ 

private int id; 

private int price; 

private String manufacturer; 

}

 

public class Desktop extends Computer{ 

private boolean isLCD; 

}

 

public class Notepad extends Computer{ 

private float weight; 

private float thickness; 

 

1、建3張表 table per class 

子類中保存父類的主鍵做爲外鍵 

create table computer_tpc ( 

id number(10,0) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

primary key (id) 

);

 

create table desktop_tpc ( 

computerid number(10,0) not null, 

islcd char(1), 

primary key (computerid) 

);

 

create table notepad_tpc ( 

computerid number(10,0) not null, 

weight float, 

thickness float, 

primary key (computerid) 

);

 

alter table desktop_tpc 

add constraint fk_desk_computer_tpc 

foreign key (computerid) 

references computer_tpc(id);

 

alter table notepad_tpc 

add constraint fk_note_computer_tpc 

foreign key (computerid) 

references computer_tpc(id);

 

查找全部電腦的配製(只要是電腦就能被查出來) 

select c.id,c.price,d.islcd,n.weight,n.thickness 

from computer c, desktop d,notepad n 

where c.id = d.computerid(+) 

and c.id = n.computer(+) 

 

2、建2張表 

create table desktop ( 

id number(10,0) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

islcd char(1), 

primary key (id) 

);

 

create table notepad ( 

id number(10,0) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

weight float, 

thickness float, 

primary key (id) 

);

 

3、建1張表 

create table computer_tph ( 

id number(10,0) not null, 

category char(1) not null, 

price number(10,0) not null, 

manufacturer varchar2(30) not null, 

islcd char(1), 

weight float, 

thickness float, 

primary key (id) 

);

 

4、JDBC2.0擴展

 

一、JDBC DataSource 

DataSourse(數據源),包含了鏈接數據庫所需的信息,能夠經過數據源或的數據庫鏈接,有時因爲某些鏈接數據庫的信息會變動, 

因此常用包含數據庫鏈接信息的數據源。 

 

JDBC取鏈接有2種方式:Driver Manager 和 數據源 

 

二、JNDIDataSourse 

主要功能:定位服務 

JNDI,(命名路徑服務)也用於存儲數據,可是他所存儲的是一寫零散的信息。 

JNDI的方法是在javax.naming包下

 

InitialContext 鏈接,初始化上下文,這個類的提供者通常也是服務器的提供者 

查找和綁定 

查找由咱們作,綁定咱們並不關心,只配制數據源就行了 

 

代替DriverManager定位數據源 

遍及式企業的數據源的屬性能夠存儲在同一個目錄(JNDI)中 

以這種方式集中管理用戶名、密碼、數據庫名和JDBC URL 

建立鏈接: 

Context jndiContext = new InitialContext(); 

DataSource source = (DataSource)jndiContext.lookup(" "); 

COnnection con = source.getConnection(); 

 

三、鏈接池 

要提供鏈接池數據源,帶緩存的鏈接 

帶緩存的鏈接,便可池化的鏈接,其close()方法,在物理上並無被關閉,而是保留在一個隊列中並被反覆使用。 

 

四、分佈式事務 

事務分爲JDBC事務和JTA 

JDBC事務,由容器管理 

JTA,分佈式事務,由容器管理

<!--EndFragment-->

相關文章
相關標籤/搜索