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