JDBC常見面試題

如下我是概括的JDBC知識點圖:java

圖上的知識點均可以在我其餘的文章內找到相應內容。mysql

JDBC常見面試題

JDBC操做數據庫的步驟 ?

JDBC操做數據庫的步驟 ?
  1. 註冊數據庫驅動。
  2. 創建數據庫鏈接。
  3. 建立一個Statement。
  4. 執行SQL語句。
  5. 處理結果集。
  6. 關閉數據庫鏈接

代碼以下:面試

Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;

        try {

            /*
            * 加載驅動有兩種方式
            *
            * 1:會致使驅動會註冊兩次,過分依賴於mysql的api,脫離的mysql的開發包,程序則沒法編譯
            * 2:驅動只會加載一次,不須要依賴具體的驅動,靈活性高
            *
            * 咱們通常都是使用第二種方式
            * */

            //1.
            //DriverManager.registerDriver(new com.mysql.jdbc.Driver());

            //2.
            Class.forName("com.mysql.jdbc.Driver");

            //獲取與數據庫鏈接的對象-Connetcion
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/zhongfucheng", "root", "root");

            //獲取執行sql語句的statement對象
            statement = connection.createStatement();

            //執行sql語句,拿到結果集
            resultSet = statement.executeQuery("SELECT * FROM users");

            //遍歷結果集,獲得數據
            while (resultSet.next()) {

                System.out.println(resultSet.getString(1));

                System.out.println(resultSet.getString(2));
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {

            /*
            * 關閉資源,後調用的先關閉
            *
            * 關閉以前,要判斷對象是否存在
            * */

            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }

            }

        }

JDBC中的Statement 和PreparedStatement,CallableStatement的區別?

JDBC中的Statement 和PreparedStatement的區別?

區別:sql

  • PreparedStatement是預編譯的SQL語句,效率高於Statement。
  • PreparedStatement支持?操做符,相對於Statement更加靈活。
  • PreparedStatement能夠防止SQL注入,安全性高於Statement。
  • CallableStatement適用於執行存儲過程。

JDBC中大數據量的分頁解決方法?

JDBC中大數據量的分頁解決方法?

最好的辦法是利用sql語句進行分頁,這樣每次查詢出的結果集中就只包含某頁的數據內容。數據庫

mysql語法:apache

SELECT *
    FROM 表名
    LIMIT [START], length;

oracle語法:api

SELECT *FROM (
        SELECT 列名,列名,ROWNUM rn
        FROM 表名
        WHERE ROWNUM<=(currentPage*lineSize)) temp
    
    WHERE temp.rn>(currentPage-1)*lineSize;

說說數據庫鏈接池工做原理和實現方案?

說說數據庫鏈接池工做原理和實現方案?

工做原理:緩存

  • JAVA EE服務器啓動時會創建必定數量的池鏈接,並一直維持很多於此數目的池鏈接。客戶端程序須要鏈接時,池驅動程序會返回一個未使用的池鏈接並將其表記爲忙。若是當前沒有空閒鏈接,池驅動程序就新建必定數量的鏈接,新建鏈接的數量有配置參數決定。當使用的池鏈接調用完成後,池驅動程序將此鏈接表記爲空閒,其餘調用就可使用這個鏈接。

實現方案:鏈接池使用集合來進行裝載,返回的Connection是原始Connection的代理,代理Connection的close方法,當調用close方法時,不是真正關鏈接,而是把它代理的Connection對象放回到鏈接池中,等待下一次重複利用。tomcat

具體代碼:安全

@Override
    public Connection getConnection() throws SQLException {

        if (list.size() > 0) {
            final Connection connection = list.removeFirst();

            //看看池的大小
            System.out.println(list.size());

            //返回一個動態代理對象
            return (Connection) Proxy.newProxyInstance(Demo1.class.getClassLoader(), connection.getClass().getInterfaces(), new InvocationHandler() {

                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                    //若是不是調用close方法,就按照正常的來調用
                    if (!method.getName().equals("close")) {
                        method.invoke(connection, args);
                    } else {

                        //進到這裏來,說明調用的是close方法
                        list.add(connection);

                        //再看看池的大小
                        System.out.println(list.size());

                    }
                    return null;
                }

            });
        }
        return null;
    }

