JDBC的全稱是Java DataBase Connection,也就是Java數據庫鏈接,咱們能夠用它來操做關係型數據庫。JDBC接口及相關類在java.sql包和javax.sql包裏。咱們能夠用它來鏈接數據庫,執行SQL查詢,存儲過程,並處理返回的結果。html
JDBC接口讓Java程序和JDBC驅動實現了鬆耦合,使得切換不一樣的數據庫變得更加簡單。java
有四類JDBC驅動。和數據庫進行交互的Java程序分紅兩個部分,一部分是JDBC的API,實際工做的驅動則是另外一部分。mysql
A JDBC-ODBC Bridge plus ODBC Driver(類型1):它使用ODBC驅動鏈接數據庫。須要安裝ODBC以便鏈接數據庫,正由於這樣,這種方式如今已經基本淘汰了。sql
B Native API partly Java technology-enabled driver(類型2):這種驅動把JDBC調用適配成數據庫的本地接口的調用。數據庫
C Pure Java Driver for Database Middleware(類型3):這個驅動把JDBC調用轉發給中間件服務器,由它去和不一樣的數據庫進行鏈接。用這種類型的驅動須要部署中間件服務器。這種方式增長了額外的網絡調用,致使性能變差,所以不多使用。緩存
D Direct-to-Database Pure Java Driver(類型4):這個驅動把JDBC轉化成數據庫使用的網絡協議。這種方案最簡單,也適合經過網絡鏈接數據庫。不過使用這種方式的話,須要根據不一樣數據庫選用特定的驅動程序,好比OJDBC是Oracle開發的Oracle數據庫的驅動,而MySQL Connector/J是MySQL數據庫的驅動。服務器
JDBC API使用Java的反射機制來實現Java程序和JDBC驅動的鬆耦合。隨便看一個簡單的JDBC示例,你會發現全部操做都是經過JDBC接口完成的,而驅動只有在經過Class.forName反射機制來加載的時候纔會出現。網絡
我以爲這是Java核心庫裏反射機制的最佳實踐之一,它使得應用程序和驅動程序之間進行了隔離,讓遷移數據庫的工做變得更簡單。在這裏能夠看到更多JDBC的使用示例。併發
JDBC鏈接是和數據庫服務器創建的一個會話。你能夠想像成是一個和數據庫的Socket鏈接。oracle
建立JDBC鏈接很簡單,只須要兩步:
A. 註冊並加載驅動:使用Class.forName(),驅動類就會註冊到DriverManager裏面並加載到內存裏。 B. 用DriverManager獲取鏈接對象:調用DriverManager.getConnnection()方法並傳入數據庫鏈接的URL,用戶名及密碼,就能獲取到鏈接對象。
Connection con = null; try{ // load the Driver Class Class.forName("com.mysql.jdbc.Driver"); // create the connection now con = DriverManager.getConnection("jdbc:mysql://localhost:3306/UserDB", "pankaj", "pankaj123"); }catch (SQLException e) { System.out.println("Check database is UP and configs are correct"); e.printStackTrace(); }catch (ClassNotFoundException e) { System.out.println("Please include JDBC MySQL jar in classpath"); e.printStackTrace(); }
JDBC的DriverManager是一個工廠類,咱們經過它來建立數據庫鏈接。當JDBC的Driver類被加載進來時,它會本身註冊到DriverManager類裏面,你能夠看下JDBC Driver類的源碼來了解一下。
而後咱們會把數據庫配置信息傳成DriverManager.getConnection()方法,DriverManager會使用註冊到它裏面的驅動來獲取數據庫鏈接,並返回給調用的程序。
使用DatabaseMetaData能夠獲取到服務器的信息。當和數據庫的鏈接成功創建了以後,能夠經過調用getMetaData()方法來獲取數據庫的元信息。DatabaseMetaData裏面有不少方法,經過它們能夠獲取到數據庫的產品名稱,版本號,配置信息等。
DatabaseMetaData metaData = con.getMetaData(); String dbProduct = metaData.getDatabaseProductName();
Statement是JDBC中用來執行數據庫SQL查詢語句的接口。經過調用鏈接對象的getStatement()方法咱們能夠生成一個Statement對象。咱們能夠經過調用它的execute(),executeQuery(),executeUpdate()方法來執行靜態SQL查詢。
因爲SQL語句是程序中傳入的,若是沒有對用戶輸入進行校驗的話可能會引發SQL注入的問題,若是想了解更多關於SQL注入的,能夠看下這裏。
默認狀況下,一個Statement同時只能打開一個ResultSet。若是想操做多個ResultSet對象的話,須要建立多個Statement。Statement接口的全部execute方法開始執行時都默認會關閉當前打開的ResultSet。
Statement的execute(String query)方法用來執行任意的SQL查詢,若是查詢的結果是一個ResultSet,這個方法就返回true。若是結果不是ResultSet,好比insert或者update查詢,它就會返回false。咱們能夠經過它的getResultSet方法來獲取ResultSet,或者經過getUpdateCount()方法來獲取更新的記錄條數。
Statement的executeQuery(String query)接口用來執行select查詢,而且返回ResultSet。即便查詢不到記錄返回的ResultSet也不會爲null。咱們一般使用executeQuery來執行查詢語句,這樣的話若是傳進來的是insert或者update語句的話,它會拋出錯誤信息爲 「executeQuery method can not be used for update」的java.util.SQLException。
Statement的executeUpdate(String query)方法用來執行insert或者update/delete(DML)語句,或者 什麼也不返回DDL語句。返回值是int類型,若是是DML語句的話,它就是更新的條數,若是是DDL的話,就返回0。
只有當你不肯定是什麼語句的時候才應該使用execute()方法,不然應該使用executeQuery或者executeUpdate方法。
PreparedStatement對象表明的是一個預編譯的SQL語句。用它提供的setter方法能夠傳入查詢的變量。
因爲PreparedStatement是預編譯的,經過它能夠將對應的SQL語句高效的執行屢次。因爲PreparedStatement自動對特殊字符轉義,避免了SQL注入攻擊,所以應當儘可能的使用它。
可使用它的setNull方法來把null值綁定到指定的變量上。setNull方法須要傳入參數的索引以及SQL字段的類型,像這樣:
ps.setNull(10, java.sql.Types.INTEGER);.
有的時候表會生成主鍵,這時候就能夠用Statement的getGeneratedKeys()方法來獲取這個自動生成的主鍵的值了。
它和Statement相比優勢在於:
PreparedStatement的一個缺點是,咱們不能直接用它來執行in條件語句;須要執行IN條件語句的話,下面有一些解決方案:
在查詢數據庫後會返回一個ResultSet,它就像是查詢結果集的一張數據表。
ResultSet對象維護了一個遊標,指向當前的數據行。開始的時候這個遊標指向的是第一行。若是調用了ResultSet的next()方法遊標會下移一行,若是沒有更多的數據了,next()方法會返回false。能夠在for循環中用它來遍歷數據集。
默認的ResultSet是不能更新的,遊標也只能往下移。也就是說你只能從第一行到最後一行遍歷一遍。不過也能夠建立能夠回滾或者可更新的ResultSet,像下面這樣。
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
當生成ResultSet的Statement對象要關閉或者從新執行或是獲取下一個ResultSet的時候,ResultSet對象也會自動關閉。
能夠經過ResultSet的getter方法,傳入列名或者從1開始的序號來獲取列數據。
根據建立Statement時輸入參數的不一樣,會對應不一樣類型的ResultSet。若是你看下Connection的方法,你會發現createStatement和prepareStatement方法重載了,以支持不一樣的ResultSet和併發類型。
一共有三種ResultSet對象。
ResultSet有兩種併發類型。
setMaxRows能夠用來限制返回的數據集的行數。固然經過SQL語句也能夠實現這個功能。好比在MySQL中咱們能夠用LIMIT條件來設置返回結果的最大行數。
setFetchSize理解起來就有點費勁了,由於你得知道Statement和ResultSet是怎麼工做的。當數據庫在執行一條查詢語句時,查詢到的數據是在數據庫的緩存中維護的。ResultSet其實引用的是數據庫中緩存的結果。
假設咱們有一條查詢返回了100行數據,咱們把fetchSize設置成了10,那麼數據庫驅動每次只會取10條數據,也就是說得取10次。當每條數據須要處理的時間比較長的時候而且返回數據又很是多的時候,這個可選的參數就變得很是有用了。
咱們能夠經過Statement來設置fetchSize參數,不過它會被ResultSet對象設置進來的值所覆蓋掉。
存儲過程就是數據庫編譯好的一組SQL語句,能夠經過JDBC接口來進行調用。咱們能夠經過JDBC的CallableStatement接口來在數據庫中執行存儲過程。初始化CallableStatement的語法是這樣的:
CallableStatement stmt = con.prepareCall("{call insertEmployee(?,?,?,?,?,?)}"); stmt.setInt(1, id); stmt.setString(2, name); stmt.setString(3, role); stmt.setString(4, city); stmt.setString(5, country); //register the OUT parameter before calling the stored procedure stmt.registerOutParameter(6, java.sql.Types.VARCHAR); stmt.executeUpdate();
咱們得在執行CallableStatement以前註冊OUT參數。關於這個更詳細的資料能夠看這裏。
有時候相似的查詢咱們須要執行不少遍,好比從CSV文件中加載數據到關係型數據庫的表裏。咱們也知道,執行查詢能夠用Statement或者PreparedStatement。除此以外,JDBC還提供了批處理的特性,有了它,咱們能夠在一次數據庫調用中執行多條查詢語句。
JDBC經過Statement和PreparedStatement中的addBatch和executeBatch方法來支持批處理。
批處理比一條條語句執行的速度要快得多,由於它須要不多的數據庫調用,想進一步瞭解請點這裏。
默認狀況下,咱們建立的數據庫鏈接,是工做在自動提交的模式下的。這意味着只要咱們執行完一條查詢語句,就會自動進行提交。所以咱們的每條查詢,實際上都是一個事務,若是咱們執行的是DML或者DDL,每條語句完成的時候,數據庫就已經完成修改了。
有的時候咱們但願由一組SQL查詢組成一個事務,若是它們都執行OK咱們再進行提交,若是中途出現異常了,咱們能夠進行回滾。
JDBC接口提供了一個setAutoCommit(boolean flag)方法,咱們能夠用它來關閉鏈接自動提交的特性。咱們應該在須要手動提交時才關閉這個特性,否則的話事務不會自動提交,每次都得手動提交。數據庫經過表鎖來管理事務,這個操做很是消耗資源。所以咱們應當完成操做後儘快的提交事務。在這裏有更多關於事務的示例程序。
經過Connection對象的rollback方法能夠回滾事務。它會回滾此次事務中的全部修改操做,並釋放當前鏈接所持有的數據庫鎖。
轉自:http://it.deepinmind.com/jdbc/2014/03/18/JDBC%E5%B8%B8%E8%A7%81%E9%9D%A2%E8%AF%95%E9%A2%98%E9%9B%86%E9%94%A6%28%E4%B8%80%29.html