---
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:對於浮點數欄位卻以整數方式取得,雖不至於導致程式發生例外,但因為資料取得的不完整,就是屬於一種警告

# <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>

# <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

---
# <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>
### ,此方法不得於執行交易區間內呼叫
交易問題與可預防隔離行為關係(補充):

mysql預設 -> REPEATABLE_READ
Oracle XE預設 -> READ_COMMITTED
<span class=g>TRANSACTION_NONE</span>僅適用沒有交易功能或唯讀功能的資料庫表格;若是表格是有支援交易功能,則會無視此設定
<font class=o>Lost Update 更新遺失</font>

<span class=g>TRANSACTION_READ_UNCOMMITTED</span>
<font class=o>Dirty Read 髒讀</font>

<span class=g>READ_COMMITTED</span>
<font class=o>Unrepeatable Read 無法重複讀取</font>

<span class=g>SERIALZABLE</span>
<font class=o>Phantom Read 幻讀 整個表格的讀取前後不一樣</font>
# <font class=g style="background-color:#abd7;">隔離行為 與 同步處理的機制</font>
