---
# System prepended metadata

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

---

---
tags: JDBC
---
# <font class=o>JDBC2</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>
# <font class=g style="background-color:#abd7;">JDBC Metadata</font>


<span class=p>透過Connection的getMetaData方法取得DatabaseMetaData物件</span>，而DatabaseMetaData可取得資料庫相關資訊，為研發人員用來寫獨立於資料庫的驅動程式和開發工具(mysql workbench)

<span class=p>透過ResultSet的getMetaData方法即可取得ResultSetMetaData物件</span><font class=o>(查詢結果有關的資訊)</font>，也是應用程式開發人員較常用到的metadata，因為可以獲取查詢資料列以外的相關資訊


```java 
//欄位數量
int numberOfColumns = rsmd.getColumnCount();
System.out.println(numberOfColumns);
```

配合欄位的型別對應方法獲得結果


### 1.ResultSetMetaData常用方法：
### 2.int getColumnCount()：取得欄位數
### 3.String getColumnName(int col)：取得欄位名
### 4.String getColumnTypeName(int col)：取得欄位在DBMS所使用的SQL type的名稱
### 5.String getTableName(int col)：取得欄位所屬的表格名稱
### 6.int isNullable(int col)：欄位能否為null

# <font class=g style="background-color:#abd7;">批次更新</font>
    

<font color=gree>批次更新(Batch Update)適用於對資料庫進行大量更新的行為</font>
透過<font color=gree>addBatch()</font>方法收集SQL指令後，將結合成一句SQL指令，再利用executeBatch()
方法送出，其中執行SQL產生的例外為BatchUpdateException
<span class=o>只適用於更新(如新增、修改或刪除)</span>;，對於查詢功能無法支援

```java

Connection con = null;
Statement stat = null;
try {

    Class.forName(Util.DRIVER);
    con = DriverManager.getConnection(Util.URL, Util.USER, Util.PASSWORD);

    long startTime = System.currentTimeMillis();

    stat = con.createStatement();
    for (int i = 1; i <= 5000; i++) {
        stat.addBatch("INSERT INTO EMPLOYEE(EMPNO, ENAME, JOB, HIREDATE, SAL, COMM, DEPTNO)"
                + "VALUES(" + (i + 7015) + ", 'DAVID', 'MANAGER', '2016-01-01', 2500, 0.0, 40)");
    }

    stat.executeBatch();
    long endTime = System.currentTimeMillis();
    System.out.println((endTime - startTime)/1000 +"seconds"); 
		
    
```

stat.addBatch
stat.executeUpdate 一句一句送

Statement //
PreparedStatement //指令裡面參數的資料
# <font class=g style="background-color:#abd7;">JDBC例外處理</font>
 

## SQLException為受檢例外(Checked Exception)
#### <font color=gree>getMessage() – 足以應付大部份情況，訊息是驅動程式在執行產生例外時加入</font>

下面不常用, 各家資料的代表跟狀態對應描述都不一樣
getSQLState()
getErrorCode()
getNextException()

## SQLWarning相關(警告,但不會中止)
<font class=o>主要就是資料截斷的問題發生 --> 拿的資料不完整</font>

