JDBC原理
一.JDBC原理概述
1,JDBC是一套協議,是JAVA開發人員和數據庫廠商達成的協議,也就是由Sun定義一組接口,由數據庫廠商來實現,并規定了JAVA開發人員訪問數據庫所使用的方法的調用規范。
2,JDBC的實現是由數據庫廠商提供,以驅動程序形式提供。
3,JDBC在使用前要先加載驅動。
JDBC對于使用者要有一致性,對不同的數據庫其使用方法都是相同的。
驅動開發必須要實現Driver接口。
數據庫驅動的實現方式
JDBC-ODBC橋接式
JDBC網絡驅動,這種方式是通過中間服務器的協議轉換來實現的
JDBC+本地驅動,這種方式的安全性比較差。
JDBC驅動,由數據庫廠商實現。
二.JDBC的API
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。
注意:6,7兩個步驟是必須要做的,因為這些資源是不會自動釋放的,必須要自己關閉
訪問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:XXXX(IP地址及端口號):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返回0
execute(sqlString);
//適用于不知道具體的操作是什么,返回值是boolean類型的
//如果返回值是true,代表執行查詢操作;否則代表執行更新操作.
ResultSet
next()方法:
1.判斷是否存在下一條記錄
2.將游標移向下一條記錄
getXXX(字段名或字段序號)//注意:字段序號從1開始
關閉問題:
使用Connection對象獲得一個Statement,Statement中的executeQuery(String sql) 方法可以使用select語句查詢,并且返回一個結果集 ResultSet通過遍歷這個結果集,可以獲得select語句的查詢結果,ResultSet的next()方法會操作一個游標從第一條記錄的前邊開 始讀取,直到最后一條記錄。executeUpdate(String sql) 方法用于執行DDL和DML語句,可以update,delete操作。
注意:要按先ResultSet結果集,后Statement,最后Connection的順序關閉資源,因為Statement和ResultSet是需要連接時才可以使用的,所以在使用結束之后有可能其他的Statement還需要連接,所以不能先關閉
一、Statement
execute(sql); 當不知道執行的SQL語句是什么類型的時候執行 ,返回值是boolean
executeQuery(sql); 執行查詢語句
executeUpdate(sql); 執行更新語句
二、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()可以獲得錯誤碼,可以對錯誤進行查詢。
三、源數據
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();
}
}
四、數據庫源數據
DatabaseMetaData
getURL(),獲得連接數據庫的URL
getDatabaseProductName() 獲得數據庫產品的名稱
getDriverVersion() 獲得JDBC驅動程序的String形式的版本號
getTables()獲得數據庫中該用戶的所有表
getUserName() 獲得數據庫用戶名。
五、異常的處理
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()
一、ID的High/Low算法
高位數字分別與低位數字相匹配,得到的數字是唯一的
減少與數據庫的交互
二、ORM
1、類映射成表
類名與表名對應
2、屬性定義映射成列,類型之間必須是兼容的
3、類關系映射成表關系
一對一雙向關系
內存中都保存對方的一個引用
數據庫中,表b的id是主鍵,也是外鍵,引用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'
三、繼承關系
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)
);
四、JDBC2.0擴展
1、JDBC DataSource
DataSourse(數據源),包含了連接數據庫所需的信息,可以通過數據源或的數據庫連接,有時由于某些連接數據庫的信息會變更,
所以經常使用包含數據庫連接信息的數據源。
JDBC取連接有2種方式:Driver Manager 和 數據源
2、JNDI和DataSourse
主要功能:定位服務
JNDI,(命名路徑服務)也用于存儲數據,但是他所存儲的是一寫零散的信息。
JNDI的方法是在javax.naming包下
InitialContext 連接,初始化上下文,這個類的提供者一般也是服務器的提供者
查找和綁定
查找由我們做,綁定我們并不關心,只配制數據源就好了
代替DriverManager定位數據源
遍布式企業的數據源的屬性可以存儲在同一個目錄(JNDI)中
以這種方式集中管理用戶名、密碼、數據庫名和JDBC URL
創建連接:
Context jndiContext = new InitialContext();
DataSource source = (DataSource)jndiContext.lookup(" ");
COnnection con = source.getConnection();
3、連接池
要提供連接池數據源,帶緩存的連接
帶緩存的連接,即可池化的連接,其close()方法,在物理上并沒有被關閉,而是保留在一個隊列中并被反復使用。
4、分布式事務
事務分為JDBC事務和JTA
JDBC事務,由容器管理
JTA,分布式事務,由容器管理