【筆記整理】之JDBC復習
阿新 • • 發佈:2019-03-10
結果集 all con jdb 但是 sys callable end 參數
import java.sql.CallableStatement; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.util.Properties;import java.util.UUID; /** * * jdbc復習: * SQL語言共分為四大類: * dcl控制 ddl定義 dql查詢 dml執行(insert update delete) * * JDBC API是java app用來連接數據庫的API接口,可以用來連接、操作語句、接收操作結果. * JDBC客戶端分為:thin瘦客戶端;OCI Oracle call interface 需要安裝Oracle客戶端,調用客戶端dll訪問數據庫 * Oracle 一般裝了數據庫就有jar包 在jdbc/lib下面 * Mysql Connector/Jhttps://dev.mysql.com/downloads/connector/j/ * 這個5.7 和 8版本不一樣 jar包還不一樣. * JDBC調用流程: * java app --> jdbc api --> driveManager --> driver --> database * * 打開一個數據庫連接的方式: * * 第一種: * Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orclwq","smwb_framework","salis"); 第二種、單 URL getConnection模式: Connection con = DriverManager.getConnection("jdbc:oracle:thin:smwb_framework/[email protected]:1521/orclwq"); 第三種: Properties info = new Properties(); info.put("user", "smwb_framework"); info.put("password", "salis"); Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orclwq", info); * * 操作數據庫三個接口: * CallAbleStatement extends PreparedStatement extends Statement * * 編寫過程: * 1、註冊驅動 * 2、加載驅動類 * Class.forName("") 類裏有靜態代碼可以直接 registerDriver() * 而且可以RTTI,運行時動態可配 * 3、創建連接 * 4、執行 * 5、獲取結果 * * * ResultSet:DQL返回的結果集. * 方法說明: * 1、移動型: * next()、previous()、first()、last()、absolute() -->每一行都有一個編號, 1 2 3 -1 -2 -3 負的為從尾開始、 * relative() 相對於當前位置,不是從頭、尾開始、 * getRow() 返回當前行的行號 * * 每移動一次,指針指向下一列 absolute(11)超過num移動到0處(標識哪個指針也不指向) * 2、獲取型: * get 每一個類型都有倆方法,一個是字段名稱 一個是字段index * * 如果是基礎類型數據,如果數據庫是NULL,會附一個值,可以用 wasNull判斷上一個取值的字段是否原始是NULL * 同時基礎類型數據也不允許賦值時候給NULL 報錯! * * 列名無效直接報錯!!! * * 3、更新型: * updateString() 類型 每一個update對應兩種,一個針對字段update 一個針對 index update * 更新到集合列表裏 * updateRow() 更新一行 更新到數據庫 提交! * deleteRow() 直接刪除數據 提交! * refreshRow 去數據庫取最新數據替換當前的ResultRow(rowId匹配上),從數據庫獲取更新,必須有指針才開始 * 沒有調用updateRow的話,refreshRow會替換掉之前的update * 多次調用refreshRow 效率會下降 * cancelRowUpdates -->取消對當前行的更新操作,必須在updateRow 之前操作 * * 光標沒有next時候很多方法都不能使用!!! * * 4、判斷函數 * isFirst()... * 5、插入語句 * insertRow插入數據之前 需要先調用moveToInsertRow方法,移動到插入位置處. * 其實相當於moveToInsertRow之後創建一個空對象,然後對這個空對象進行 * 賦值等更新操作,然後才可以插入 * 6、如果resultSet獲取列的時候getString("xxx", "") 所指定的列不存在 則報錯~~ 沒有好辦法 * * * StateMent初始化與 集合操作類型: * 1、第一個參數決定 移動類型 * TYPE_FORWARD_ONLY * TYPE_SCROLL_INSENSITIVE * TYPE_SCROLL_SENSITIVE * 是否雙向滾動、是否及時更新數據庫操作同步 * * 1、只能向前瀏覽數據 * 2、可以回退、向前,但是對數據更新不敏感 * 3、可以回退、向前,更新數據敏感 * 意思是獲取到一個ResultSet結果集,並不是緩存結果集,而是獲取rowId,每次取數據都是再次執行sql根據rowId去獲取一次最新數據; * 對於新插入的數據,因為沒有rowId所以獲取不到 * 對於delete的數據,因為rowId還在,所以還是可以找到(因為數據庫刪除記錄只是記錄上做一個標記不再被檢索,但原來被緩存的ROWID還在, * 根據它還可以通過數據庫自己的底層操作正確地把數據提取出來) * * 不起動作: * 1、數據庫不支持這個 操作 * 2、setFetchSize 機制 -->不去數據庫 去緩存取 避免內存開銷,一次拉一批回來放進緩存 * * 2、第二個參數決定 讀取類型 * CONCUR_READ_ONLY 只讀 * CONCUR_UPDATABLE 可寫入 * * 初始化導致問題: * 對只轉發結果集的無效操作: first 默認是只能單向next操作的列表集合 * 對只讀結果集的無效操作: updateString * 1、這個一個是驅動版本問題 class12.jar不支持寫 * 2、select * 操作,driver直接禁止寫操作 -->改成 select UserName 具體字段 * * PreparedStateMent: * PreparedStateMent繼承自StateMent,所以很多方法類似; * 區別在於PreparedStateMent執行的SQL是數據庫裏預編譯的,編譯好之後緩存在數據庫裏,之後替換參數執行執行. * 批量執行時候效率比StateMent好,但是初次預編譯時候效率較低. * * 優點: * 1、可讀性更高 * 2、批量效率高 * 3、安全性相對高 --->註入問題 * 因為SQL語句在程序運行前已經進行了預編譯,在程序運行時第一次操作數據庫之前, * SQL語句已經被數據庫分析,編譯和優化,對應的執行計劃也會緩存下來並允許數據庫已參數化的形式進行查詢, * 當運行時動態地把參數傳給PreprareStatement時,即使參數裏有敏感字符如 or ‘1=1‘ * 也數據庫會作為一個參數一個字段的屬性值來處理而不會作為一個SQL指令,如此,就起到了SQL註入的作用了! * * * * CallableStatement --->調用存儲過程 * 存儲過程參數有三類,IN OUT INOUT,後面兩個使用時候需要註冊聲明一下 * * String xx = { call getEmpName (?, ?)} * call = con.prepareCall(xx) * call.setXX(); * * inout 或者 out的都需要使用registerOutParameter方法 * call.registerOutParameter(index, type); * call.execute(); * call.getXX(index); * * 特別註意 ? 替換的索引從 1 開始的!!! * * * 特別異常: * SQLException * * Close關閉順序: * resultSet.close() --> statement.close() --> con.close() * * 事物: * ACID * 事物是一個邏輯單元,一次有始有終的活動, 可以包括一個或多個 DML,一個DDL、一個DCL 或多個DQL * 1、減少請求次數 * 2、保障數據安全、有效 * ACID --->I 隔離 * 見網易雲 事物隔離: * 臟讀:一個事物做了修改,沒提交,另一個事物讀取了沒提交的事物 --->臟讀 * 不可重復讀:兩次查詢結果不一樣 -->更新 刪除 * 幻讀:匯總之後 出現新增數據 * * 第一次丟失更新: * 第二類丟失更新: * * 解決隔離問題加鎖 --->數據庫為我們提供了事務隔離機制,發生響應事物時候自動加鎖限制 * 隔離級別越高,並發越差. * 串行 -->最高級別 讀/寫都加表級鎖 * 讀可以一起讀 但是還是之前版本 * 寫的時候只有我操作 * 讀已提交的 --->Oracle默認 可能A B差不多一起發生,比如買票 看見還3張,先後下單。數據有延緩性 * 開始讀時候才加鎖,讀完釋放鎖 * (不可重復讀問題) * 重復讀 --->讀取時候 加共享鎖,可以一起讀(就算你提交了,也是讀那一瞬間的表現 版本) 但是不能改 * --->更新時候 加排他鎖,不能一起操作一行 * 鎖一直在 * (幻讀問題) * 讀未提交 --->只有提交瞬間加鎖 其他時間可以讀到未提交的. --->各種問題 * 讀時候不限制 不加鎖,可以讀未提交 * * (讀取)操作創建的鎖,其他用戶可以並發讀取數據,但任何事務都不能獲取數據上的排他鎖,直到已釋放所有共享鎖。 共享鎖(S鎖)又稱為讀鎖,若事務T對數據對象A加上S鎖,則事務T只能讀A; 其他事務只能再對A加S鎖,而不能加X鎖,直到T釋放A上的S鎖 這就保證了其他事務可以讀A,但在T釋放A上的S鎖之前不能對A做任何修改 * * 排他鎖(X鎖):如果事務T對數據A加上排他鎖後,則其他事務不能再對A加任任何類型的封鎖 * 獲準排他鎖的事務既能讀數據,又能修改數據。 * * * *@author *** * @date *** */ public class jdbcTest { public static void main(String[] args) throws ClassNotFoundException, SQLException { //加載類 Class.forName("oracle.jdbc.driver.OracleDriver"); //打開連接 Connection con = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orcl", "smwb_framework", "salis"); Connection con2 = DriverManager.getConnection("jdbc:oracle:thin:smwb_framework/[email protected]:1521/orcl"); Properties property = new Properties(); property.put("user", "smwb_framework"); property.put("password", "salis"); Connection con3 = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.66:1521/orcl", property); //執行statement Statement statement = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); statement.setFetchSize(1); //DDL true/false //statement.execute(""); //DML int 影響行數 //statement.executeUpdate("") //DQL 列表 ResultSet resultSet = statement.executeQuery("select ID,AREA from MESSAGE"); while(resultSet.next()) { System.out.println(resultSet.getString("AREA")); //resultSet.relative("") System.out.println(resultSet.getRow()); //resultSet.refreshRow(); } // absolute(11) 超出的話移動到0位置,0位置為未指定 resultSet.absolute(11); System.out.println("absolute_Row " + resultSet.getRow()); resultSet.moveToInsertRow(); System.out.println("Row " + resultSet.getRow()); String uuid = UUID.randomUUID().toString().replace("-", ""); resultSet.updateInt("ID", (int)Math.random()); //resultSet.insertRow(); System.out.println("Row " + resultSet.getRow()); //以下為updateString refreshRow(強制獲取數據庫最新數據) //對只轉發結果集的無效操作: first 默認是只能單向next操作的列表集合 //resultSet.first(); resultSet.absolute(1); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); System.out.println(resultSet.getString("AREA")); resultSet.updateString("AREA", "XYL"); System.out.println(resultSet.getString("AREA")); resultSet.cancelRowUpdates(); System.out.println("cancelRowUpdates " + resultSet.getString("AREA")); resultSet.refreshRow(); System.out.println("refreshRow " + resultSet.getString("AREA")); resultSet.updateString("AREA", "XYL"); resultSet.updateRow(); System.out.println("updateRow " + resultSet.getString("AREA")); resultSet.refreshRow(); System.out.println("refreshRow " + resultSet.getString("AREA")); resultSet.deleteRow(); //預編譯 PreparedStatement preState = con2.prepareStatement("select ID,AREA from MESSAGE where ID = ?"); preState.setInt(1, 10086); ResultSet set2 = preState.executeQuery(); //callabled String callAble = "{call BDC_WORKFLOW.PROC_GETMAXNum(?, ?, ?, ?, ?)}"; CallableStatement call = con3.prepareCall(callAble); call.setInt(1, 20190306); call.setString(2, "330110"); call.setString(3, "0"); call.setString(4, "SSSS"); call.registerOutParameter(5, Types.VARCHAR); call.execute(); System.out.println(call.getString(5)); //close resultSet.close(); statement.close(); call.close(); con.close(); con2.close(); con3.close(); } }
【筆記整理】之JDBC復習