JDBC常見面試題集錦(一)

 什麼是JDBC,在什麼時候會用到它?

  JDBC的全稱是Java DataBase Connection,也就是Java數據庫連接,我們可以用它來操作關係型數據庫。JDBC接口及相關類在java.sql包和javax.sql包裏。我們可以用它來連接數據庫,執行SQL查詢,存儲過程,並處理返回的結果。

  JDBC接口讓Java程序和JDBC驅動實現了鬆耦合,使得切換不同的數據庫變得更加簡單。

  有哪些不同類型的JDBC驅動?

  有四類JDBC驅動。和數據庫進行交互的Java程序分成兩個部分,一部分是JDBC的API,實際工作的驅動則是另一部分。

20140319130840736985.PNG

  A JDBC-ODBC Bridge plus ODBC Driver(類型1):它使用ODBC驅動連接數據庫。需要安裝ODBC以便連接數據庫,正因爲這樣,這種方式現在已經基本淘汰了。

  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是如何實現Java程序和JDBC驅動的鬆耦合的?

  JDBC API使用Java的反射機制來實現Java程序和JDBC驅動的鬆耦合。隨便看一個簡單的JDBC示例,你會發現所有操作都是通過JDBC接口完成的,而驅動只有在通過Class.forName反射機制來加載的時候纔會出現。

  我覺得這是Java核心庫裏反射機制的最佳實踐之一,它使得應用程序和驅動程序之間進行了隔離,讓遷移數據庫的工作變得更簡單。在這裏可以看到更多JDBC的使用示例。

  什麼是JDBC連接,在Java中如何創建一個JDBC連接?

  JDBC連接是和數據庫服務器建立的一個會話。你可以想像成是一個和數據庫的Socket連接。

  創建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的DriverManager是一個工廠類,我們通過它來創建數據庫連接。當JDBC的Driver類被加載進來時,它會自己註冊到DriverManager類裏面,你可以看下JDBC Driver類的源碼來了解一下。

  然後我們會把數據庫配置信息傳成DriverManager.getConnection()方法,DriverManager會使用註冊到它裏面的驅動來獲取數據庫連接,並返回給調用的程序。

  在Java程序中,如何獲取數據庫服務器的相關信息?

  使用DatabaseMetaData可以獲取到服務器的信息。當和數據庫的連接成功建立了之後,可以通過調用getMetaData()方法來獲取數據庫的元信息。DatabaseMetaData裏面有很多方法,通過它們可以獲取到數據庫的產品名稱,版本號,配置信息等。

DatabaseMetaData metaData = con.getMetaData();
String dbProduct = metaData.getDatabaseProductName();

  JDBC的Statement是什麼?

  Statement是JDBC中用來執行數據庫SQL查詢語句的接口。通過調用連接對象的getStatement()方法我們可以生成一個Statement對象。我們可以通過調用它的execute(),executeQuery(),executeUpdate()方法來執行靜態SQL查詢。

  由於SQL語句是程序中傳入的,如果沒有對用戶輸入進行校驗的話可能會引起SQL注入的問題,如果想了解更多關於SQL注入的,可以看下這裏

  默認情況下,一個Statement同時只能打開一個ResultSet。如果想操作多個ResultSet對象的話,需要創建多個Statement。Statement接口的所有execute方法開始執行時都默認會關閉當前打開的ResultSet。

  execute,executeQuery,executeUpdate的區別是什麼?

  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方法。

  JDBC的PreparedStatement是什麼?

  PreparedStatement對象代表的是一個預編譯的SQL語句。用它提供的setter方法可以傳入查詢的變量。

  由於PreparedStatement是預編譯的,通過它可以將對應的SQL語句高效的執行多次。由於PreparedStatement自動對特殊字符轉義,避免了SQL注入***,因此應當儘量的使用它。

  PreparedStatement中如何注入NULL值?

  可以使用它的setNull方法來把null值綁定到指定的變量上。setNull方法需要傳入參數的索引以及SQL字段的類型,像這樣:

ps.setNull(10, java.sql.Types.INTEGER);.

  Statement中的getGeneratedKeys方法有什麼用?

  有的時候表會生成主鍵,這時候就可以用Statement的getGeneratedKeys()方法來獲取這個自動生成的主鍵的值了。

  相對於Statement,PreparedStatement的優點是什麼?

  它和Statement相比優點在於:

  • PreparedStatement有助於防止SQL注入,因爲它會自動對特殊字符轉義。

  • PreparedStatement可以用來進行動態查詢。

  • PreparedStatement執行更快。尤其當你重用它或者使用它的拼量查詢接口執行多條語句時。

  • 使用PreparedStatement的setter方法更容易寫出面向對象的代碼,而Statement的話,我們得拼接字符串來生成查詢語句。如果參數太多了,字符串拼接看起來會非常醜陋並且容易出錯。

  PreparedStatement的缺點是什麼,怎麼解決這個問題?

  PreparedStatement的一個缺點是,我們不能直接用它來執行in條件語句;需要執行IN條件語句的話,下面有一些解決方案:

  • 分別進行單條查詢——這樣做性能很差,不推薦。

  • 使用存儲過程——這取決於數據庫的實現,不是所有數據庫都支持。

  • 動態生成PreparedStatement——這是個好辦法,但是不能享受PreparedStatement的緩存帶來的好處了。

  • 在PreparedStatement查詢中使用NULL值——如果你知道輸入變量的最大個數的話,這是個不錯的辦法,擴展一下還可以支持無限參數。

  關於這個問題更詳細的分析可以看下這篇文章

  JDBC的ResultSet是什麼?

  在查詢數據庫後會返回一個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開始的序號來獲取列數據。

  有哪些不同的ResultSet?

  根據創建Statement時輸入參數的不同,會對應不同類型的ResultSet。如果你看下Connection的方法,你會發現createStatement和prepareStatement方法重載了,以支持不同的ResultSet和併發類型。

  一共有三種ResultSet對象。

  • ResultSet.TYPE_FORWARD_ONLY:這是默認的類型,它的遊標只能往下移。

  • ResultSet.TYPE_SCROLL_INSENSITIVE:遊標可以上下移動,一旦它創建後,數據庫裏的數據再發生修改,對它來說是透明的。

  • ResultSet.TYPE_SCROLL_SENSITIVE:遊標可以上下移動,如果生成後數據庫還發生了修改操作,它是能夠感知到的。

  ResultSet有兩種併發類型。

  • ResultSet.CONCUR_READ_ONLY:ResultSet是隻讀的,這是默認類型。

  • ResultSet.CONCUR_UPDATABLE:我們可以使用ResultSet的更新方法來更新裏面的數據。

  Statement中的setFetchSize和setMaxRows方法有什麼用處?

  setMaxRows可以用來限制返回的數據集的行數。當然通過SQL語句也可以實現這個功能。比如在MySQL中我們可以用LIMIT條件來設置返回結果的最大行數。

  setFetchSize理解起來就有點費勁了,因爲你得知道Statement和ResultSet是怎麼工作的。當數據庫在執行一條查詢語句時,查詢到的數據是在數據庫的緩存中維護的。ResultSet其實引用的是數據庫中緩存的結果。

  假設我們有一條查詢返回了100行數據,我們把fetchSize設置成了10,那麼數據庫驅動每次只會取10條數據,也就是說得取10次。當每條數據需要處理的時間比較長的時候並且返回數據又非常多的時候,這個可選的參數就變得非常有用了。

  我們可以通過Statement來設置fetchSize參數,不過它會被ResultSet對象設置進來的值所覆蓋掉。

  如何使用JDBC接口來調用存儲過程?

  存儲過程就是數據庫編譯好的一組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參數。關於這個更詳細的資料可以看這裏

  JDBC的批處理是什麼,有什麼好處?

  有時候類似的查詢我們需要執行很多遍,比如從CSV文件中加載數據到關係型數據庫的表裏。我們也知道,執行查詢可以用Statement或者PreparedStatement。除此之外,JDBC還提供了批處理的特性,有了它,我們可以在一次數據庫調用中執行多條查詢語句。

  JDBC通過Statement和PreparedStatement中的addBatch和executeBatch方法來支持批處理。

  批處理比一條條語句執行的速度要快得多,因爲它需要很少的數據庫調用,想進一步瞭解請點這裏

  JDBC的事務管理是什麼,爲什麼需要它?

  默認情況下,我們創建的數據庫連接,是工作在自動提交的模式下的。這意味着只要我們執行完一條查詢語句,就會自動進行提交。因此我們的每條查詢,實際上都是一個事務,如果我們執行的是DML或者DDL,每條語句完成的時候,數據庫就已經完成修改了。

  有的時候我們希望由一組SQL查詢組成一個事務,如果它們都執行OK我們再進行提交,如果中途出現異常了,我們可以進行回滾。

  JDBC接口提供了一個setAutoCommit(boolean flag)方法,我們可以用它來關閉連接自動提交的特性。我們應該在需要手動提交時才關閉這個特性,不然的話事務不會自動提交,每次都得手動提交。數據庫通過表鎖來管理事務,這個操作非常消耗資源。因此我們應當完成操作後儘快的提交事務。在這裏有更多關於事務的示例程序。

  如何回滾事務?

  通過Connection對象的rollback方法可以回滾事務。它會回滾這次事務中的所有修改操作,並釋放當前連接所持有的數據庫鎖。

  譯註:先到這吧,後面還有20題,明天繼續。都比較基礎,不過有些同學可能長期使用ORM框架,就當複習一下了,萬一面試問到呢~

轉載自:http://www.admin10000.com/document/4071.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章