一、 JDBC概述
JDBC(Java Data Base Connectivity,java數(shù)據(jù)庫連接)是一種用于執(zhí)行SQL語句的Java API,可以為多種關系數(shù)據(jù)庫提供統(tǒng)一訪問,它由一組用Java語言編寫的類和接口組成。是Java訪問數(shù)據(jù)庫的標準規(guī)范。
JDBC提供了一種基準,據(jù)此可以構(gòu)建更高級的工具和接口,使數(shù)據(jù)庫開發(fā)人員能夠編寫數(shù)據(jù)庫應用程序。
JDBC需要連接驅(qū)動,驅(qū)動是兩個設備要進行通信,滿足一定通信數(shù)據(jù)格式,數(shù)據(jù)格式由設備提供商規(guī)定,設備提供商為設備提供驅(qū)動軟件,通過軟件可以與該設備進行通信。.
所以需要導入對應數(shù)據(jù)庫廠商提供的第三方jar包。

- JDBC原理
Java提供訪問數(shù)據(jù)庫規(guī)范稱為JDBC,而生產(chǎn)廠商提供規(guī)范的實現(xiàn)類稱為驅(qū)動。

JDBC是接口,驅(qū)動是接口的實現(xiàn),沒有驅(qū)動將無法完成數(shù)據(jù)庫連接,從而不能操作數(shù)據(jù)庫!每個數(shù)據(jù)庫廠商都需要提供自己的驅(qū)動,用來連接自己公司的數(shù)據(jù)庫,也就是說驅(qū)動一般都由數(shù)據(jù)庫生成廠商提供。
二、 JDBC開發(fā)步驟
1.準備工作
因為JDBC是Java針對數(shù)據(jù)庫數(shù)據(jù)的操作,所以我們需要先創(chuàng)建數(shù)據(jù)庫數(shù)據(jù)。
#創(chuàng)建數(shù)據(jù)庫
create database day22_JDBC;
#使用數(shù)據(jù)庫
use day22_JDBC;
###創(chuàng)建分類表
create table sort(
sid int PRIMARY KEY ,
sname varchar(100)
);
#初始化數(shù)據(jù)
insert into sort(sname) values('家電');
insert into sort(sname) values('服飾');
insert into sort(sname) values('化妝品');
還需要根據(jù)所使用的數(shù)據(jù)庫,導入對應數(shù)據(jù)庫廠商提供的第三方jar包,在這里我們使用maven來管理:
1. 導入MySQL驅(qū)動包,很簡單:
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
2. 導入Oracle驅(qū)動包,稍復雜:
因為Oracle授權問題,Maven3不提供oracle JDBC driver,我們也可以在Maven的中心搜索ojdbc驅(qū)動包,但是可以看到版本過于陳舊,即使有坐標,也下載不了。
為了可以在使用Maven構(gòu)建的項目中使用Oracle JDBC driver,我們就必須手動添加Oracle的JDBC驅(qū)動依賴到本地倉庫中。
既然要手動添加Maven依賴到本地倉庫中,那么就必須要先得到Oracle數(shù)據(jù)庫的JDBC驅(qū)動包,我們可以通過以下兩種方式獲?。?/p>
- 方式一:通過Oracle官方網(wǎng)站下載相應版本
我們在瀏覽器中輸入:
http://www.oracle.com/technetwork/database/features/jdbc/index-091264.html
由于我安裝的Oracle數(shù)據(jù)庫版本是11.2.0.1.0,因此我選擇下載的是對應的11g版本下的11.2.0.1.0版本,我們點擊上圖選中的連接,在頁面中找到對應的11.2.0.1.0版本模塊
Oracle數(shù)據(jù)庫的JDBC驅(qū)動包
由于Oracle數(shù)據(jù)庫JDBC驅(qū)動包的使用的JDK版本有所區(qū)別,我們一定要下載對應的JDK版本的Oracle數(shù)據(jù)庫JDBC驅(qū)動,JDK1.5的選擇ojdbc.jar驅(qū)動包,如果是JDK1.6及以上版本的選擇ojdbc6.jar驅(qū)動包,我的JDK環(huán)境是JDK1.8,那么選擇ojdbc6.jar驅(qū)動包。