Java中如何進行事務的處理?

Java中如何進行事務的處理?
  1. 事務是做爲單個邏輯工做單元執行的一系列操做
  2. 一個邏輯工做單元必須有四個屬性,稱爲原子性、一致性、隔離性和持久性 (ACID) 屬性,只有這樣才能成爲一個事務

Connection類中提供了4個事務處理方法:

  • setAutoCommit(Boolean autoCommit):設置是否自動提交事務,默認爲自動提交,即爲true,經過設置false禁止自動提交事務;
  • commit():提交事務;
  • rollback():回滾事務.
  • savepoint:保存點

    • 注意:savepoint不會結束當前事務,普通提交和回滾都會結束當前事務的

修改JDBC代碼質量

下述程序是一段簡單的基於JDBC的數據庫訪問代碼,實現瞭如下功能:從數據庫中查詢product表中的全部記錄,而後打印輸出到控制檯.該代碼質量較低,如沒有正確處理異常,鏈接字符串以」魔數」的形式直接存在於代碼中等,請用你的思路從新編寫程序, 完成相同的功能,提升代碼質量.

原來的代碼:

public void printProducts(){
    Connection c = null;
    Statements s = null;
    ResultSet r = null;
    try{

c=DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:sid","username","password");
        s=c.createStatement();
        r=s.executeQuery("select id, name, price from product");
        System.out.println("Id\tName\tPrice");
        while(r.next()){
            int x = r.getInt("id");
            String y = r.getString("name");
            float z = r.getFloat("price");
            System.out.println(x + "\t" + y + "\t" + z);
        }
    } catch(Exception e){

    }
}

修改後的代碼:

class Constant{
    public static final String URL="jdbc:oracle:thin:@127.0.0.1:1521:sid";
    public static final String USERNAME="username";
    public static final String PASSWORD="password";
}

class DAOException extends Exception{
    public DAOException(){
        super();
    }
    public DAOException(String msg){
        super(msg);
    }
}

public class Test{

