JDBC常见面试题

如下我是概括的JDBC知识点图:java

 

 

图上的知识点均可以在我其余的文章内找到相应内容。mysql

JDBC常见面试题

JDBC操做数据库的步骤 ?

JDBC操做数据库的步骤 ?面试

  1. 注册数据库驱动。
  2. 创建数据库链接。
  3. 建立一个Statement。
  4. 执行SQL语句。
  5. 处理结果集。
  6. 关闭数据库链接

代码以下:sql

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的区别?数据库

区别:apache

  • PreparedStatement是预编译的SQL语句,效率高于Statement。
  • PreparedStatement支持?操做符,相对于Statement更加灵活。
  • PreparedStatement能够防止SQL注入,安全性高于Statement。
  • CallableStatement适用于执行存储过程。

JDBC中大数据量的分页解决方法?

JDBC中大数据量的分页解决方法?api

最好的办法是利用sql语句进行分页,这样每次查询出的结果集中就只包含某页的数据内容。缓存

mysql语法:tomcat

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

oracle语法:安全

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对象放回到链接池中,等待下一次重复利用。

具体代码:

@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

相关文章
相关标签/搜索