JDBC詳解
JDBC原理概述
1,JDBC是一套協議,是JAVA開發人員和數據庫廠商達成的協議,也就是由Sun定義一組接口,由數據庫廠商來實現,并規定了JAVA開發人員訪問數據庫所使用的方法的掉用規范。
2,JDBC的實現是由數據庫廠商提供,以驅動程序形式提供。
3,JDBC在使用前要先加載驅動。
JDBC對于使用者要有一致性,對不同的數據庫其使用方法都是相同的。
driver開發必須要實現Driver接口。
數據庫驅動的實現方式
JDBC-ODBC橋接式
JDBC網絡驅動,這種方式是通過中間服務器的協議轉換來實現的
JDBC+本地驅動,這種方式的安全性比較差
JDBC驅動,由數據庫廠商實現。
JDBC的API
java.sql包和javax.sql包
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。
注意:6,7兩個步驟勢必須要做的,因為這些資源是不會自動釋放的,必須要自己關閉
訪問Oracle的數據庫的驅動名字叫ojdbc14.jar,這個jar文件中出訪的驅動程序的.class文件
要使用這個驅動程序,要先將他加到環境變量PATH中。
一,注冊加載驅動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:XXXX(IP地址及端口號):XXXXXXX(所使用的庫名)
例:jdbc:oracle:thin:@192.168.0.39:1521:TARENADB
MySql URL的寫法
例:jdbc:mysql://192.168.8.21:3306/test
SQLServer URL的寫法
例:jdbc:microsoft:sqlserver://192.168.8.21:1433
java -Djdbc.drivers=驅動全名 類名
使用系統屬性名,加載驅動 -D表示為系統屬性賦值
使用Connection對象獲得一個Statement,Statement中的executeQuery(String sql) 方法可以使用select語句查詢,并且返回一個結果集 ResultSet通過遍歷這個結果集,可以獲得select語句的查尋結果,ResultSet的next()方法會操作一個游標從第一條記錄的前邊開始讀取,直到最后一條記錄。executeUpdate(Stringsql) 方法用于執行DDL和DML語句,可以update,delete操作。
注意:要按先ResultSet結果集,后Statement,最后 Connection的順序關閉資源,因為Statement和ResultSet是需要連接是才可以使用的,所以在使用結束之后有可能起他的 Statement還需要連接,所以不能現關閉Connection。
預編譯的Statement
PreparedStatement 可以使用參數替代sql語句中的某些參數使用 "?"代替,他先將帶參數的sql語句發送到數據庫,進行編譯,然后PreparedStatement會將參數發送給數據庫。
在使用PreparedStatement時,在設置相應參數時,要指明參數的位置和類型,以及給出參數值
根據不同的參數類型使用不同的setXXX(參數的位置,參數值)來設置參數
例:
public void insert(Student s){
Connectioncon=ConnectionFactory.getConnection();//建立連接
Stringsql="insert into student(id,name) values(?,?)";
PreparedStatementps=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(...)方法,來執行這個存儲過程,"..."是存儲過程的名字。
SQLException是檢查異常必須處理要么throws ,要么try{}catch(){}
getErrorCode()可以獲得錯誤碼,可以對錯誤進行查詢。
源數據
JDBC中有兩種源數據,一種是數據庫源數據,另一種是ResultSet源數據。
源數據就是描述存儲用戶數據的容器的數據結構。
ResultSet rs=ps.executeQuery(sql);
ResultSetMetaData m=rs.getMetaData();
getColumnCount(),獲得實際列數
getColumnName(int colnum),獲得指定列的列名
getColumnType(int colnum),獲得指定列的數據類型
getColumnTypeName(int colnum),獲得指定列的數據類型名
數據庫源數據
DatabaseMetaData
getURL(),獲得連接數據庫的URL
getDatabaseProductName() 獲得數據庫產品的名稱
getDriverVersion() 獲得JDBC驅動程序的String形式的版本號
getTables()獲得數據庫中該用戶的所有表
getUserName() 獲得數據庫用戶名。
事務(Transaction)
事務是針對原子操作的,要求原子操作不可再分,要求原子操作必須同時成功同時失敗。
事務是捆綁的原子操作的邊界。
JDBC中使用事務,先要使用連接調用setAutoCommite(false)方法,把自動提交(commit)置為false。打開事務就要關閉自動提交。不用事務是要把setAutoCommite(true)
在處理事務時,在發送sql語句后執行成功并確認時,就在try塊中使用連接調用commit()方法來發送提交信息,在發送sql語句后執行失敗時,會在catch語句塊中使用連接調用rollback()方法來發送回滾信息,也可以在需要時做回滾操作(主觀原因)。
JDBC事務并發產生的問題和事務隔離級別
1,臟讀(dirty read),讀取到了沒有提交的數據。
2,不可重復讀(UnPrpeatable Read),兩次讀取到了不同的數據,就是要保持在同一時間點上兩次讀取到的數據相同,不能夠使查詢數據時進行改變。
3,幻讀(phantom),在兩次查詢同一時間點數據時,數據數量發生改變,要保持在同一時間點上兩次讀取到的數據相同。
事務隔離級別
TRANSACTION_NONE不使用事務。
TRANSACTION_READ_UNCOMMITTED 可以讀取為提交數據。
TRANSACTION_READ_COMMITTED可以避免臟讀,不能夠讀取沒提交的數據,最常用的隔離級別 大部分數據庫的默認隔離級別
TRANSACTION_REPEATABLE_READ可以避免臟讀,重復讀取,
TRANSACTION_SERIALIZABLE可以避免臟讀,重復讀取和幻讀,(事務串行化)會降低數據庫效率
以上的五個事務隔離級別都是在Connection類中定義的靜態常量,使用setTransactionIsolation(intlevel) 方法可以設置事務隔離級別。
JDBC2.0新特性
可滾動結果集(可雙向滾動),這種結果集不但可以雙向滾動,相對定位,絕對定位,并且可以修改數據信息。
滾動特性
next(),此方法是使游標向下一條記錄移動。
previous() ,此方法可以使游標上一條記錄移動,前提前面還有記錄。
absolute(int row),可以使用此方法跳到指定的記錄位置。定位成功返回true,不成功返回false,返回值為false,則游標不會移動。
afterLast() ,游標跳到最后一條記錄之后,(結果集一回來時就有的位置)。
beforeFirst() ,游標跳到第一條記錄 之前,(結果集一回來時就有的位置)。(跳到游標初始位)
first(),游標指向第一條記錄。
last(),有彪指向最后一條記錄。
relative(int rows) ,相對定位方法,參數值可正可負,參數為正,游標從當前位置向下移動指定值,參數為負,游標從當前位置向上移動指定值。
TYPE_FORWARD_ONLY ,單向,該常量指示指針只能向前移動的 ResultSet 對象的類型。不可滾動。
TYPE_SCROLL_INSENSITIVE ,雙向,該常量指示可滾動但通常不受其他的更改影響的 ResultSet 對象的類型。
TYPE_SCROLL_SENSITIVE ,雙向,該常量指示可滾動并且通常受其他的更改影響的 ResultSet 對象的類型。該特性某些數據庫不支持。
要使用可滾動結果集時,要在Statement創建時指定參數,才可以使用
Statement st=null;
(int,int)(可滾動特性,可更新特性)
st=con.createStatement(ReusltSet.TYPE_SCROLL_INSENSITIVE,ResuleSet.CONCUR_UPDATABLE)
ResultSet結果集中,先使用moveToInsertRow(),將游標移到和結果集結構類似的緩沖區中
然后可以使用updateXxx(intcolumn,columnType value)方法來更新指定列數據,再使用insertRow() 方法插入記錄,最后將游標指回原位,moveToCurrentRow() 。
能否使用可更新結果集,要看使用的數據庫驅動是否支持,還有只能用于單表且表中有主鍵字段(可能會是聯合主鍵),不能夠有表連接,會取所有非空字段且沒有默認值。結果集用select * from t也不行,不能用*,不能排序
能否使用JDBC2.0ResultSet的新特性要看數據庫驅動程序是否支持。
批處理更新
Statement.
addBatch(String sql), 方法會在批處理緩存中加入一條sql語句
executeBatch() ,執行批處理緩存中的所有sql語句。
PreparedStatement. 先準備一組參數
addBatch() 將一組參數添加到此 PreparedStatement 對象的批處理命令中。
executeBatch() 將一批命令提交給數據庫來執行,如果全部命令執行成功,則返回更新計數組成的數組。
PreparedStatement中使用批量更新時,要先設置好參數后使用addBatch()方法加入緩存。
注意:批量更新中只能使用更新或插入語句
execute(String sql),這個方法的返回值是boolean類型,如果返回true就表示sql是一個select語句,可以通過getResultSet()獲得結果集,如果是false,sql就是DML語句或者是DDL語句。
SQL3.0中的行類型
Array,數組
Sturct,結構類似給該列類型起個描述性的別名
Blob,大的二進制數據文件 create table t_blob(idnumber(12) primary key,filename varchar(20),blobData blob);
ps=con.prepareStatement("insert intot_blob" +
"values(?,?,empty_blob())");。
Clob,大文本文件對象。
在使用上述大對象的時候,在使用JDBC插入記錄時要先插入一個空的占位對象,然后使用
select blobdata from t_blob where id =" + id + " for update 這樣的語法來對獲得的大對象,進行實際的寫入操作 Blod通過getBinaryOutputStream()方法獲取流進行寫入。getBinaryStream()方法獲得流來獲取blob中存儲的數據。
clob的操作也和blob相同。getAsciiStream()方法用于讀取存儲的文本對象,getAsciiOutputStream()方法之獲得流用來向文件對象寫入的。
JDBC2.0擴展
JNDI和DataSourse
JNDI,(命名路徑服務)也用于存儲數據,但是他所存儲的是一寫零散的信息。
JNDI的方法是在javax.naming包下
bind(String name, Object obj) 將名稱綁定到對象資源,建立指定的字符串和對象資源的關聯
lookup(String name) ,通過指定的字符串獲得先前綁定的資源
以下是將資源和JNDI命名綁定的方法
public static void bind(String context, Object obj) throwsNamingException
{
Properties pro = new Properties();
//Weblogic的JNDI服務器參數
pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");
Context ctx = new InitialContext(pro);
ctx.bind(context, obj);//建立指定的字符串和對象資源的關聯
}
DataSourse(數據源),包含了連接數據庫所需的信息,可以通過數據源或的數據庫連接,有時由于某些連接數據庫的信息會變更,所以經常使用包含數據庫連接信息的數據源。
通過JNDI獲得綁定的資源
public static Object lookup(String context)throws NamingException
{
Properties pro = new Properties();
//Weblogic的JNDI服務器參數
pro.put(InitialContext.INITIAL_CONTEXT_FACTORY,"weblogic.jndi.WLInitialContextFactory");
pro.put(InitialContext.PROVIDER_URL, "t3://localhost:7001");
Context ctx = new InitialContext(pro);
return ctx.lookup(context);//通過指定的字符串獲得先前綁定的資源。
}
連接池,保持連接池中有指定個數的連接,并在程序使用過之后不關閉連接,再放回連接池中等待其他的程序在需要時來取用,這樣可以大量的節省銷毀和創建連接的資源消耗。
JTA分布式的事務
分布式事務是針對多個不同數據庫同時操作,要保證原子操作的不可分,也不用再自己寫commit,和rollback,全部都交給中間服務器來處理。(兩階段提交),也就是在中間服務器發送sql語句等待數據庫回應,都回應操作成功才提交,否則同時回滾。
RowSet
行集,這是一個JavaBean(事件機制),它增強了ResultSet的功能,通過RowSet可以獲得數據源,設置隔離級別,也可以發送查尋語句,也實現了離線的操作遍歷,RowSet也支持預編譯的Statement。
RowSet中的方法大致上和ResultSet相同,當需要使用時請查閱JAVA API參考文檔。
面向對象的數據庫設計
Id通常是用來表示記錄的唯一性的,通常會使用業務無關的數字類型
Object id 對象的id,sequence只有Oracle才可用,對象id(OID)使用高低位算法先生成高位,在生成低位,通過運算獲得對象id。
類應當對象到表,屬性對應字段,對象對應記錄。
類繼承關系對應表,
1,每個類建一個表,為父子類每個類都對應的創建表,這種方法類關系清晰,但是如果類比較多就不適合了
2,只有具體類才建表,也就是把父類中的屬性均勻分配到子類的表中,也就是父類不建表,這種表關系不能使用多態
3,所有類對應一張表,這種方法是在標中加上一個字段來區分父子類,但是只能用于類屬性較少的情況下,而且數據會有冗余。
類關聯關系對應表
1,一對一關聯,類關系對應成表時有兩種做法,一是引用主鍵,也就是一方引用另一方的主鍵既作為外鍵有作為自身的主鍵。二是外鍵引用,一方引用另一方的主鍵作為自身的外鍵,并且自己擁有主鍵。
2,一對多關聯,也就是多端引用一端的主鍵當作外鍵,多端自身擁有主鍵。
3,多對多關系,多對多關系是通過中間表來實現的,中間表引用兩表的主鍵當作聯合主鍵,就可以實現多對多關聯。
JDCB應用的分層
分層就是對功能的隔離,降低層與層間的耦合性。