上面的方式可能由于某些原因是無法下載的。
-
方式二:通過Oracle數(shù)據(jù)庫的安裝目錄獲得(推薦)
我們安裝完Oracle數(shù)據(jù)庫后,可以在對應的數(shù)據(jù)庫目錄下找到Oracle數(shù)據(jù)庫的JDBC驅(qū)動包,這個目錄并不是統(tǒng)一的,在安裝的時候是自己指定的,我的數(shù)據(jù)庫實例安裝在這個目錄下:
Oracle數(shù)據(jù)庫的JDBC驅(qū)動包3
獲取到Oracle數(shù)據(jù)庫的JDBC驅(qū)動后,接下來就是手動向Maven的本地倉庫中添加Oracle數(shù)據(jù)庫的JDBC驅(qū)動依賴。
這里安裝的前提是你的電腦必須安裝了Maven,并且配置了Maven環(huán)境變量。
由于我的IDEA配置了本地Maven,可以使用IDEA手動向Maven本地倉庫中添加Oracle數(shù)據(jù)庫的JDBC驅(qū)動依賴。
在上面我們已經(jīng)查找到了Oracle驅(qū)動包位置,但是如果將Maven構(gòu)建jar包的文件路徑直接指向這個目錄,可能出現(xiàn)會Oracle會拒絕訪問,那么我們可以復制ojdbc6.jar到另一個目錄中,我選擇的是E:\JAVArj\oracle11g\jdbc01\lib\ojdbc6.jar
使用Maven的添加依賴(安裝)命令:
mvn install:install-file -Dfile=E:\JAVArj\oracle11g\jdbc01\lib\ojdbc6.jar -DgroupId=com.oracle -DartifactId=ojdbc6 -Dversion=11.2.0.1.0 -Dpackaging=jar
我們可以使用各種工具向Maven倉庫安裝,比如DOS命令窗口,Eclipse的Maven插件,IDEA的Termial,工具不一樣,但是操作都是一樣的,指定的文件位置和Maven坐標都是一樣的,我選擇的是IDEA的Termial。
我們看到Maven將這個jar包已經(jīng)安裝到本地Maven倉庫中,找到這個倉庫目錄:

我們在Maven項目中添加如下Oracle驅(qū)動依賴
<!-- 添加oracle驅(qū)動依賴 -->
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.1.0</version>
</dependency>
Maven坐標一定要和自己構(gòu)建Oracle驅(qū)動Maven依賴時相同,否則導入報錯。
看結(jié)果:

可以在maven依賴圖中找到:

2.開發(fā)步驟
- 注冊驅(qū)動.
- 獲得連接.
- 獲得語句執(zhí)行平臺
- 執(zhí)行sql語句
- 處理結(jié)果
- 釋放資源.
API詳解:注冊驅(qū)動
代碼:Class.forName("com.mysql.jdbc.Driver");
JDBC規(guī)范定義驅(qū)動接口:java.sql.Driver,MySql驅(qū)動包提供了實現(xiàn)類:com.mysql.jdbc.Driver
DriverManager工具類,提供注冊驅(qū)動的方法 registerDriver(),方法的參數(shù)是java.sql.Driver,所以我們可以通過如下語句進行注冊:
DriverManager.registerDriver(new com.mysql.jdbc.Driver());
以上代碼不推薦使用,存在兩方面不足
- 硬編碼,后期不易于程序擴展和維護
- 驅(qū)動被注冊兩次。
通常開發(fā)我們使用Class.forName() 加載一個使用字符串描述的驅(qū)動類。
如果使用Class.forName()將類加載到內(nèi)存,該類的靜態(tài)代碼將自動執(zhí)行。
通過查詢com.mysql.jdbc.Driver源碼,我們發(fā)現(xiàn)Driver類“主動”將自己進行注冊:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
……
}
API詳解:獲得鏈接
代碼:Connection con = DriverManager.getConnection (“jdbc:mysql://localhost:3306/mydb”,”root”,”root”);
獲取連接需要方法 DriverManager.getConnection(url,username,password),三個參數(shù)分別表示:url 需要連接數(shù)據(jù)庫的位置(網(wǎng)址) user用戶名 password 密碼
url比較復雜,mysql的url:jdbc:mysql://localhost:3306/mydb
JDBC規(guī)定url的格式由三部分組成,每個部分中間使用冒號分隔。
第一部分是jdbc,這是固定的;
第二部分是數(shù)據(jù)庫名稱,那么連接mysql數(shù)據(jù)庫,第二部分當然是mysql了;
第三部分是由數(shù)據(jù)庫廠商規(guī)定的,我們需要了解每個數(shù)據(jù)庫廠商的要求,mysql的第三部分分別由數(shù)據(jù)庫服務器的IP地址(localhost)、端口號(3306),以及DATABASE名稱(mydb)組成。
API詳解:獲得語句執(zhí)行平臺
String sql = "某SQL語句";
獲取Statement語句執(zhí)行平臺:Statement stmt = con.createStatement();
常用方法:
int executeUpdate(String sql); --執(zhí)行insert update delete語句.
ResultSet executeQuery(String sql); --執(zhí)行select語句.
boolean execute(String sql); --執(zhí)行select返回true 執(zhí)行其他的語句返回false.
- SQL注入問題
sql注入就是本來我只有我能操作數(shù)據(jù)庫,本來只是讓你輸入內(nèi)容就走,而你卻輸入命令,從而在我不知情下操作數(shù)據(jù)庫。
會產(chǎn)生上門面的情況是因為上面的sql是使用動態(tài)拼接的方式,所以sql傳入的方式可能改變sql的語義。
動態(tài)拼接就是在java中java變量和sql語句混合使用:select * from user where userName=’”+userName+”’ and password = ‘”+password”’
假設有登錄案例SQL語句如下:
SELECT * FROM 用戶表 WHERE NAME = 用戶輸入的用戶名 AND PASSWORD = 用戶輸?shù)拿艽a;
此時,當用戶輸入正確的賬號與密碼后,查詢到了信息則讓用戶登錄。
但是當用戶輸入的賬號為XXX 密碼為:XXX’ OR ‘a(chǎn)’=’a時,則真正執(zhí)行的代碼變?yōu)椋?br>SELECT * FROM 用戶表 WHERE NAME = ‘XXX’ AND PASSWORD =’ XXX’ OR ’a’=’a’;
此時,上述查詢語句時永遠可以查詢出結(jié)果的。那么用戶就直接登錄成功了,顯然我們不希望看到這樣的結(jié)果,這便是SQL注入問題。
為此,我們需要使用預處理對象PreparedStatement來解決對應的問題。
API詳解:預處理對象PreparedStatement
使用PreparedStatement預處理對象時,建議每條sql語句所有的實際參數(shù),都使用逗號分隔。
String sql = "insert into sort(sid,sname) values(?,?)";
PreparedStatement預處理對象代碼:
PreparedStatement psmt = conn.prepareStatement(sql)
常用方法:
執(zhí)行SQL語句:
int executeUpdate(); --執(zhí)行insert update delete語句.
ResultSet executeQuery(); --執(zhí)行select語句.
boolean execute(); --執(zhí)行select返回true 執(zhí)行其他的語句返回false.設置實際參數(shù)
void setXxx(int index, Xxx xx) 將指定參數(shù)設置為給定Java的xx值。在將此值發(fā)送到數(shù)據(jù)庫時,驅(qū)動程序?qū)⑺D(zhuǎn)換成一個 SQLXxx``類型值。
例如:
setString(2, "家用電器") 把SQL語句中第2個位置的占位符? 替換成實際參數(shù) "家用電器"
預處理對象executeUpdate方法
通過預處理對象的executeUpdate方法,完成記錄的insert\update\delete語句的執(zhí)行。操作格式統(tǒng)一如下:
1. 注冊驅(qū)動
2. 獲取連接
3. 獲取預處理對象
4. SQL語句占位符設置實際參數(shù)
5. 執(zhí)行SQL語句
6. 釋放資源預處理對象executeQuery方法
1.注冊驅(qū)動
2.獲取連接
3.獲取預處理對象
4.SQL語句占位符設置實際參數(shù)
5.執(zhí)行SQL語句
6.處理結(jié)果集(遍歷結(jié)果集合)
7.釋放資源
API詳解:處理結(jié)果集(執(zhí)行insert、update、delete無需處理)
ResultSet實際上就是一張二維的表格,我們可以調(diào)用其boolean next()方法指向某行記錄,當?shù)谝淮握{(diào)用next()方法時,便指向第一行記錄的位置,這時就可以使用ResultSet提供的getXXX(int col)方法(與索引從0開始不同個,列從1開始)來獲取指定列的數(shù)據(jù):
rs.next(); //指向第一行
rs.getInt(1); //獲取第一行第一列的數(shù)據(jù)
常用方法:
Object getObject(int index) / Object getObject(String name) 獲得任意對象
String getString(int index) / Object getObject(String name) 獲得字符串
int getInt(int index) / Object getObject(String name) 獲得整形
double getDouble(int index) / Object getObject(String name) 獲得雙精度浮點型
API詳解:釋放資源
與IO流一樣,使用后的東西都需要關閉!關閉的順序是先得到的后關閉,后得到的先關閉。
rs.close();
stmt.close();
con.close();
3.JDBC操作數(shù)據(jù)庫實例
- 插入記錄:insert
實現(xiàn)向分類表中插入指定的新分類
public void demo01() throws Exception {
// 1注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
// 2獲取連接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
// 3獲得預處理對象
String sql = "insert into sort(sname) values(?)";
PreparedStatement stat = conn.prepareStatement(sql);
// 4 SQL語句占位符設置實際參數(shù)
stat.setString(1, "奢侈品");
// 5執(zhí)行SQL語句
int line = stat.executeUpdate();
System.out.println("新添加記錄數(shù):" + line);
// 6釋放資源
stat.close();
conn.close();
}
- 更新記錄:update
實現(xiàn)更新分類表中指定分類ID所對應記錄的分類名稱
public void demo02() throws Exception {
// 1注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
// 2獲取連接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
// 3獲得預處理對象中
String sql = "update sort set sname=? where sid=?";
PreparedStatement stat = conn.prepareStatement(sql);
// 4 SQL語句占位符設置實際參數(shù)
stat.setString(1, "數(shù)碼產(chǎn)品");
stat.setInt(2, 1);
// 5執(zhí)行SQL語句
int line = stat.executeUpdate();
System.out.println("更新記錄數(shù):" + line);
// 6釋放資源
stat.close();
conn.close();
}
- 刪除記錄:delete
實現(xiàn)刪除分類表中指定分類ID的記錄
public void demo03() throws Exception {
// 1注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
// 2獲取連接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
// 3獲得預處理對象
String sql = "delete from sort where sid=?";
PreparedStatement stat = conn.prepareStatement(sql);
// 4 SQL語句占位符設置實際參數(shù)
stat.setInt(1, 1);
// 5執(zhí)行SQL語句
int line = stat.executeUpdate();
System.out.println("刪除記錄數(shù):" + line);
// 6釋放資源
stat.close();
conn.close();
}
- 查詢記錄:select
實現(xiàn)查詢分類表所有記錄
public void demo04() throws Exception {
// 1注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
// 2獲取連接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
// 3獲得預處理對象
String sql = "select * from sort";
PreparedStatement stat = conn.prepareStatement(sql);
// 4 SQL語句占位符設置實際參數(shù)
// 5執(zhí)行SQL語句
ResultSet rs = stat.executeQuery();
// 6處理結(jié)果集(遍歷結(jié)果集合)
while( rs.next() ){
//獲取當前行的分類ID
String sid = rs.getString("sid");//方法參數(shù)為數(shù)據(jù)庫表中的列名
//獲取當前行的分類名稱
String sname = rs.getString("sname");
//顯示數(shù)據(jù)
System.out.println(sid+"-----"+sname);
}
// 7釋放資源
rs.close();
stat.close();
conn.close();
}
實現(xiàn)查詢分類表中指定分類名稱的記錄
public void demo05() throws Exception {
// 1注冊驅(qū)動
Class.forName("com.mysql.jdbc.Driver");
// 2獲取連接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "root");
// 3獲得預處理對象
String sql = "select * from sort where sname=?";
PreparedStatement stat = conn.prepareStatement(sql);
// 4 SQL語句占位符設置實際參數(shù)
stat.setString(1, "奢侈品");
// 5執(zhí)行SQL語句
ResultSet rs = stat.executeQuery();
// 6處理結(jié)果集(遍歷結(jié)果集合)
while( rs.next() ){
//獲取當前行的分類ID
String sid = rs.getString("sid");//方法參數(shù)為數(shù)據(jù)庫表中的列名
//獲取當前行的分類名稱
String sname = rs.getString("sname");
//顯示數(shù)據(jù)
System.out.println(sid+"-----"+sname);
}
// 7釋放資源
rs.close();
stat.close();
conn.close();
}
三、 封裝JDBC工具類
1. 簡單封裝
“獲得數(shù)據(jù)庫連接”操作,將在以后的增刪改查所有功能中都存在,可以封裝工具類JDBCUtils。提供獲取連接對象的方法,從而達到代碼的重復利用。
該工具類提供方法:public static Connection getConn()。代碼如下:
/*
* JDBC工具類
*/
public class JDBCUtils {
public static final String DRIVERNAME = "com.mysql.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/mydb";
public static final String USER = "root";
public static final String PASSWORD = "root";
static {
try {
Class.forName(DRIVERNAME);
} catch (ClassNotFoundException e) {
System.out.println("數(shù)據(jù)庫驅(qū)動注冊失??!");
}
}
//提供獲取連接的方法
public static Connection getConn() throws Exception {
// 2\. 獲得連接
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
// 返回連接
return conn;
}
}
2.使用properties配置文件做進一步封裝
上述封裝雖然減少了一部分重復的代碼,但如果我們更換了數(shù)據(jù)庫或者改動了用戶名或密碼,依然需要到Java類中改動代碼,這不符合Java編程的某些原則,也不利于后期程序的維護,所以我們還需要對它做進一步的封裝。
開發(fā)中獲得連接的4個參數(shù)(驅(qū)動、URL、用戶名、密碼)通常都存在配置文件中,方便后期維護,程序如果需要更換數(shù)據(jù)庫,只需要修改配置文件即可。
通常情況下,我們習慣使用properties文件,此文件我們將做如下要求:
- 文件位置:任意,建議src下
- 文件名稱:任意,擴展名為properties
- 文件內(nèi)容:一行一組數(shù)據(jù),格式是“key=value”.
a) key命名自定義,如果是多個單詞,習慣使用點分隔。例如:jdbc.driver
b) value值不支持中文,如果需要使用非英文字符,將進行unicode轉(zhuǎn)換。
2.1 創(chuàng)建配置文件
在項目跟目錄下,創(chuàng)建文件,輸入“db.properties”文件名。
文件中的內(nèi)容:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mydb
user=root
password=root
2.2 加載配置文件:Properties對象
對應properties文件處理,開發(fā)中也使用Properties對象進行。我們將采用加載properties文件獲得流,然后使用Properties對象進行處理。
JDBCUtils.java中編寫代碼
public class JDBCUtils {
private static String driver;
private static String url;
private static String user;
private static String password;
// 靜態(tài)代碼塊
static{
try{
// 1 使用Properties處理流
// 使用load()方法加載指定的流
Properties props = new Properties();
Reader is = new FileReader("db.properties");
props.load(is);
// 2 使用getProperty(key),通過key獲得需要的值,
driver = props.getProperty("driver");
url = props.getProperty("url");
user = props.getProperty("user");
password = props.getProperty("password");
} catch(Exception e) {
throw new RuntimeException(e);
}
}
/**
* 獲得連接
*/
public static Connection getConnection() {
try {
// 1 注冊驅(qū)動
Class.forName(driver);
// 2 獲得連接
Connection conn = DriverManager.getConnection(url, user, password);
return conn;
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
2.3 使用JDBCUtils工具類
測試類
public class Demo {
@Test
public void insert(){
try{
//1,獲取連接對象
Connection conn = JDBCUtils.getConnection();
//2,指定要執(zhí)行的SQL語句
String sql = "INSERT INTO zhangwu(name,money,parent) VALUES(?,?,?)";
//4,獲取SQL語句的執(zhí)行對象 PreparedStatement
PreparedStatement ppstat = conn.prepareStatement(sql);
//5,執(zhí)行SQL語句
ppstat.setString(1, "股票收入");
ppstat.setDouble(2, 5000);
ppstat.setString(3, "收入");
int line = ppstat.executeUpdate();
//6,處理結(jié)果集
System.out.println("line=" + line);
//7,關閉連接
ppstat.close();
conn.close();
} catch(SQLException e){
throw new RuntimeException(e);
}
}
}