### Ex:對於浮點數欄位卻以整數方式取得，雖不至於導致程式發生例外，但因為資料取得的不完整，就是屬於一種警告
![](https://i.imgur.com/eXVz0Ny.png)
# <font class=g style="background-color:#abd7;">BLOB & CLOB</font>


<span class=p>BLOB (Binary Large OBject)</span>讓我們可以將檔案(如圖片、影音資料等)存入資料庫欄位裡，<font class= g>單筆最高可達4GB大小</font>


<span class=p>CLOB (Character Large OBject)</span>與BLOB不同的是，可以用CLOB儲存大量的文字資料，


## <span class =g>範例</br>TestWriteBlob.java 將圖片新增置資料庫<br>TestReadBlob.java 從資料庫讀取圖片</span>

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

# <font class=g style="background-color:#abd7;">BLOB</font>
## <span class = g>setBlob</span>
```java 
private static final String SQL = "INSERT INTO CLUB(ID, NAME, PIC) VALUES (?, ?, ?)";

    public static void main(String[] args) {
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        Class.forName(Util.DRIVER);
        con = DriverManager.getConnection(Util.URL, Util.USER, Util.PASSWORD);
        pstmt = con.prepareStatement(SQL);

        // 1. setBlob (JDBC 4.0) 有版本相容性問題 ,java 6以上才能使用
        pstmt.setInt(1, 1);
        pstmt.setString(2, "拜仁慕尼黑");
        InputStream is = getPictureStream("items/FC_Bayern.png");
        pstmt.setBlob(3, is);
        pstmt.executeUpdate();
        is.close();


        System.out.println("新增成功");

    } catch (ClassNotFoundException ce) {
        System.out.println(ce);
    } catch (SQLException se) {
        System.out.println(se);
    } catch (IOException ie) {
        System.out.println(ie);
    } finally {
        // 依建立順序關閉資源 (越晚建立越早關閉)
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
        }

        if (con != null) {
            try {
                con.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
        }
    }
    }

    // 使用InputStream資料流方式
    public static InputStream getPictureStream(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    return fis;
    }
```

## <span class =g>setBytes</span>

```java 
private static final String SQL = "INSERT INTO CLUB(ID, NAME, PIC) VALUES (?, ?, ?)";

public static void main(String[] args) {
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        Class.forName(Util.DRIVER);
        con = DriverManager.getConnection(Util.URL, Util.USER, Util.PASSWORD);
        pstmt = con.prepareStatement(SQL);
    // 2. setBytes
        pstmt.setInt(1, 2);
        pstmt.setString(2, "巴塞隆納");
        byte[] pic = getPictureByteArray("items/FC_Barcelona.png");
        pstmt.setBytes(3, pic);
        pstmt.executeUpdate();
    System.out.println("新增成功");

    } catch (ClassNotFoundException ce) {
        System.out.println(ce);
    } catch (SQLException se) {
        System.out.println(se);
    } catch (IOException ie) {
        System.out.println(ie);
    } finally {
        // 依建立順序關閉資源 (越晚建立越早關閉)
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
        }

        if (con != null) {
            try {
                con.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
        }
    }
    }

// 使用byte[]方式
    public static byte[] getPictureByteArray(String path) throws IOException {
        FileInputStream fis = new FileInputStream(path);
        byte[] buffer = new byte[fis.available()];
        fis.read(buffer);
        fis.close();
        return buffer;
    }
```

## <span class =g>setBinaryStream</span>
```java 
private static final String SQL = "INSERT INTO CLUB(ID, NAME, PIC) VALUES (?, ?, ?)";

public static void main(String[] args) {
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        Class.forName(Util.DRIVER);
        con = DriverManager.getConnection(Util.URL, Util.USER, Util.PASSWORD);
        pstmt = con.prepareStatement(SQL);

        // 3. setBinaryStream
        pstmt.setInt(1, 3);
        pstmt.setString(2, "皇家馬德里");
        InputStream is2 = getPictureStream("items/FC_Real_Madrid.png");
        pstmt.setBinaryStream(3, is2);
        pstmt.executeUpdate();
        is2.close();

        System.out.println("新增成功");

    } catch (ClassNotFoundException ce) {
        System.out.println(ce);
    } catch (SQLException se) {
        System.out.println(se);
    } catch (IOException ie) {
        System.out.println(ie);
    } finally {
        // 依建立順序關閉資源 (越晚建立越早關閉)
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
        }

        if (con != null) {
            try {
                con.close();
            } catch (SQLException se) {
                System.out.println(se);
            }
        }
    }
}

// 使用InputStream資料流方式
public static InputStream getPictureStream(String path) throws IOException {
    FileInputStream fis = new FileInputStream(path);
    return fis;
}
```

## BLOB處理方式(以圖片為例)
相關處理做法：
<font class =o>getBytes / setBytes</font>
任何java版本都可用, 適合較少量的byte資料(如一般圖片)

<font class =o>getBlob / setBlob</font>
有版本相容性問題(jdbc 4.0/jdk 6以上)才支援

<font class =o>getBinaryStream / setBinaryStream</font>
任何版本都可用 ,適合較大量的byte資料(如影片等)
結合資料流操作，取得byte[ ]資料

```java 
pstmt.setInt(1, 1);
//(1,1) (第1個? , id:1)
byte[] pic = rs2.getBytes(1);
//查詢結果的第一個欄位資料
```


# <font class=g style="background-color:#abd7;">CLOB</font>
## <span class =g>getString</span>

```java 
private static final String SQL = "SELECT INTRO FROM CLUB WHERE ID = ?";

public static void main(String[] args) {
    Connection con = null;
    PreparedStatement pstmt = null;

    try {
        Class.forName(Util.DRIVER);
        con = DriverManager.getConnection(Util.URL, Util.USER, Util.PASSWORD);
        pstmt = con.prepareStatement(SQL);

// 3. getString 最棒
pstmt.setInt(1, 3);
ResultSet rs3 = pstmt.executeQuery();
rs3.next();
System.out.println(rs3.getString(1));
rs3.close();


} catch (ClassNotFoundException ce) {
System.out.println(ce);
} catch (SQLException se) {
System.out.println(se);
//} catch (IOException ie) {
//System.out.println(ie);
} finally {
// 依建立順序關閉資源 (越晚建立越早關閉)
if (pstmt != null) {
    try {
        pstmt.close();
    } catch (SQLException se) {
        System.out.println(se);
    }
}

if (con != null) {
    try {
        con.close();
    } catch (SQLException se) {
        System.out.println(se);
    }
}
}
```

# <font class=g style="background-color:#abd7;">交易 Transaction</font>
### 交易的<font class=o>四個基本要求</font>是
<font class =o>
1.原子性(Atomicity)<br>
2.一致性(Consistency)<br>
3.隔離行為(Isolation behavior)<br>
4.持續性(Durability)
</font>

### 也常簡稱為ACID
---

# <font class=g style="background-color:#abd7;">JDBC與Transactionn</font>

一個Connection物件<span class=g>預設是自動提交(auto-commit)模式</span>

## <font class=k style="background-color:red">Connection介面有關交易的三大方法：</font>
  

### <div class=k style="background-color:darkgray">setAutoCommit(boolean autoCommit)： 設定自動提交模式<font class=g>參數提供'false',改為手動確認</font><br><br>commit()： 結束目前的交易並將所有暫存的資料永久變更至資料庫中<br><br>rollback()： 結束目前的交易並將所有暫存的資料遺棄
</div>

一般會在交易被中斷時呼叫，即發生SQLException時呼叫
<font class=g>在catch裡面呼叫rollback()</font>

---

# <font class=g style="background-color:#abd7;">Save Point</font>
如果在交易管理時，僅想要撒回某個SQL執行點，可以利用<span class=o>設定儲存點(Save Point)</span>的方式進行設計


### <font class=o>進行交易時，除了使用同個連線物件完成之外，連線物件不可與其他使用者共用！</font>
參考訂單與訂單明細實作範例：TestTranscationWithOrder.java
 ![](https://i.imgur.com/sd00vcv.png) 


---
# <font class=g style="background-color:#abd7;">隔離行為 與 同步處理的機制</font>
Connection介面定義了5個常數
int TRANSACTION_NONE
int TRANSACTION_READ_COMMITTED
int TRANSACTION_READ_UNCOMMITTED
int TRANSCATION_REPEATABLE_READ
int TRANSCATION_SERIALIZABLE

)
### <font color = red>void  setTransactionIsolation(int level)
### (小心!! 不要亂用 ,會被罵)</font>
### ，此方法不得於執行交易區間內呼叫


交易問題與可預防隔離行為關係(補充)：
![](https://i.imgur.com/odN7w5u.png)

mysql預設 -> REPEATABLE_READ
Oracle XE預設 -> READ_COMMITTED


<span class=g>TRANSACTION_NONE</span>僅適用沒有交易功能或唯讀功能的資料庫表格；若是表格是有支援交易功能，則會無視此設定
<font class=o>Lost Update 更新遺失</font>
![](https://i.imgur.com/AZx4Mqr.png)

<span class=g>TRANSACTION_READ_UNCOMMITTED</span>
<font class=o>Dirty Read 髒讀</font>
![](https://i.imgur.com/DFukuOe.png)

<span class=g>READ_COMMITTED</span>
<font class=o>Unrepeatable Read 無法重複讀取</font>
![](https://i.imgur.com/w2o15mg.png)

<span class=g>SERIALZABLE</span>
<font class=o>Phantom Read 幻讀 整個表格的讀取前後不一樣</font>

# <font class=g style="background-color:#abd7;">隔離行為 與 同步處理的機制</font>
![](https://i.imgur.com/PPkwOlY.png)
