---
# System prepended metadata

title: <font class=o>JDBC1</font>
tags: [JDBC]

---

---
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)
定義規格
型別轉換
貼標籤
降低相依性
多重繼承

![](https://i.imgur.com/1jKmqdC.png)

![](https://i.imgur.com/TH2OApA.png)
# ==connection 非常重要==
```java
ResultSet 
對應Select指令 ,代表查詢結果

statement 
對應SQL指令的執行

SQLException
一定要處理的例外
```

![](https://i.imgur.com/toYSrW6.png)
# 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&lt;/span&gt;
### ==第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 -&gt;對應到的就是我們自己的類別路徑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為一字串，用以說明連接某一特定資料庫所需要的資訊，包含
    -&gt;資料庫位址<span class=g>(IP)</span>
-&gt;埠號(Port Number ,從0-65535,有65536個 )
潛規則0-1024不使用
-&gt;名稱等

![](https://i.imgur.com/TbtYRZz.png)
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*/
```
![](https://i.imgur.com/RvSSzlO.png)

# Connection
```java 
/*JDBC Api所以要選擇java.sql*/
import java.sql.Connection;
```


```java
//step 2:建立連線
Connection con = DriverManager.getConnection(URL, USER, PASSWORD);
```		

![](https://i.imgur.com/d2rEvza.png)
```java 
/*SQLException例外*/
} catch (SQLException se) {
    se.printStackTrace();
}
```

### <span class=g>Connection非常珍貴，使用完畢後務必完成歸還的動作！</span>


![](https://i.imgur.com/Km9ghKM.png)
![](https://i.imgur.com/RnCWfUZ.png)
![](https://i.imgur.com/rWxmlcm.png)
    
```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");
```
![](https://i.imgur.com/8835Udl.png)
![](https://i.imgur.com/l36qIYD.png)



![](https://i.imgur.com/HalIsfd.png)



```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(?,?,?)");

```

![](https://i.imgur.com/0CPLsC7.png)
無須再傳參數 ,因為指令已經預先交給資料庫


# <span class = g style="background-color:#abd7;"> CallableStatement 介面</span>
    
預設schema
![](https://i.imgur.com/Kkt4rvv.png)

delimiter # ,end # 
最後還是要切換分號
IN -> input 輸入
![](https://i.imgur.com/ZWYC0ER.png)

固定內容
![](https://i.imgur.com/PT6zg3h.png)

![](https://i.imgur.com/qztBo3Q.png)

{call } 
![](https://i.imgur.com/gpSRvwu.png)


<font class=p>CallableStatement prepareCall(String sql)</font>：使用<span class=p>預存程序(stored procedure)</span>，預存程序已事先內建在資料庫中，通常比預先編譯的效能佳

<font color=gree>excute</font> ---> CallableStatement

boolean excute(): 用來執行&#34;未知&#34;的SQL指令,例如預存程序


```java 
cstmt.registerOutParameter(2, Types.DOUBLE);//第2個? 註冊成為輸出的參數 (後面定義型別)
```

# <span class = g style="background-color:#abd7;"> ResultSet進階操作</span>
    
![](https://i.imgur.com/6oP6sKb.png)
## 游標進階操作
### <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 -->絕對位置
![](https://i.imgur.com/ukn3ksq.png)

```java 
rs.relative(2);
//相對位置 搭配absolute(10) 結果會是10+2	   
//也可以給負數 ex:relative(-2) 搭配absolute(10) 結果會是10-2
```
## relative --> 相對位置
![](https://i.imgur.com/gJ6n1nx.png)

![](https://i.imgur.com/ee8CaD3.png)


---

```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();

```
![](https://i.imgur.com/L2HG1Hl.png)

