Java-JDBC學(xué)習(xí)教程-由淺入深
《Java-JDBC學(xué)習(xí)教程-由淺入深》由會員分享,可在線閱讀,更多相關(guān)《Java-JDBC學(xué)習(xí)教程-由淺入深(98頁珍藏版)》請在裝配圖網(wǎng)上搜索。
目錄 第一節(jié) 整理目的 3 第二節(jié) jdbc的概念 3 2.1概念 3 2.2 Jdbc與應(yīng)用程序的關(guān)系 3 2.3 數(shù)據(jù)庫的連接步驟 4 2.4 Quick Start 4 第三節(jié) 如何與數(shù)據(jù)庫建立連接 6 3.1 注冊驅(qū)動 6 3.2 建立數(shù)據(jù)庫的連接 8 3.3 規(guī)范Quick Start中的例子 10 第四節(jié) Statement 接口的使用詳解 12 4.1 Statement 的常用方法 12 4.2 CRUD操作 16 4.3 Statement有那些缺點(diǎn) 19 第五節(jié) ResultSet接口的使用詳解 20 第六節(jié) JDBC 中數(shù)據(jù)類型詳解 30 6.1 基本數(shù)據(jù)類型 30 6.2 日期類型 34 6.3 CLOB類型 36 6.4 BLOB類型 39 6.5 其他數(shù)據(jù)類型 41 第七節(jié) DAO設(shè)計模式詳解 41 7.1 實際項目中如何使用JDBC 41 7.2 DAO設(shè)計模式簡介 42 7.3 DAO設(shè)計模式的實現(xiàn) 42 7.4 DAO設(shè)計模式與工廠模式的整合 49 7.5 DAO設(shè)計模式測試 52 第八節(jié) JDBC對事務(wù)的支持 52 8.1 模擬轉(zhuǎn)賬 53 8.2 jdbc默認(rèn)事務(wù) 54 8.3 事務(wù)提交與回滾 54 8.4 設(shè)置保存點(diǎn) 55 8.5 JTA事務(wù)的介紹 56 8.6 數(shù)據(jù)庫的隔離級別介紹 56 8.6.1 未提交讀 57 8.6.2 提交讀 58 8.6.3 重復(fù)讀 59 8.6.4 序列化讀 60 8.7 小結(jié) 62 第九節(jié) PreparedStatement接口的使用 62 第十節(jié) CallableStatement接口的使用 62 9.1 無參無返回值存儲過程調(diào)用 63 9.2 有參無返回值存儲過程調(diào)用 63 9.3 有參有返回值存儲過程調(diào)用 64 9.4 JDBC其他API 65 第十一節(jié) 元數(shù)據(jù)信息 66 11.1 數(shù)據(jù)庫元數(shù)據(jù)信息 66 11.2 參數(shù)元數(shù)據(jù)信息 67 第十二節(jié) 批處理的使用 67 12.1 普通方式插入一千條數(shù)據(jù) 68 12.2 批處理方式插入一千條數(shù)據(jù) 69 第十三節(jié) JDBC其他API 70 13.1 可滾動結(jié)果集 70 13.2 分頁技術(shù) 72 13.3 可更新結(jié)果集 73 第十四節(jié) 編寫一個簡單的數(shù)據(jù)庫連接池 74 14.1 為什么要使用數(shù)據(jù)庫連接池 74 14.2 數(shù)據(jù)庫連接池雛形 74 14.2 數(shù)據(jù)庫連接池優(yōu)化 77 14.2.1 對線程池加鎖 77 14.2.2 連接不夠用時拋出異常 77 14.3 數(shù)據(jù)庫連接池之代理模式 78 14.3.1 靜態(tài)代理 78 14.3.2 動態(tài)代理 84 14.4 DBCP數(shù)據(jù)庫連接池的使用 87 第十五節(jié) jdbc輕量級封裝 88 15.1 將結(jié)果集封裝為Map 88 15.1.1 ResultSetMetaData演示 88 15.1.2解決多行記錄的問題 89 15.1.3 Map結(jié)果集的封裝 90 15.2 將結(jié)果集封裝為對象 91 15.2.1 user表POJO的編寫 91 15.2.2 Bean結(jié)果集的封裝 92 15.3 將結(jié)果集封裝為List 94 15.4 策略模式的應(yīng)用 96 15.4.1 Map結(jié)果集策略模式應(yīng)用 96 15.4.2 Bean結(jié)果集策略模式應(yīng)用 97 15.4.3 List結(jié)果集策略模式應(yīng)用 98 15.4.4 單元測試 99 15.5 模板模式的應(yīng)用 100 第十六節(jié) 近期推出 101 第一節(jié) 整理目的 當(dāng)今orm等全自動針對對象持久化的框架越來越多并且也越來越成熟(ibatis,hibernate,ejb的jpa),但是無奈新東家需要使用jdbc(原始手工作坊)的模式和數(shù)據(jù)庫打交道,用了幾年的ibatis,再次使用jdbc發(fā)現(xiàn)有些細(xì)節(jié)和底層的東西自己并不是十分清楚,所以就啰理啰嗦的整理出一份學(xué)習(xí)筆記,第一作為自己對jdbc重新的復(fù)習(xí),第二如果有可能希望給初學(xué)jdbc的朋友帶來一定的便利,這樣也不枉我點(diǎn)點(diǎn)滴滴的記錄。 隨著對jdbc整理和學(xué)習(xí)的逐漸深入,發(fā)現(xiàn)原先使用orm框架時忽略了那么多的細(xì)節(jié),這樣在出現(xiàn)問題或者學(xué)習(xí)orm更加深入知識時則會顯得力不從心,在本文檔將jdbc如何入門闡述清楚之后,增加了如下的內(nèi)容: 數(shù)據(jù)庫連接池,以及常用連接池的使用(dbcp,c3p0等) 編寫一套基于jdbc輕量級的api,方便使用; 如何將查詢結(jié)果封裝為對象; 如何將查詢結(jié)果封裝為Map; 如何將查詢結(jié)果封裝為List; 如何在JDBC的使用中加入策略,模板等模式; 在后面的JDBC高級部分將會講解到Dbutils源碼,Spring對JDBC的強(qiáng)大封裝 第二節(jié) jdbc的概念 2.1概念 我最不喜歡替別人整理某個名詞的概念了,只要是概念性的東西基本上在任何地方都可以查得到,所以我就通俗的寫一些自己對jdbc的理解,所謂jdbc就是java與數(shù)據(jù)庫之間進(jìn)行通訊的api,也就是一個標(biāo)準(zhǔn),所以如果一個java應(yīng)用程序想要和數(shù)據(jù)庫打交道基本上都離不開jdbc,眾所周知,一些優(yōu)秀的orm框架的底層也是采用jdbc進(jìn)行封裝的。 2.2 Jdbc與應(yīng)用程序的關(guān)系 JdbcAPI所處的位置和它與應(yīng)用程序之間的關(guān)系,下面的一張圖再也明顯不過了,其中綠色的部分代表jdbcAPI,它提供了很多接口,并且本身也實現(xiàn)了很多方法,可以看到藍(lán)色的部分就是各個數(shù)據(jù)庫廠商自己對jdbcAPI的一些實現(xiàn),這就是我們常見的數(shù)據(jù)庫連接驅(qū)動,這是使用jdbc程序進(jìn)行開發(fā)必不可少的東西。 2.3 數(shù)據(jù)庫的連接步驟 1. 注冊驅(qū)動 (Driver) 2. 建立連接(創(chuàng)建Connection) 3. 創(chuàng)建執(zhí)行sql語句(通常是創(chuàng)建Statement或者其子類) 4. 執(zhí)行語句 5. 處理執(zhí)行結(jié)果(在非查詢語句中,該步驟是可以省略的) 6. 釋放相關(guān)資源 在后文中,將會對上述幾個步驟一一進(jìn)行講解,希望讀者能夠仔細(xì)閱讀; 2.4 Quick Start 好了,了解了一下jdbc的基本概念,相比對jdbc已經(jīng)有了一個感性的認(rèn)識,現(xiàn)在我們?yōu)榱酥庇^期間,直接來上一段代碼了解一下jdbc最簡單的程序如何進(jìn)行開發(fā)的。 在該小節(jié)中,我們以一個簡單的增刪改查為例進(jìn)行說明,然后會將該章節(jié)中涉及的各個常用以及關(guān)鍵的API進(jìn)行詳細(xì)的講解; 首先我們創(chuàng)建一個數(shù)據(jù)表,在test數(shù)據(jù)庫下,見表語句為 create table user(id integer primary key, name varchar(30) , birthday date, money float); 插入兩條語句 l insert into user values(2,zhangsan,2010-01-01,15000); l insert into user values(1,wangwenjun,1984-06-09,8500.00); 好了,數(shù)據(jù)準(zhǔn)備好了,我們通過一個完整的例子講上述中數(shù)據(jù)庫的連接步驟進(jìn)行一個演示,在本例子中,初學(xué)者可能有些地方會覺得陌生,看不明白,不用著急,在后文中會對涉及的知識點(diǎn)逐個進(jìn)行講解 @Test public void wholeExample(){ try { //1.注冊驅(qū)動 Class.forName("com.mysql.jdbc.Driver"); //2.獲取數(shù)據(jù)庫連接 Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","r66t"); //3.創(chuàng)建執(zhí)行句柄 Statement stmt = conn.createStatement(); //4.執(zhí)行sql語句 ResultSet rs = stmt.executeQuery("select * from user"); //5.處理執(zhí)行結(jié)果 while(rs.next()){ System.out.println("id:"+rs.getInt(1)+"\tname:"+rs.getString(2)+"\tbirthday:"+rs.getDate(3)+"\tmoney:"+rs.getFloat(4)); } //6.釋放資源 rs.close(); stmt.close(); conn.close(); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } 執(zhí)行結(jié)果如下 id:1 name:wangwenjun birthday:1984-06-09 money:8500.0 id:2 name:zhangsan birthday:2010-01-01 money:15000.0 第三節(jié) 如何與數(shù)據(jù)庫建立連接 3.1 注冊驅(qū)動 l 第一種注冊方式 通常來說,注冊驅(qū)動的方式有三種,下面我們將一一進(jìn)行介紹,首先來看看直接調(diào)用DriverManager的registerDriver方法進(jìn)行加載驅(qū)動,在本文中所有的程序均是在mysql數(shù)據(jù)庫上進(jìn)行演示的。 示例代碼如下 @Test public void registDriver1(){ try { DriverManager.registerDriver(new com.mysql.jdbc.Driver()); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","r66t"); Assert.assertEquals(false, conn.isClosed()); } catch (SQLException e) { e.printStackTrace(); } } 執(zhí)行結(jié)果為 可以看到,當(dāng)前我們的程序與數(shù)據(jù)庫的連接是正常的。 l 第二種注冊方式 @Test public void registDriver2(){ try { System.setProperty("jdbc.drivers", "com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","r66t"); Assert.assertEquals(false, conn.isClosed()); } catch (SQLException e) { e.printStackTrace(); } } 執(zhí)行結(jié)果為 可以看到,當(dāng)前我們的程序與數(shù)據(jù)庫的連接是正常的。 l 第三種注冊方式 @Test public void registDriver3(){ try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","r66t"); Assert.assertEquals(false, conn.isClosed()); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } 執(zhí)行結(jié)果為 可以看到,當(dāng)前我們的程序與數(shù)據(jù)庫的連接是正常的。 一般來說注冊驅(qū)動的方式大致上有上述三個,但是最常用的是最后一個,通過我們的代碼演示可以看出,數(shù)據(jù)庫都是完全可以被訪問成功的。 3.2 建立數(shù)據(jù)庫的連接 其實在上文中的代碼演示中,我們都會看到如何獲取一個數(shù)據(jù)庫連接,就是通過DriverManager.getConnection()方法獲取數(shù)據(jù)庫的鏈接,該方法大致有三個重載的方法,都是可以進(jìn)行數(shù)據(jù)庫連接的獲取的,下面我們將會一一進(jìn)行演示 Static Connection getConnection(Stringurl) 試圖建立到給定數(shù)據(jù)庫 URL 的連接。 static Connection getConnection(Stringurl, Propertiesinfo) 試圖建立到給定數(shù)據(jù)庫 URL 的連接。 static Connection getConnection(Stringurl,String user, Stringpassword) 試圖建立到給定數(shù)據(jù)庫 URL 的連接。 l getConnection(Stringurl) 該實例中,登錄數(shù)據(jù)庫的所有信息都編寫在url中,實例代碼如下 @Test public void getConn1(){ try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?user=root&password=r66t"); Assert.assertEquals(false, conn.isClosed()); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } l getConnection(Stringurl, Propertiesinfo) 該方法則是將用戶名和密碼的信息存放在一個Properties鍵值對中,示例代碼如下 @Test public void getConn2(){ try { Class.forName("com.mysql.jdbc.Driver"); Properties props = new Properties(); props.put("user", "root"); props.put("password", "r66t"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test",props); Assert.assertEquals(false, conn.isClosed()); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } l getConnection(Stringurl,String user, Stringpassword) 該方法則是我們在上文中演示了很多次的方式,也是最常用的一種方式,在這里再次進(jìn)行一下贅述 @Test public void getConn3(){ try { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","r66t"); Assert.assertEquals(false, conn.isClosed()); } catch (SQLException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } 3.3 規(guī)范Quick Start中的例子 其中,我們在Quick Start中寫了一個較為完整的代碼示例,但是在該代碼中存在很多的問題,我們通過本節(jié)的介紹,一一進(jìn)行規(guī)范和優(yōu)化,并且說明一下優(yōu)化的好處是什么 使用數(shù)據(jù)庫時,涉及數(shù)據(jù)庫的資源都是非常奇缺的,我們在使用的過程中務(wù)必保證我們將使用過的資源釋放,供別人再次使用或者自己下次再次使用,還有,創(chuàng)建數(shù)據(jù)庫連接時可能存在各種各樣的問題導(dǎo)致數(shù)據(jù)庫連接獲取失敗,這個時候你的應(yīng)用應(yīng)該有義務(wù)告知上一層使用者到底出現(xiàn)了什么問題,這樣就需要一個異常傳遞的過程(異常是一個比較復(fù)雜的機(jī)制,筆者在另一篇文章中有詳細(xì)的講解,希望讀者能夠關(guān)注) @Test public void regularWhole() throws Exception{//拋出異常 Connection conn = null; Statement stmt = null; ResultSet rs = null; try { //1.注冊驅(qū)動 Class.forName("com.mysql.jdbc.Driver"); //2.獲取數(shù)據(jù)庫連接 conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root","r66t"); //3.創(chuàng)建執(zhí)行句柄 stmt = conn.createStatement(); //4.執(zhí)行sql語句 rs = stmt.executeQuery("select * from user"); //5.處理執(zhí)行結(jié)果 while(rs.next()){ System.out.println("id:"+rs.getInt(1)+"\tname:"+rs.getString(2)+"\tbirthday:"+rs.getDate(3)+"\tmoney:"+rs.getFloat(4)); } } finally{ try { //6.釋放資源 if(null!=rs){ rs.close(); } if(null!=stmt){ stmt.close(); } if(null!=conn){ conn.close(); } } finally{ if(null!=rs){ rs.close(); } if(null!=stmt){ stmt.close(); } if(null!=conn){ conn.close(); } } } } 在該實例中,可以看出確保了資源的完全釋放,也將異常拋出告知上一層使用者,那塊出現(xiàn)了問題,但是可以看到代碼明顯寫的很羅嗦,而且有很多地方還是值得考究的 其中,注冊驅(qū)動,數(shù)據(jù)庫的驅(qū)動注冊,只需要一次即可,重復(fù)注冊是沒有任何意義的,并且資源的釋放,在每次使用的時候都進(jìn)行資源的釋放(寫資源釋放的代碼)顯得非常羅嗦,所以我們進(jìn)行再一次的一個優(yōu)化,代碼如下 package com.wangwenjun.jdbc; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public final class ConnCreate { static { try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static Connection getConnection(String url, String user, String pwd) { Connection conn = null; try { conn = DriverManager.getConnection(url, user, pwd); } catch (SQLException e) { e.printStackTrace(); } return conn; } public static void close(Connection conn, Statement stmt, ResultSet rs) throws SQLException { if (null != rs) { rs.close(); } if (null != stmt) { stmt.close(); } if (null != conn) { conn.close(); } } } 可以看出,驅(qū)動只會被注冊一次,并且對資源釋放的代碼進(jìn)行了抽取,在以后的使用過程中則會簡單許多,當(dāng)然上述的代碼如果還需要追究問題,肯定還是存在的,在接下來的章節(jié)中我們也會進(jìn)行深入的說明。 第四節(jié) Statement 接口的使用詳解 Statement 是應(yīng)用與數(shù)據(jù)庫打交道最關(guān)鍵的一個接口,該接口包括了我們常用的CRUD操作,還可以設(shè)置抓取策略,比如設(shè)置數(shù)據(jù)庫的游標(biāo)是多少,可以根據(jù)數(shù)據(jù)量進(jìn)行調(diào)優(yōu),也可以進(jìn)行批量處理等,總之,該接口是非常關(guān)鍵的一個接口,包括后文中的預(yù)處理命令接口以及執(zhí)行存儲過程的接口。 4.1 Statement 的常用方法 下面的列表是從jdk API文檔上粘貼出來的,當(dāng)然很多方法我們并不是都能碰到,但是了解一下還是會有好處的 方法摘要 void addBatch(Stringsql) 將給定的 SQL 命令添加到此 Statement 對象的當(dāng)前命令列表中。 void cancel() 如果 DBMS 和驅(qū)動程序都支持中止 SQL 語句,則取消此 Statement 對象。 void clearBatch() 清空此 Statement 對象的當(dāng)前 SQL 命令列表。 void clearWarnings() 清除在此 Statement 對象上報告的所有警告。 void close() 立即釋放此 Statement 對象的數(shù)據(jù)庫和 JDBC 資源,而不是等待該對象自動關(guān)閉時發(fā)生此操作。 boolean execute(Stringsql) 執(zhí)行給定的 SQL 語句,該語句可能返回多個結(jié)果。 boolean execute(Stringsql, intautoGeneratedKeys) 執(zhí)行給定的 SQL 語句(該語句可能返回多個結(jié)果),并通知驅(qū)動程序所有自動生成的鍵都應(yīng)該可用于檢索。 boolean execute(Stringsql, int[]columnIndexes) 執(zhí)行給定的 SQL 語句(該語句可能返回多個結(jié)果),并通知驅(qū)動程序在給定數(shù)組中指示的自動生成的鍵應(yīng)該可用于檢索。 boolean execute(Stringsql, String[]columnNames) 執(zhí)行給定的 SQL 語句(該語句可能返回多個結(jié)果),并通知驅(qū)動程序在給定數(shù)組中指示的自動生成的鍵應(yīng)該可用于檢索。 int[] executeBatch() 將一批命令提交給數(shù)據(jù)庫來執(zhí)行,如果全部命令執(zhí)行成功,則返回更新計數(shù)組成的數(shù)組。 ResultSet executeQuery(Stringsql) 執(zhí)行給定的 SQL 語句,該語句返回單個 ResultSet 對象。 int executeUpdate(Stringsql) 執(zhí)行給定 SQL 語句,該語句可能為 INSERT、UPDATE 或 DELETE 語句,或者不返回任何內(nèi)容的 SQL 語句(如 SQL DDL 語句)。 int executeUpdate(Stringsql, intautoGeneratedKeys) 執(zhí)行給定的 SQL 語句,并用給定標(biāo)志通知驅(qū)動程序由此 Statement 生成的自動生成鍵是否可用于檢索。 int executeUpdate(Stringsql, int[]columnIndexes) 執(zhí)行給定的 SQL 語句,并通知驅(qū)動程序在給定數(shù)組中指示的自動生成的鍵應(yīng)該可用于檢索。 int executeUpdate(Stringsql, String[]columnNames) 執(zhí)行給定的 SQL 語句,并通知驅(qū)動程序在給定數(shù)組中指示的自動生成的鍵應(yīng)該可用于檢索。 Connection getConnection() 檢索生成此 Statement 對象的 Connection 對象。 int getFetchDirection() 檢索從數(shù)據(jù)庫表獲取行的方向,該方向是根據(jù)此 Statement 對象生成的結(jié)果集合的默認(rèn)值。 int getFetchSize() 檢索結(jié)果集合的行數(shù),該數(shù)是根據(jù)此 Statement 對象生成的 ResultSet 對象的默認(rèn)獲取大小。 ResultSet getGeneratedKeys() 檢索由于執(zhí)行此 Statement 對象而創(chuàng)建的所有自動生成的鍵。 int getMaxFieldSize() 檢索可以為此 Statement 對象所生成 ResultSet 對象中的字符和二進(jìn)制列值返回的最大字節(jié)數(shù)。 int getMaxRows() 檢索由此 Statement 對象生成的 ResultSet 對象可以包含的最大行數(shù)。 boolean getMoreResults() 移動到此 Statement 對象的下一個結(jié)果,如果其為 ResultSet 對象,則返回 true,并隱式關(guān)閉利用方法 getResultSet 獲取的所有當(dāng)前 ResultSet 對象。 boolean getMoreResults(intcurrent) 將此 Statement 對象移動到下一個結(jié)果,根據(jù)給定標(biāo)志指定的指令處理所有當(dāng)前 ResultSet 對象;如果下一個結(jié)果為 ResultSet 對象,則返回 true。 int getQueryTimeout() 檢索驅(qū)動程序等待 Statement 對象執(zhí)行的秒數(shù)。 ResultSet getResultSet() 以 ResultSet 對象的形式檢索當(dāng)前結(jié)果。 int getResultSetConcurrency() 檢索此 Statement 對象生成的 ResultSet 對象的結(jié)果集合并發(fā)性。 int getResultSetHoldability() 檢索此 Statement 對象生成的 ResultSet 對象的結(jié)果集合可保存性。 int getResultSetType() 檢索此 Statement 對象生成的 ResultSet 對象的結(jié)果集合類型。 int getUpdateCount() 以更新計數(shù)的形式檢索當(dāng)前結(jié)果;如果結(jié)果為 ResultSet 對象或沒有更多結(jié)果,則返回 -1。 SQLWarning getWarnings() 檢索此 Statement 對象上的調(diào)用報告的第一個警告。 void setCursorName(Stringname) 將 SQL 指針名稱設(shè)置為給定的 String,后續(xù) Statement 對象的 execute 方法將使用此字符串。 void setEscapeProcessing(booleanenable) 將轉(zhuǎn)義處理設(shè)置為開或關(guān)。 void setFetchDirection(intdirection) 向驅(qū)動程序提供關(guān)于方向的提示,在使用此 Statement 對象創(chuàng)建的 ResultSet 對象中將按該方向處理行。 void setFetchSize(introws) 為 JDBC 驅(qū)動程序提供關(guān)于需要更多行時應(yīng)該從數(shù)據(jù)庫獲取的行數(shù)的提示。 void setMaxFieldSize(intmax) 設(shè)置將字符或二進(jìn)制值存儲到給定字節(jié)數(shù)中 ResultSet 列中的最大字節(jié)數(shù)限制。 void setMaxRows(intmax) 將任何 ResultSet 對象都可以包含的最大行數(shù)限制設(shè)置為給定數(shù)。 void setQueryTimeout(intseconds) 將驅(qū)動程序等待 Statement 對象執(zhí)行的秒數(shù)設(shè)置為給定秒數(shù)。 其中API 上特別注明了一句話,是非常關(guān)鍵的,我們在使用的時候一定要注意,否則會出現(xiàn)很嚴(yán)重的問題 在默認(rèn)情況下,同一時間每個 Statement 對象在只能打開一個 ResultSet 對象。因此,如果讀取一個 ResultSet 對象與讀取另一個交叉,則這兩個對象必須是由不同的 Statement 對象生成的。如果存在某個語句的打開的當(dāng)前 ResultSet 對象,則 Statement 接口中的所有執(zhí)行方法都會隱式關(guān)閉它。 其實從Statement的原理來說,底層他還是從過游標(biāo)的方式操作數(shù)據(jù),尤其是進(jìn)行查詢的時候,并且還是顯式游標(biāo),如果對其不能進(jìn)行及時的資源釋放,當(dāng)運(yùn)行到一定時間,數(shù)據(jù)庫則會拋出異常給應(yīng)用(打開的游標(biāo)超過了最大值) 4.2 CRUD操作 我們還是通過上述創(chuàng)建的user表進(jìn)行一下增刪改查的操作,來看看通過Statement怎樣進(jìn)行數(shù)據(jù)的操作。 l 新增數(shù)據(jù) 現(xiàn)在想到數(shù)據(jù)庫中新增一條數(shù)據(jù),編號為3,名稱為lisi,生日為2010-05-05,money為13000.00,代碼示例如下 @Test public void insert() throws SQLException{ Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 1.注冊驅(qū)動 //Class.forName("com.mysql.jdbc.Driver"); // 2.獲取數(shù)據(jù)庫連接 conn = ConnCreate.getConnection("jdbc:mysql://localhost:3306/test", "root", "r66t"); // 3.創(chuàng)建執(zhí)行句柄 stmt = conn.createStatement(); // 4.執(zhí)行sql語句 String sql="insert into user(id,name,birthday,money) values(3,lisi,2010-05-05,13000.00)"; stmt.executeUpdate(sql); } finally { ConnCreate.close(conn,stmt,rs); } } 執(zhí)行結(jié)果對比為(下圖為,sql執(zhí)行前后的查詢展示) l 修改數(shù)據(jù) 物價上漲,待遇一直不漲,所以我們將lisi的money給再加上5000元,當(dāng)然這只是一個演示,意思就是說更新一下數(shù)據(jù)表中的數(shù)據(jù),如果真的可以這么做,大家可以隨時Q我,給增加工資,呵呵,好了,直接上演示代碼 @Test public void update() throws SQLException{ Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 1.注冊驅(qū)動 //Class.forName("com.mysql.jdbc.Driver"); // 2.獲取數(shù)據(jù)庫連接 conn = ConnCreate.getConnection("jdbc:mysql://localhost:3306/test", "root", "r66t"); // 3.創(chuàng)建執(zhí)行句柄 stmt = conn.createStatement(); // 4.執(zhí)行sql語句 String sql="update user set money=money+5000 where id=3"; stmt.executeUpdate(sql); } finally { ConnCreate.close(conn,stmt,rs); } } 執(zhí)行結(jié)果為 可以看出,lisi的money字段被更新了 l 刪除數(shù)據(jù) 剛才將lisi的money給上調(diào)了,結(jié)果被領(lǐng)導(dǎo)發(fā)現(xiàn)了,現(xiàn)在要求將lisi給開除了,所以我們只好進(jìn)行刪除操作了(所以說敏感數(shù)據(jù)不能隨便修改噠),示例代碼如下 @Test public void delete() throws SQLException{ Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 1.注冊驅(qū)動 //Class.forName("com.mysql.jdbc.Driver"); // 2.獲取數(shù)據(jù)庫連接 conn = ConnCreate.getConnection("jdbc:mysql://localhost:3306/test", "root", "r66t"); // 3.創(chuàng)建執(zhí)行句柄 stmt = conn.createStatement(); // 4.執(zhí)行sql語句 String sql="delete from user where id=3"; stmt.executeUpdate(sql); } finally { ConnCreate.close(conn,stmt,rs); } } 執(zhí)行結(jié)果如下圖所示 從上圖可以看出,lisi已經(jīng)被刪除掉了。 l 查詢數(shù)據(jù) 前面的章節(jié)中,關(guān)于查詢的示例實在太多了,此處省略。 4.3 Statement有那些缺點(diǎn) 應(yīng)用執(zhí)行sql其實大體上可以分為兩步,第一步是將sql交給數(shù)據(jù)庫應(yīng)用檢查編譯,然后再由數(shù)據(jù)庫將執(zhí)行結(jié)果返回給應(yīng)用。好了我們看一個實例,來感受一下Statement會有哪些缺點(diǎn) @Test public void conditionQuery() throws SQLException{ Connection conn = null; Statement stmt = null; ResultSet rs = null; try { // 1.注冊驅(qū)動 //Class.forName("com.mysql.jdbc.Driver"); // 2.獲取數(shù)據(jù)庫連接 conn = ConnCreate.getConnection("jdbc:mysql://localhost:3306/test", "root", "r66t"); // 3.創(chuàng)建執(zhí)行句柄 stmt = conn.createStatement(); // 4.執(zhí)行sql語句 String sql="select * from user where name=wangwenjun or 1 or "; System.out.println(sql); rs = stmt.executeQuery(sql); // 5.處理執(zhí)行結(jié)果 while (rs.next()) { System.out.println("id:" + rs.getInt(1) + "\tname:" + rs.getString(2) + "\tbirthday:" + rs.getDate(3) + "\tmoney:" + rs.getFloat(4)); } } finally { ConnCreate.close(conn,stmt,rs); } } 請看一下我們的代碼,其中name的值很詭異,假設(shè),這個值是由前臺用戶傳遞進(jìn)來的,我們就不能保證他到底是什么,當(dāng)然我們也可以通過編寫過濾器來進(jìn)行規(guī)避,但是我們怎么樣通過jdbc來規(guī)避上述的問題呢,首先看看運(yùn)行結(jié)果吧 select * from user where name=wangwenjun or 1 or id:1 name:wangwenjun birthday:1984-06-09 money:8500.0 id:2 name:zhangsan birthday:2010-01-01 money:15000.0 id:3 name:lisi birthday:2010-05-05 money:18000.0 可以看到將所有的語句都查詢出來了。 簡單總結(jié)一下Statement的缺點(diǎn)哈(純屬個人觀點(diǎn),可能有些不太全面) l 執(zhí)行時發(fā)送sql,影響效率. l 同樣的sql,每次都要發(fā)送,不能進(jìn)行有效的緩存,是一種資源的浪費(fèi). l 示例代碼中演示可以看出,為了防止惡意數(shù)據(jù)我們還需要編寫附加的程序(過濾器)帶來不必要的開支. l 拼接sql字符串很容易出現(xiàn)錯誤. 為了解決上述的問題,我們引入一個新的接口PreparedStatement,該接口是Statement的子接口,他們的主要區(qū)別是,在執(zhí)行sql之前首先準(zhǔn)備好sql語句,將其中的條件通過?進(jìn)行占位,還有一個好處就是,同樣的sql會被PreparedStatement有效的緩存,也就是說,數(shù)據(jù)庫會減少校驗的過程,減少消耗,這就是我們常說的預(yù)處理命令方式,在后文中也會涉及到。 第五節(jié) ResultSet接口的使用詳解 從字面意思上來了解,該接口是一個數(shù)據(jù)集合,是從數(shù)據(jù)庫中獲取的數(shù)據(jù)會存放在該集合當(dāng)中,該集合提供了很多種數(shù)據(jù)獲取的方法,詳細(xì)信息,請看下表展示 方法摘要 boolean absolute(introw) 將指針移動到此 ResultSet 對象的給定行編號。 void afterLast() 將指針移動到此 ResultSet 對象的末尾,正好位于最后一行之后。 void beforeFirst() 將指針移動到此 ResultSet 對象的開頭,正好位于第一行之前。 void cancelRowUpdates() 取消對 ResultSet 對象中的當(dāng)前行所作的更新。 void clearWarnings() 清除在此 ResultSet 對象上報告的所有警告。 void close() 立即釋放此 ResultSet 對象的數(shù)據(jù)庫和 JDBC 資源,而不是等待該對象自動關(guān)閉時發(fā)生此操作。 void deleteRow() 從此 ResultSet 對象和底層數(shù)據(jù)庫中刪除當(dāng)前行。 int findColumn(StringcolumnName) 將給定的 ResultSet 列名稱映射到其 ResultSet 列索引。 boolean first() 將指針移動到此 ResultSet 對象的第一行。 Array getArray(inti) 以 Java 編程語言中 Array 對象的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 Array getArray(StringcolName) 以 Java 編程語言中 Array 對象的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 InputStream getAsciiStream(intcolumnIndex) 以 ASCII 字符流的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 InputStream getAsciiStream(StringcolumnName) 以 ASCII 字符流的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 BigDecimal getBigDecimal(intcolumnIndex) 以具有全精度的 java.math.BigDecimal 的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 BigDecimal getBigDecimal(intcolumnIndex, intscale) 已過時。 BigDecimal getBigDecimal(StringcolumnName) 以具有全精度的 java.math.BigDecimal 的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 BigDecimal getBigDecimal(StringcolumnName, intscale) 已過時。 InputStream getBinaryStream(intcolumnIndex) 以未解釋字節(jié)的二進(jìn)制流的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 InputStream getBinaryStream(StringcolumnName) 以未解釋的 byte 流的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 Blob getBlob(inti) 以 Java 編程語言中 Blob 對象的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 Blob getBlob(StringcolName) 以 Java 編程語言中 Blob 對象的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 boolean getBoolean(intcolumnIndex) 以 Java 編程語言中 boolean 的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 boolean getBoolean(StringcolumnName) 以 Java 編程語言中 boolean 的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 byte getByte(intcolumnIndex) 以 Java 編程語言中 byte 的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 byte getByte(StringcolumnName) 以 Java 編程語言中 byte 的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 byte[] getBytes(intcolumnIndex) 以 Java 編程語言中 byte 數(shù)組的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 byte[] getBytes(StringcolumnName) 以 Java 編程語言中 byte 數(shù)組的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 Reader getCharacterStream(intcolumnIndex) 以 java.io.Reader 對象的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 Reader getCharacterStream(StringcolumnName) 以 java.io.Reader 對象的形式檢索此 ResultSet 對象的當(dāng)前行中指定列的值。 Clob getClob(inti) 以 Java- 1.請仔細(xì)閱讀文檔,確保文檔完整性,對于不預(yù)覽、不比對內(nèi)容而直接下載帶來的問題本站不予受理。
- 2.下載的文檔,不會出現(xiàn)我們的網(wǎng)址水印。
- 3、該文檔所得收入(下載+內(nèi)容+預(yù)覽)歸上傳者、原創(chuàng)作者;如果您是本文檔原作者,請點(diǎn)此認(rèn)領(lǐng)!既往收益都?xì)w您。
下載文檔到電腦,查找使用更方便
15 積分
下載 |
- 配套講稿:
如PPT文件的首頁顯示word圖標(biāo),表示該P(yáng)PT已包含配套word講稿。雙擊word圖標(biāo)可打開word文檔。
- 特殊限制:
部分文檔作品中含有的國旗、國徽等圖片,僅作為作品整體效果示例展示,禁止商用。設(shè)計者僅對作品中獨(dú)創(chuàng)性部分享有著作權(quán)。
- 關(guān) 鍵 詞:
- Java JDBC 學(xué)習(xí) 教程 由淺入深
鏈接地址:http://m.appdesigncorp.com/p-10795933.html