    public void printProducts() throws DAOException{
        Connection c = null;
        Statement s = null;
        ResultSet r = null;
        try{
            c = DriverManager.getConnection(Constant.URL,Constant.USERNAME,Constant.PASSWORD);
            s = c.createStatement();
            r = s.executeQuery("select id,name,price from product");
            System.out.println("Id\tName\tPrice");
            while(r.next()){
                int x = r.getInt("id");
                String y = r.getString("name");
                float z = r.getFloat("price");
                System.out.println(x + "\t" + y + "\t" + z);
            }
        } catch (SQLException e){
            throw new DAOException("數據庫異常");
        } finally {
            try{
                r.close();
                s.close();
                c.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

修改點:

  • url、password等信息不該該直接使用字符串「寫死」,可使用常量代替
  • catch中應該回滾事務,拋出RuntimeException也是回滾事務的一種方法
  • 關閉資源

寫出一段JDBC鏈接本機MySQL數據庫的代碼

寫出一段JDBC鏈接本機MySQL數據庫的代碼
Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql://localhost/test";
Stirng user='root';
String password='root';
Connection conn = DriverManager.getConnection(url,user,password);

JDBC是如何實現Java程序和JDBC驅動的鬆耦合的?

JDBC是如何實現Java程序和JDBC驅動的鬆耦合的?

經過制定接口,數據庫廠商來實現。咱們只要經過接口調用便可。隨便看一個簡單的JDBC示例,你會發現全部操做都是經過JDBC接口完成的,而驅動只有在經過Class.forName反射機制來加載的時候纔會出現。

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

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

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

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

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

  • 分別進行單條查詢——這樣作性能不好,不推薦。
  • 使用存儲過程——這取決於數據庫的實現,不是全部數據庫都支持。
  • 動態生成PreparedStatement——這是個好辦法,可是不能享受PreparedStatement的緩存帶來的好處了。
  • 在PreparedStatement查詢中使用NULL值——若是你知道輸入變量的最大個數的話,這是個不錯的辦法,擴展一下還能夠支持無限參數。

JDBC的髒讀是什麼?哪一種數據庫隔離級別能防止髒讀?

JDBC的髒讀是什麼?哪一種數據庫隔離級別能防止髒讀?

髒讀:一個事務讀取到另一個事務未提交的數據

例子:A向B轉帳,A執行了轉帳語句,但A尚未提交事務,B讀取數據,發現本身帳戶錢變多了!B跟A說,我已經收到錢了。A回滾事務【rollback】,等B再查看帳戶的錢時,發現錢並無多。

下面的三種個隔離級別均可以防止:

  • Serializable【TRANSACTION_SERIALIZABLE】
  • Repeatable read【TRANSACTION_REPEATABLE_READ】
  • Read committed【TRANSACTION_READ_COMMITTED】

什麼是幻讀,哪一種隔離級別能夠防止幻讀?

什麼是幻讀,哪一種隔離級別能夠防止幻讀?

是指在一個事務內讀取到了別的事務插入的數據,致使先後讀取不一致。

只有TRANSACTION_SERIALIZABLE隔離級別才能防止產生幻讀。

JDBC的DriverManager是用來作什麼的?

JDBC的DriverManager是用來作什麼的?
  • JDBC的DriverManager是一個工廠類,咱們經過它來建立數據庫鏈接。
  • 當JDBC的Driver類被加載進來時,它會本身註冊到DriverManager類裏面
  • 而後咱們會把數據庫配置信息傳成DriverManager.getConnection()方法,DriverManager會使用註冊到它裏面的驅動來獲取數據庫鏈接,並返回給調用的程序

JDBC的ResultSet是什麼?

JDBC的ResultSet是什麼?
  • 在查詢數據庫後會返回一個ResultSet,它就像是查詢結果集的一張數據表。
  • ResultSet對象維護了一個遊標,指向當前的數據行。開始的時候這個遊標指向的是第一行。若是調用了ResultSet的next()方法遊標會下移一行,若是沒有更多的數據了,next()方法會返回false。能夠在for循環中用它來遍歷數據集。
  • 默認的ResultSet是不能更新的,遊標也只能往下移。也就是說你只能從第一行到最後一行遍歷一遍。不過也能夠建立能夠回滾或者可更新的ResultSet
  • 當生成ResultSet的Statement對象要關閉或者從新執行或是獲取下一個ResultSet的時候,ResultSet對象也會自動關閉。
  • 能夠經過ResultSet的getter方法,傳入列名或者從1開始的序號來獲取列數據。

有哪些不一樣的ResultSet?

有哪些不一樣的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的更新方法來更新裏面的數據。

JDBC的DataSource是什麼,有什麼好處

JDBC的DataSource是什麼,有什麼好處

DataSource即數據源,它是定義在javax.sql中的一個接口,跟DriverManager相比,它的功能要更強大。咱們能夠用它來建立數據庫鏈接,固然驅動的實現類會實際去完成這個工做。除了能建立鏈接外,它還提供了以下的特性:

  • 緩存PreparedStatement以便更快的執行
  • 能夠設置鏈接超時時間
  • 提供日誌記錄的功能
  • ResultSet大小的最大閾值設置
  • 經過JNDI的支持,能夠爲servlet容器提供鏈接池的功能

如何經過JDBC的DataSource和Apache Tomcat的JNDI來建立鏈接池?

如何經過JDBC的DataSource和Apache Tomcat的JNDI來建立鏈接池?

Tomcat服務器也給咱們提供了鏈接池,內部其實就是DBCP

步驟:

  1. 在META-INF目錄下配置context.xml文件【文件內容能夠在tomcat默認頁面的 JNDI Resources下Configure Tomcat's Resource Factory找到】
  2. 導入Mysql或oracle開發包到tomcat的lib目錄下
  3. 初始化JNDI->獲取JNDI容器->檢索以XXX爲名字在JNDI容器存放的鏈接池

context.xml文件的配置:

<Context>

  <Resource name="jdbc/EmployeeDB"
            auth="Container"
            type="javax.sql.DataSource"
            
            username="root"
            password="root"
            driverClassName="com.mysql.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/zhongfucheng"
            maxActive="8"
            maxIdle="4"/>
</Context>
try {

            //初始化JNDI容器
            Context initCtx = new InitialContext();

            //獲取到JNDI容器
            Context envCtx = (Context) initCtx.lookup("java:comp/env");

            //掃描以jdbc/EmployeeDB名字綁定在JNDI容器下的鏈接池
            DataSource ds = (DataSource)
                    envCtx.lookup("jdbc/EmployeeDB");

            Connection conn = ds.getConnection();
            System.out.println(conn);

        }

Apache的DBCP是什麼?

Apache的DBCP是什麼

若是用DataSource來獲取鏈接的話,一般獲取鏈接的代碼和驅動特定的DataSource是緊耦合的。另外,除了選擇DataSource的實現類,剩下的代碼基本都是同樣的。

Apache的DBCP就是用來解決這些問題的,它提供的DataSource實現成爲了應用程序和不一樣JDBC驅動間的一個抽象層。Apache的DBCP庫依賴commons-pool庫,因此要確保它們都在部署路徑下。

使用DBCP數據源的步驟:

  1. 導入兩個jar包【Commons-dbcp.jar和Commons-pool.jar】
  2. 讀取配置文件
  3. 獲取BasicDataSourceFactory對象
  4. 建立DataSource對象
private static DataSource dataSource = null;

    static {
        try {
            //讀取配置文件
            InputStream inputStream = Demo3.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(inputStream);

            //獲取工廠對象
            BasicDataSourceFactory basicDataSourceFactory = new BasicDataSourceFactory();
            dataSource = basicDataSourceFactory.createDataSource(properties);

        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();

    }

    //這裏釋放資源不是把數據庫的物理鏈接釋放了,是把鏈接歸還給鏈接池【鏈接池的Connection內部本身作好了】
    public static void release(Connection conn, Statement st, ResultSet rs) {

        if (rs != null) {
            try {
                rs.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            rs = null;
        }
        if (st != null) {
            try {
                st.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
        if (conn != null) {
            try {
                conn.close();
            } catch (Exception e) {
                e.printStackTrace();
            }

        }


    }

常見的JDBC異常有哪些?

常見的JDBC異常有哪些?

有如下這些:

  • java.sql.SQLException——這是JDBC異常的基類。
  • java.sql.BatchUpdateException——當批處理操做執行失敗的時候可能會拋出這個異常。這取決於具體的JDBC驅動的實現,它也可能直接拋出基類異常java.sql.SQLException。
  • java.sql.SQLWarning——SQL操做出現的警告信息。
  • java.sql.DataTruncation——字段值因爲某些非正常緣由被截斷了(不是由於超過對應字段類型的長度限制)。

JDBC中存在哪些不一樣類型的鎖?

JDBC中存在哪些不一樣類型的鎖?

從廣義上講,有兩種鎖機制來防止多個用戶同時操做引發的數據損壞。

  • 樂觀鎖——只有當更新數據的時候纔會鎖定記錄
  • 悲觀鎖——從查詢到更新和提交整個過程都會對數據記錄進行加鎖。

java.util.Date和java.sql.Date有什麼區別?

java.util.Date和java.sql.Date有什麼區別?

java.util.Date包含日期和時間,而java.sql.Date只包含日期信息,而沒有具體的時間信息。若是你想把時間信息存儲在數據庫裏,能夠考慮使用Timestamp或者DateTime字段

SQLWarning是什麼,在程序中如何獲取SQLWarning?

SQLWarning是什麼,在程序中如何獲取SQLWarning?

SQLWarning是SQLException的子類,經過Connection, Statement, Result的getWarnings方法均可以獲取到它。 SQLWarning不會中斷查詢語句的執行,只是用來提示用戶存在相關的警告信息。

若是java.sql.SQLException: No suitable driver found該怎麼辦?

若是java.sql.SQLException: No suitable driver found該怎麼辦?

若是你的SQL URL串格式不正確的話,就會拋出這樣的異常。不論是使用DriverManager仍是JNDI數據源來建立鏈接都有可能拋出這種異常。它的異常棧看起來會像下面這樣。

org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot create JDBC driver of class 'com.mysql.jdbc.Driver' for connect URL ''jdbc:mysql://localhost:3306/UserDB'
    at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createConnectionFactory(BasicDataSource.java:1452)
    at org.apache.tomcat.dbcp.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1371)
    at org.apache.tomcat.dbcp.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
java.sql.SQLException: No suitable driver found for 'jdbc:mysql://localhost:3306/UserDB
    at java.sql.DriverManager.getConnection(DriverManager.java:604)
    at java.sql.DriverManager.getConnection(DriverManager.java:221)
    at com.journaldev.jdbc.DBConnection.getConnection(DBConnection.java:24)
    at com.journaldev.jdbc.DBConnectionTest.main(DBConnectionTest.java:15)
Exception in thread "main" java.lang.NullPointerException
    at com.journaldev.jdbc.DBConnectionTest.main(DBConnectionTest.java:16)

解決這類問題的方法就是,檢查下日誌文件,像上面的這個日誌中,URL串是'jdbc:mysql://localhost:3306/UserDB,只要把它改爲jdbc:mysql://localhost:3306/UserDB就行了。

JDBC的RowSet是什麼,有哪些不一樣的RowSet?

JDBC的RowSet是什麼,有哪些不一樣的RowSet?

RowSet用於存儲查詢的數據結果,和ResultSet相比,它更具靈活性。RowSet繼承自ResultSet,所以ResultSet能幹的,它們也能,而ResultSet作不到的,它們仍是能夠。RowSet接口定義在javax.sql包裏。

RowSet提供的額外的特性有:

  • 提供了Java Bean的功能,能夠經過settter和getter方法來設置和獲取屬性。RowSet使用了JavaBean的事件驅動模型,它能夠給註冊的組件發送事件通知,好比遊標的移動,行的增刪改,以及RowSet內容的修改等。
  • RowSet對象默認是可滾動,可更新的,所以若是數據庫系統不支持ResultSet實現相似的功能,可使用RowSet來實現

RowSet分爲兩大類:

  • A. 鏈接型RowSet——這類對象與數據庫進行鏈接,和ResultSet很相似。JDBC接口只提供了一種鏈接型RowSet,javax.sql.rowset.JdbcRowSet,它的標準實現是com.sun.rowset.JdbcRowSetImpl。
  • B. 離線型RowSet——這類對象不須要和數據庫進行鏈接,所以它們更輕量級,更容易序列化。它們適用於在網絡間傳遞數據

    • 有四種不一樣的離線型RowSet的實現。

      • CachedRowSet——能夠經過他們獲取鏈接,執行查詢並讀取ResultSet的數據到RowSet裏。咱們能夠在離線時對數據進行維護和更新,而後從新鏈接到數據庫裏,並回寫改動的數據。
      • WebRowSet繼承自CachedRowSet——他能夠讀寫XML文檔。
      • JoinRowSet繼承自WebRowSet——它不用鏈接數據庫就能夠執行SQL的join操做。
      • FilteredRowSet繼承自WebRowSet——咱們能夠用它來設置過濾規則,這樣只有選中的數據纔可見。

 什麼是JDBC的最佳實踐?

 什麼是JDBC的最佳實踐?
  • 數據庫資源是很是昂貴的,用完了應該儘快關閉它。Connection, Statement, ResultSet等JDBC對象都有close方法,調用它就行了。
  • 養成在代碼中顯式關閉掉ResultSet,Statement,Connection的習慣,若是你用的是鏈接池的話,鏈接用完後會放回池裏,可是沒有關閉的ResultSet和Statement就會形成資源泄漏了。
  • 在finally塊中關閉資源,保證即使出了異常也能正常關閉。
  • 大量相似的查詢應當使用批處理完成
  • 儘可能使用PreparedStatement而不是Statement,以免SQL注入,同時還能經過預編譯和緩存機制提高執行的效率。
  • 若是你要將大量數據讀入到ResultSet中,應該合理的設置fetchSize以便提高性能
  • 你用的數據庫可能沒有支持全部的隔離級別,用以前先仔細確認下。
  • 數據庫隔離級別越高性能越差,確保你的數據庫鏈接設置的隔離級別是最優的。
  • 若是在WEB程序中建立數據庫鏈接,最好經過JNDI使用JDBC的數據源,這樣能夠對鏈接進行重用。
  • 若是你須要長時間對ResultSet進行操做的話,儘可能使用離線的RowSet。
若是文章有錯的地方歡迎指正,你們互相交流。習慣在微信看技術文章的同窗, 能夠關注微信公衆號:Java3y
相關文章
相關標籤/搜索