---
tags: JDBC
---
# <font class=o>JDBC1</font>
# <font class=g style="background-color:#abd7;">JDBC API 簡介</font>
{%hackmd BJrTq20hE %}
<style>
.o{
color:orange;
}
.k{
color:black;
}
.f{
color:#abd7;
}
.p{
color:#6d7c;
}
.y {
color: yellow;
}
.g {
color:greenyellow;
}
.b {
color:#6b70fa;
}
.r{
color:red;
}
//底線顏色
.mark{
color: #22053b;
background: linear-gradient(gray 50%,gray 50%);
border-radius: 5px;
}
</style>
## [CRUD](https://zh.wikipedia.org/wiki/%E5%A2%9E%E5%88%AA%E6%9F%A5%E6%94%B9)
增加(Create,意為「建立」)
刪除(Delete)
查詢(Read,意為「讀取」)
改正(Update,意為「更新」)
多層式架構就是為了解決<span class=g>相依性過高</span>的問題
JDBC是用於<span class=g>執行SQL的解決方案</span>關聯式資料庫
JDBC API已包含在JDK裡
---
## <span class=g>java.sql</span>:JDBC的核心API,用來存取資料庫資料,基本功能
## <span class=g>javax.sql</span>:JDBC Extension API,支援進階資料庫存取功能
JDBC API使得應用系統開發者能使用<font color=gree>相同的介面名稱與方法來存取資料庫的資料</font>,讓程式設計師能專心於應用系統的開發,<font color=gree>無需顧慮到不同廠商所提供的不同資料庫系統</font>
[介面的五大特性](https://hackmd.io/@XucbOJaPQomAPvBTtrCZ8Q/HJcz9BJ_t)
定義規格
型別轉換
貼標籤
降低相依性
多重繼承


# ==connection 非常重要==
```java
ResultSet
對應Select指令 ,代表查詢結果
statement
對應SQL指令的執行
SQLException
一定要處理的例外
```

# JDBC Driver
Type 4 Driver
Native Protocol Driver
driver可以完全用Java技術實現
達到跨平台功能,效能也有不錯表現,
為業界最常見的driver類型
mysql jdbc driver type 4
oracle 11g jdbc driver type 4
sqlserver jdbc driver type 4
---
# <span class = y>載入JDBC Driver</span>
### ==第1種做法: <span class= g>class loader方式 常用</span>==
## class loader 類別載入器
```java
Class.forName(“com.mysql.cj.jdbc.Driver”); (MySQL 8以後的驅動名稱)
com.mysql.cj.jdbc.Driver
廠商實作Driver介面的類別完整名稱(套件名+類別名)
```
步驟
```java
public class HelloJDBC {
//驅動
public static final String DRIVER = "com.mysql.cj.jdbc.Driver";
Class.forName(DRIVER);
/*ClassNotFoundException*/
```
```java
try {
//step 1:載入驅動
Class.forName(DRIVER);
System.out.println("載入成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
```
JDK 8以前類別載入器可分為三個層級:
Bootstrap Loader → ExtClassLoader →AppClassLoader
AppClassLoader ->對應到的就是我們自己的類別路徑java-1 (模組28-3) 環境變數的classpath設定
### ==第2種做法: <span class= g>~~register~~</span> 不建議使用==
:需要手動new 新的物件
因為new也是載入類別, 那載入時就已產生物件,這樣就會建立第二個驅動物件
### ==第3種作法: System Property方式 看情況==
```java
System.setProperty(“jdbc.drivers”, “com.mysql.jdbc.Driver”);
“jdbc.drivers” -> 固定
“com.mysql.jdbc.Driver” ->要一次載入的各種驅動名稱
可以'包含數個drivers',彼此之間以冒號(:)隔開
```
# <div style="background-color:#abd7;" > <span class=k>建立與關閉資料庫連線</span> </div>
# 資料庫位置(URL)
資料庫URL為一字串,用以說明連接某一特定資料庫所需要的資訊,包含
->資料庫位址<span class=g>(IP)</span>
->埠號(Port Number ,從0-65535,有65536個 )
潛規則0-1024不使用
->名稱等

3306 mysql預設的埠號
<span class=g>(註:MySQL 8 以後的驅動版本需在URL後接著?serverTimezone=Asia/Taipei)</span> 時區
```java
public static final String URL = "jdbc:mysql://localhost:3306/jdbcsample?serverTimezone=Asia/Taipei";
```
```java
public static final String USER ="David";
public static final String PASSWORD ="123456";
/*mysql設定的David*/
```

# Connection
```java
/*JDBC Api所以要選擇java.sql*/
import java.sql.Connection;
```
```java
//step 2:建立連線
Connection con = DriverManager.getConnection(URL, USER, PASSWORD);
```

```java
/*SQLException例外*/
} catch (SQLException se) {
se.printStackTrace();
}
```
### <span class=g>Connection非常珍貴,使用完畢後務必完成歸還的動作!</span>



```java=
con.close();
/*跟區域變數有關 ,con變數宣告的位置在try*/
/*區域變數沒有預設的起始值*/
```
但問題是con如果沒有拿到
con.close()沒執行到依然會跳例外在finally
所以修改方式是:
## 1
```java
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class HelloJDBC {
// 驅動
public static final String DRIVER = "com.mysql.cj.jdbc.Driver"
public static final String URL = "jdbc:mysql://localhost:3306/jdbcsample?serverTimezone=Asia/Taipei";
public static final String USER = "David";
public static final String PASSWORD = "123456";
public static void main(String[] args) {
try {
// step 1:載入驅動
Class.forName(DRIVER);
System.out.println("載入成功");
// step 2:建立連線
Connection con = DriverManager.getConnection(URL, USER, PASSWORD);
System.out.println("連線成功");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException se) {
se.printStackTrace();
}
}
}
```
---
#### 2
```java
Connection con = null;
try {
// step 1:載入驅動
Class.forName(DRIVER);
System.out.println("載入成功");
// step 2:建立連線
con = DriverManager.getConnection(URL, USER, PASSWORD);
System.out.println("連線成功");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException se) {
se.printStackTrace();
} finally {
if(con!=null) {
try {
con.close();/*跟區域變數有關 ,con變數宣告的位置在try*/
/*區域變數沒有預設的起始值*/
System.out.println("連線關閉");
}catch(SQLException se) {
se.printStackTrace();
}
}
}
```
---
```java
import java.sql.Statement;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class HelloJDBC {
// 驅動
public static final String DRIVER = "com.mysql.cj.jdbc.Driver";
public static final String URL = "jdbc:mysql://localhost:3306/jdbcsample?serverTimezone=Asia/Taipei";
public static final String USER = "David";
public static final String PASSWORD = "123456";
public static void main(String[] args) {
Connection con = null;
Statement stmt = null;
try {
// step 1:載入驅動
Class.forName(DRIVER);
System.out.println("載入成功");
// step 2:建立連線
con = DriverManager.getConnection(URL, USER, PASSWORD);
System.out.println("連線成功");
// con.close();/*關閉資源*/
// step 3:送出SQL指令
stmt = con.createStatement();
int count = stmt.executeUpdate("INSERT INTO department VALUES(50,'總務部';,'中壢')");
System.out.println(count +"row(s) updated.");
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SQLException se) {
se.printStackTrace();
} finally {
if(stmt!=null) {
try {
stmt.close();/*跟區域變數有關 ,con變數宣告的位置在try*/
/*區域變數沒有預設的起始值*/
}catch(SQLException se) {
se.printStackTrace();
}
}
if(con!=null) {
try {
con.close();/*跟區域變數有關 ,con變數宣告的位置在try*/
/*區域變數沒有預設的起始值*/
System.out.println("連線關閉");
}catch(SQLException se) {
se.printStackTrace();
}
}
}
}
}
```
# <span class = g style="background-color:#abd7;"> Statement介面</span>
藉由Connection的方法:Statement createStatement()來建立與取得Statement物件
藉由Statement物件,才能執行<span class=g>靜態</span>的SQL指令並與資料庫交談
```java
Statement stmt = null;
step 3:送出SQL指令
stmt = con.createStatement();
int count = stmt.executeUpdate("INSERT INTO department VALUES(50,'總務部','中壢')");
System.out.println(count +"row(s) updated");
if(stmt!=null) {
try {
stmt.close();/*跟區域變數有關 ,con變數宣告的位置在try*/
/*區域變數沒有預設的起始值*/
}catch(SQLException se) {
se.printStackTrace();
}
}
```
## 執行SQL指令
```java
ResultSet rs = null;
stmt = con.createStatement();
rs = stmt.executeQuery("SELECT * FROM department ORDER BY deptno DESC");
if(rs!=null) {
try {
rs.close();/*跟區域變數有關 ,con變數宣告的位置在try*/
/*區域變數沒有預設的起始值*/
}catch(SQLException se) {
se.printStackTrace();
}
}
```
# <span class = g style="background-color:#abd7;"> ResultSet介面</span>
游標起始位置: 在查詢結果第一列的上方
會有一個ResultSet的游標
移動資料列游標
移動資料列游標的相關方法
### <span class=g>next()</span>
next()傳回boolean值
true代表游標停留位置有指向一個資料列,false即代表沒有指向資料列
取出查詢結果資料欄的方法
getXXX();
<span class=p>index從1開始,一般來說使用index較有效率</span>
```java
while(rs.next()) {
int deptno = rs.getInt(1);
String dname = rs.getString(2);
String loc = rs.getString(3);
```
順序對應關係
```java
rs = stmt.executeQuery("SELECT dname,deptno,
loc FROM department ORDER BY deptno DESC");
```



```java
* 駭客們所稱呼的"填空遊戲",將鍵盤輸入資料當做變數所組合成的SQL句子: SELECT * FROM USER WHERE
* (NAME = '1' OR '1'='1') and (PASSWORD = '1' OR '1'='1');
```
# <span class = g style="background-color:#abd7;"> PreparedStatement 介面</span>
因為可以動態處理SQL指令,可避免SQL Injection攻擊
```java
pstmt = con.prepareStatement("INSERT INTO department VALUES(?,?,?)");
```

無須再傳參數 ,因為指令已經預先交給資料庫
# <span class = g style="background-color:#abd7;"> CallableStatement 介面</span>
預設schema

delimiter # ,end #
最後還是要切換分號
IN -> input 輸入

固定內容


{call }

<font class=p>CallableStatement prepareCall(String sql)</font>:使用<span class=p>預存程序(stored procedure)</span>,預存程序已事先內建在資料庫中,通常比預先編譯的效能佳
<font color=gree>excute</font> ---> CallableStatement
boolean excute(): 用來執行"未知"的SQL指令,例如預存程序
```java
cstmt.registerOutParameter(2, Types.DOUBLE);//第2個? 註冊成為輸出的參數 (後面定義型別)
```
# <span class = g style="background-color:#abd7;"> ResultSet進階操作</span>

## 游標進階操作
### <font color=gree>ResultSetType</font>
<span class=b>TYPE_FORWARD_ONLY (只能前進)</span>
<span class=b>TYPE_SCROLL_INSENSITIVE (可前後移動,不反應資料修改)</span>
->當前
<span class='b'>TYPE_SCROLL_SENSITIVE (可前後移動,反應資料修改)</span>
若是查詢結果有發生更新的情況
當游標移動到該筆資料時
是否要反應更新的結果狀態
註:若沒設定,<font color=gree>預設</font>為<font color=gree>TYPE_FORWARD_ONLY & CONCUR_READ_ONLY</font>只能前進 ,資料取得不能作修改
int getRow():取得目前的列編號,若傳回0,表示游標不在任一列上,可能在第一列前或最後一列後
<span class=g>index從1開始</span>,一般來說使用index較有效率
```java
while (rs.next()) {
System.out.print(rs.getRow() + "");
System.out.print("EMPNO = " + rs.getInt(1));
System.out.print("ENAME = " + rs.getString(2) + "\n");
}
```
```java
while (rs.previous()) {//上一列
System.out.print(rs.getRow() + "");
System.out.print(" EMPNO = " + rs.getInt(1));
System.out.print(" ENAME = " + rs.getString(2) + "\n");
}
```
---
```java
rs.absolute(10);//但不能超出範圍(資料的數量),會發生例外
System.out.print(rs.getRow() + "");
System.out.print(" EMPNO = " + rs.getInt(1));
System.out.print(" ENAME = " + rs.getString(2) + "\n");
```
## absolute -->絕對位置

```java
rs.relative(2);
//相對位置 搭配absolute(10) 結果會是10+2
//也可以給負數 ex:relative(-2) 搭配absolute(10) 結果會是10-2
```
## relative --> 相對位置


---
```java
System.out.println("----------------4----------------");
rs.last();
int len = rs.getRow();
System.out.println("資料共" + len + "筆" + " [使用ResultSet游標移動的方式]");
rs.beforeFirst();
//
System.out.println("----------------5----------------'');
ResultSet rs2 = stmt.executeQuery("SELECT count(*) AS count FROM EMPLOYEE");
rs2.next();
int len2 = rs2.getInt("count");
System.out.println("資料共" + len2 + "筆" + " [直接下SQL指令的方式]");
rs2.close();
```
