DriverManager,管理JDBC驅動的服務類,主要通過它獲取Connection數據庫鏈接,常用方法如下:
static synchronized Connection getConnection(String url, String user, String passWord) throws Exception;該方法獲得url對應的數據庫的連接。Connection常用數據庫操作方法:Statement createStatement throws SQLException: 該方法返回一個Statement對象。PReparedStatement prepareStatement(String sql) throws SQLException;該方法返回預編譯的Statement對象,即將SQL語句提交到數據庫進行預編譯。CallableStatement prepareCall(String sql) throws SQLException:該方法返回CallableStatement對象,該對象用于存儲過程的調用。上面的三個方法都是返回執行SQL語句的Statement對象,PreparedStatement、CallableStatement的對象是Statement的子類,只有獲得Statement之后才可以執行SQL語句。 Connection控制事務的方法:Savepoint setSavepoint(): 創建一個保存點Savepoint setSavepoint(String name):創建一個帶有名稱的保存點void setTransactionIsolation(int level):設置事務隔離級別void rollback():回滾事務void rollback(Savepoint savepoint):回滾到指定保存點void setAutoCommit(boolean autoCommit): 關閉自動提交,打開事務void commit():提交事務 Statement,用于執行SQL語句的API接口,該對象可以執行DDL、DCL語句,也可以執行DML語句,還可以執行SQL查詢語句,當執行查詢語句是返回結果集,常用方法如下:ResultSet executeQuery(String sql) throws SQLException:該方法用于執行查詢語句,并返回查詢結果對應的ResultSet對象,該方法只用于查詢語句。int executeUpdate(String sql) throws SQLException:該方法用于執行DML語句,并返回受影響的行數;該方法也可以執行DDL,執行DDL返回0;boolean execute(String sql) throws SQLException:該方法可以執行任何SQL語句,如果執行后第一個結果是ResultSet對象,則返回true;如果執行后第一個結果為受影響的行數或沒有任何結果,則返回false;PreparedStatement,預編譯的statement對象,PreparedStatement是Statement的子接口,它允許數據庫預編譯SQL(通常指帶參數SQL)語句,以后每次只改變SQL命令參數,避免數據庫每次都編譯SQL語句,這樣性能就比較好。而相對于Statement而言,使用PreparedStatement執行SQL語句時,無需重新傳入SQL語句,因為它已經預編譯了SQL語句。但是PreparedStatement需要為編譯的SQL語句傳入參數值,所以它比了如下方法:void setXxx(int index, value)根據該方法傳入的參數值的類型不同,需要使用不同的方法。傳入的值的類型根據傳入的SQL語句參數而定。 ResultSetvoid close() throws SQLException:釋放、關閉ResultSet對象boolean absolute(int row):將結果集移動到第幾行,如果row是負數,則移動到倒數第幾行。如果移動到的記錄指針指向一條有效記錄,則該方法返回true;void beforeFisrt(): 將ResultSet的記錄指針定位到首行之前,這是ResultSet結果集記錄指針的初始狀態:記錄指針的起始位置位于第一行之前。boolean first():將ResultSet的記錄指針定位到首行。如果移動后的記錄指針指向一條有效記錄,則該方法返回true。boolean previous():將ResultSet的記錄指針定位到上一行,如果移動后的記錄指針指向一條有效記錄,則該方法返回true。boolean next():將ResultSet的記錄指針定位到下一行。如果移動后的記錄指針指向一條有效記錄,則返回true。boolean last():將ResultSet的記錄指針定位到最后一行。如果移動后的記錄指針指向一條有效記錄,則返回true。void afterLast():將ResultSet的記錄指針定位到最后一行之后。
2、JDBC驅動JDBC驅動是JDBC API接口的具體實現,不同數據庫的實現細節不同。驅動類型(四種類型),一般優先使用純Java的驅動,已獲得更好的效率。 3、JDBC操作數據庫的一般步驟注冊驅動 (只做一次)建立連接(Connection)創建執行SQL的語句(Statement)執行語句處理執行結果(ResultSet)釋放資源 4、注冊JDBC驅動有三種方式:Class.forName(“com.MySQL.jdbc.Driver”); 推薦這種方式,不會對具體的驅動類產生依賴。DriverManager.registerDriver(com.mysql.jdbc.Driver); 會造成DriverManager中產生兩個一樣的驅動,并會對具體的驅動類產生依賴。System.setProperty(“jdbc.drivers”, “driver1:driver2”); 雖然不會對具體的驅動類產生依賴;但注冊不太方便,所以很少使用。 DriverManager是一個驅動管理器,內部有一個驅動注冊表(Map結構),可以向其注冊多個JDBC驅動。 5、建立ConnectionConnection conn = DriverManager.getConnection(url, user, password);url格式:JDBC:子協議:子名稱//主機名:端口/數據庫名?屬性名=屬性值&…User,password可以用“屬性名=屬性值”方式告訴數據庫;其他參數如:useUnicode=true&characterEncoding=GBK。 6、建立Statement、PreparedStatementStatement是一個SQL執行器,可以用來執行一個靜態的SQL語句。Statement st = conn.createStatement();st.executeQuery(sql); PreparedStatement是一個與定義的SQL執行器,一般較Statement有防止SQL注入的功能,還有較好的執行效率。String sql = "select * from table_name where col_name=?";PreparedStatement ps = conn.preparedStatement(sql);ps.setString(1, "col_value");ps.executeQuery(); 在數據庫連接沒有關閉的情況下,數據庫和驅動可以對PreperedStatement進行優化,PreperedStatement對象可以被重用,從而避免頻繁編譯SQL。 7、處理ResultSetResultSet表示一個查詢結果集。ResultSet rs = statement.executeQuery(sql);While(rs.next()){ rs.getString(“col_name”); rs.getInt(“col_name”); //…} 8、釋放資源釋放資源的順序是ResultSet, Statement,Connection;Connection在使用完成后,必須關閉,ResultSet, Statement無所謂,只要Connection關閉了,它們也會被自動關閉(但資源不是立即被釋放)。Connection的使用原則是盡量晚創建,盡量早的釋放。在關閉資源異常的情況下,應該將資源賦null值,以確保資源最大可能的被釋放掉。二、JDBC編程步驟進行jdbc編程步驟大致如下:1、加載數據庫驅動Class.forName(driverClass)上面的dirverClass就是數據庫驅動類所對應的類路徑字符串,根據不同數據庫廠商提供的驅動也不同。2、通過DriverManager獲取數據庫的鏈接DriverManager.getConnection(String url, Stirng user, String pass)當使用DriverManager來獲取鏈接,需要傳入三個參數:分別是數據量的url、用戶名、密碼。3、通過Connection對象創建Statement對象,Connection創建Statement的方法如下三個:createStatement()創建基本的Statement對象。prepareStatement(String sql):根據傳入的sql語句創建預編譯的Statement對象。prepareCall(String sql):根據傳入的sql語句創建CallableStatement對象4、Statement執行SQL語句,Statement有三大方法來執行SQL語句:execute:可以執行任何SQL語句,單比較麻煩executeUpdate:可以執行DML、DDL語句。執行DML返回受影響的SQL語句行數,執行DDL返回0;executeQuery:只能執行查詢語句,執行后返回代表查詢結果的ResultSet對象。 5、操作結果集,針對ResultSet主要移動指針和獲得值next、previous、first、last、beforeFrist、afterLast、absolute等移動指針的方法。getXxx獲得移動指針指向行,特定列、索引的值。使用列名作為獲取值的參數可讀性好、使用索引作為獲取參數性能好。
6、關閉數據庫連接資源
例子
public class testJdbc { public static void main(String[] args) { Connection con = null; Statement st = null; ResultSet rs = null; try { // 獲得MySQL驅動的實例 Class.forName("com.mysql.jdbc.Driver").newInstance(); // 獲得連接對象(提供:地址,用戶名,密碼) con = DriverManager.getConnection("jdbc:mysql://127.0.0.1/Weather","root", "root"); if (!con.isClosed()) System.out.println("Successfully connected "); else System.out.println("failed connected"); //建立一個Statement,數據庫對象 st = con.createStatement(); // 運行SQL查詢語句 rs = st.executeQuery("select * from Weather.question_type_1;"); // 讀取結果集 while (rs.next()) { System.out.println("column1:"+rs.getInt(1)); System.out.println("column2:"+rs.getString(2)); System.out.println("column3:"+rs.getString(3)); System.out.println("column4:"+rs.getString(4)); } // 關閉鏈接 con.close(); } catch(Exception e) { System.err.println("Exception: " + e.getMessage()); } } }
三、 使用PreparedStatement執行SQL語句
使用PreparedStatement的好處在于他可以預編譯SQL語句,提高sql的執行效率,并且,在其中可以使用帶占位符(?)參數的SQL語句來代替它:insert into student_table values(null,?,?)
實現方式: 創建PreparedStatement對象使用Connection的preparedStatement()方法,該方法傳入一個字符串,該SQL字符串可以包含占位符。pstmt = conn.prepareStatement("insert into student_table values(null,?,1)");
示例:
[java] view plain copy print?package com.chen.yuan.jdbc; import java.io.FileInputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.util.Properties; public class PreparedStatementDemo { private String driver; private String url; private String user; private String pass; /** * 初始化屬性參數 * * @param paramFile * @throws Exception */ public void initParam(String paramFile) throws Exception { Properties props = new Properties(); props.load(new FileInputStream(paramFile)); this.driver = props.getProperty("driver"); this.url = props.getProperty("url"); this.user = props.getProperty("user"); this.pass = props.getProperty("pass"); // 加載數據庫驅動 Class.forName(driver); } /** * 使用PreparedStatement添加數據 * * @throws Exception */ public void insertUsePrepare() throws Exception { long start = System.currentTimeMillis(); Connection conn = null; PreparedStatement pstmt = null; try { conn = DriverManager.getConnection(url, user, pass); pstmt = conn .prepareStatement("insert into student_table values(null,?,1)"); // 向其中插入100條數據 for (int i = 0; i < 100; i++) { pstmt.setString(1, "姓名" + i); pstmt.executeUpdate(); } System.out.println("使用PreparedStatement費時:" + (System.currentTimeMillis() - start)); } catch (Exception e) { e.printStackTrace(); } finally { if (pstmt != null) { pstmt.close(); } if (conn != null) { conn.close(); } } } public static void main(String[] args) throws Exception { PreparedStatementDemo pstmtdemo = new PreparedStatementDemo(); pstmtdemo.initParam("mysql.ini"); pstmtdemo.insertUsePrepare(); } } 使用PreparedStatement除了可以提供sql的執行效率外,還防止SQL注入。我們假設,有如下一段程序,直接使用Statement執行sql,這段程序主要是驗證用戶名和密碼,通過之后,可登陸。
[java] view plain copy print?private boolean validate(String userName, String userPass) { // 執行查詢的SQL語句 String sql = "select * from jdbc_test " + "where jdbc_name='" + userName + "' and jdbc_desc='" + userPass + "'"; System.out.println(sql); try{ Connection conn = DriverManager.getConnection(url , user ,pass); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); // 如果查詢的ResultSet里有超過一條的記錄,則登錄成功 if (rs.next()) { return true; } }catch(Exception e) { e.printStackTrace(); } return false; } 如果,我們而已傳入userName參數,注入sql語句:[sql] view plain copy print?# 利用sql注入后生成的sql語句 select * from jdbc_test where jdbc_name='' or true or '' and jdbc_desc=''; 看到這條語句我們可以發現,該語句總是可以執行通過的,所以用戶可以通過這種sql注入方式侵入一般的系統。那我們現在看一下PreparedStatement如何來處理這種登錄呢: [java] view plain copy print?private boolean validate(String userName, String userPass) { try{ Connection conn = DriverManager.getConnection(url , user ,pass); PreparedStatement pstmt = conn.prepareStatement( "select * from jdbc_test where jdbc_name=? and jdbc_desc=?"); pstmt.setString(1, userName); pstmt.setString(2, userPass); ResultSet rs = pstmt.executeQuery()) //如果查詢的ResultSet里有超過一條的記錄,則登錄成功 if (rs.next()) { return true; } } catch(Exception e) { e.printStackTrace(); } return false; } 總體看來,PreparedStatement預編譯SQL語句,性能更好; 無需拼接sql語句,編程更簡單; PreparedStatement可以防止依賴注入,安全性好。四、 使用CallableStatement調用存儲過程
1) 我們首先需要在MySQL或Oracle數據庫中建立存儲過程。下面以mysql為例:
[sql] view plain copy print?delimiter // create procedure add_pro(a int,b int, out sum int) begin set sum = a+b; end; // 關于如何創建存儲過程,請參看相關的書籍。2) jdbc調用存儲過程,需要使用CallableStatement,可以使用Connection的prepareCall()方法來創建CallableStatement對象,創建該對象時需要傳入調用存儲過程的sql語句。調用存儲過程的格式:{call 過程名(?,?,?.....)},其中?作為存儲過程的占位符。例如,下面創建一個調用上述存儲過程的CallableStatement對象。
cstmt = conn.prepareCall("{call add_pro(?,?,?)}");
存儲過程有傳入參數和傳出參數,所謂的傳入參數就是Java程序必須為這些參數傳入值,可以通過CallableStatement的setXxx()方法為之傳入值;所謂傳出參數就是Java程序可以通過該參數獲取存儲過程里值,CallableStatement需要調用registerOutParameter()方法來注冊該參數。
cstmt.registerOutParameter(3,Types.INTEGER);
示例;
[java] view plain copy print?public class CallableStatementTest { private String driver; private String url; private String user; private String pass; public void initParam(String paramFile)throws Exception { // 使用Properties類來加載屬性文件 Properties props = new Properties(); props.load(new FileInputStream(paramFile)); driver = props.getProperty("driver"); url = props.getProperty("url"); user = props.getProperty("user"); pass = props.getProperty("pass"); } public void callProcedure()throws Exception { // 加載驅動 Class.forName(driver); try( // 獲取數據庫連接 Connection conn = DriverManager.getConnection(url , user , pass); // 使用Connection來創建一個CallableStatment對象 CallableStatement cstmt = conn.prepareCall( "{call add_pro(?,?,?)}")) { cstmt.setInt(1, 4); cstmt.setInt(2, 5); // 注冊CallableStatement的第三個參數是int類型 cstmt.registerOutParameter(3, Types.INTEGER); // 執行存儲過程 cstmt.execute(); // 獲取,并輸出存儲過程傳出參數的值。 System.out.println("執行結果是: " + cstmt.getInt(3)); } } public static void main(String[] args) throws Exception { CallableStatementTest ct = new CallableStatementTest(); ct.initParam("mysql.ini"); ct.callProcedure(); } } 總結:(節摘自http://www.cnblogs.com/hoojo/archive/2011/06/10/2077643.html)
1、 executeUpdate執行DDL、DML語句
Statement提供了execute、executeUpdate、executeQuery三種方法執行,下面用executeUpdate來執行DDL、DML語句,executeUpdate執行DDL返回值是0,執行了DML是返回影響后的記錄條數。
2、 execute執行SQL語句
當我們知道SQL語句是完成修改語句時,我們就知道使用executeUpdate語句來完成操作;如果SQL語句是完成查詢操作的時候,我們就使用executeQuery來完成。如果我們不知道SQL語句完成什么操作的時候,就可以使用execute方法來完成。當我們使用Statement對象的execute方法執行SQL語句后返回的是boolean值,這就說明該語句能否返回ResultSet對象。那么,如何判斷是否是ResultSet對象?方法如下:getResultSet():獲取該Statement執行查詢語句返回的ResultSet對象getUpdateCount():獲取該Statement執行修改語句影響的行數
3、 PrepareStatement執行SQL語句
對于我們操作數據庫的時候,執行某一條SQL語句的時候。只有它的參數不同,而SQL語句相同。我們可以使用占位符來設置我們的參數信息,PrepareStatement中的占位符是?,用?代替參數的位置。insert into table values(?, ‘abc’, ?);占位符僅僅支持PrepareStatement,而Statement不支持占位符。PrepareStatement是預編譯SQL語句的,然后將占位符替換成參數。而Statement就不能做到。 PrepareStatement對象也有execute、executeUpdate、executeQuery這三個方法,但這三個方法都無需傳遞參數。只需用PrepareStatement來設置占位符的參數,通過用setXxxx(index, value)來完成設置參數信息即可。PrepareStatement的效率要比Statement的效率高。PrepareStatement設置參數可以不拼接字符串,而Statement設置參數信息的時候需要手動拼接字符串。拼接字符串容易操作程序錯誤、可讀性降低、維護性升高、程序性能下降。而PrepareStatement直接設置參數信息就降低了編程的復雜度。并且它可以放在SQL注入。因為它是通過setXxx方法進行設置參數信息,而Statement是通過拼接字符串,很容易就造成SQL注入。 綜上所述,PrepareStatement比Statement有以下優點:預編譯SQL語句,性能更好無需拼接SQL語句,編程更簡單可以防止SQL語句注入,安全性更好
4、 CallableStatement調用存儲過程
存儲過程的調用可以通過CallableStatement,通過Connection對象的prepareCall方法來創建CallableStatement對象。然后傳入存儲過程的SQL語句,即可調用存儲過程,格式如下:{call proc_name(?, ?, ?)}上面的?是占位符,表示傳遞的參數。存儲過程有傳入參數、傳出參數。傳入參數是程程序必須傳入的參數,可以 通過setXxx方法進行設置參數值。而傳出參數則需要通過程序進行設置,可以用CallableStatement對象的registerOutParameter方法來注冊輸出參數,cs.registerOutParameter(3, Types.STRING);設置完畢后,當調用存儲過程后要獲取輸出參數值,可以通過getXxx方法來完成。JDBC是Java操作數據庫的技術規范。他實際上定義了一組標準的操作數據庫的接口。為了能讓Java操作數據庫,必須要有實現了JDBC這些接口的類,不同的數據庫廠商為了讓Java語言能操作自己的數據庫,都提供了對JDBC接口的實現--這些實現了JDBC接口的類打成一個jar包,就是我們平時看到的數據庫驅動。由于不同的數據庫操作數據的機制不一樣,因此JDBC的具體實現也就千差萬別,但是你作為java程序員,你只和Java JDBC的接口打交到,才不用理會他們怎么實現的!呵呵,現在知道JDBC驅動是怎么回事了。當然,這些類可以自己去寫--如果你很牛! 1、JDBCJDBC(Java Data Base Connectivity,java數據庫連接),由一些接口和類構成的API。J2SE的一部分,由java.sql,javax.sql包組成。
新聞熱點
疑難解答