回顾了一下, 在今年一月份开始看Java的时候, 快速总结了一遍JDBC的知识, 做了一个脑图, 至今回头看看效果还不错, 可以迅速上手.

这次再查缺补漏一下吧.

  1. JDBC的结构
  2. 示例
  3. 事务处理
  4. JNDI数据库连接

JDBC的结构

Oracle在JDBC方面, 主要规定了两大标准, 一个是JDBC API, 一个是JDBC 驱动器 API,也就是Driver接口.

Java应用程序打交道的是JDBC API ,JDBC API去寻找DriverManager类进行工作, DriverManager类根据在其中注册的Driver实现与数据库的具体沟通.

所以要使用什么样的数据库, 就需要下载对应的Driver, 然后在DriverManager中注册, 之后操作JDBC API就可以了.

JDBC和DriverManager类, Driver接口都在java.sql包中, 而具体的Driver类实现, 则由各个数据库厂商进行实现.

还有一些关键的接口如下:

  1. Connection接口, 表示数据库连接
  2. Statement接口, SQL语句对象, 负责生成和执行SQL语句
  3. PreparedStatement接口, 预处理的SQL语句
  4. CallableStatment接口, 负责执行存储过程
  5. ResultSet接口, 表示返回的结果集, SQL语句对象执行完毕之后就会返回这种类型

来详细的看使用方法.

示例

这两个类(接口)是紧密配合工作的, 在使用任何数据库之前, 都要获得驱动, 然后通过DriverManager.registerDriver(Driver)来注册驱动.

使用Class.forName()来加载驱动, 实际上就不用新建对象, 是为了解耦, 实际加载驱动的模块在驱动的静态块中.

要使用MySQL, 可以下载官方的Connector驱动, 然后就可以注册驱动了. 完整示例如下:

public static void main(String[] args) throws ClassNotFoundException, SQLException {
    Class.forName("com.mysql.cj.jdbc.Driver");

    Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/BookDB?useUnicode=true&characterEncoding=UTF-8", "root", "fflym0709");

    Statement statement = connection.createStatement();

    String sql = "SELECT * FROM BOOKS";

    ResultSet rs = statement.executeQuery(sql);

    while (rs.next()) {
        System.out.print(rs.getString(1)+"|");
        System.out.print(rs.getString(2)+"|");
        System.out.print(rs.getString(3)+"|");
        System.out.print(rs.getBigDecimal(4)+"|");
        System.out.print(rs.getDate(5).toLocalDate().getYear()+"|");
        System.out.print(rs.getString(6)+"|");
        System.out.println(rs.getInt(7)+"|");
    }

    //关闭的时候逆向关闭
    rs.close();
    statement.close();
    connection.close();

}

ResultSet的每一个元素代表一个查询的结果, 可以按照表的栏数取出其中的对象, 这里还需要知道MySQL与Java类中的对应关系.

事务处理

默认将一条语句视为一个事务, 可以关闭, 然后手工使用.commit()和.rollback():

public class TestJDBC {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.cj.jdbc.Driver");

        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/BookDB?useUnicode=true&characterEncoding=UTF-8", "root", "********");

        Statement statement = connection.createStatement();

        connection.setAutoCommit(false);

        try {
            statement.execute("UPDATE BOOKS SET SALE_AMOUNT=SALE_AMOUNT+10 WHERE ID='103'");
            statement.execute("UPDATE BOOKS SET SALE_AMOUNT=SALE_AMOUNT+10 WHERE ID='102'");
            statement.execute("UPDATE BOOKS SET PRICE=79.5 WHERE ID='102'");

            connection.commit();
        } catch (Exception e) {
            e.printStackTrace();
            connection.rollback();
        }

        statement.close();
        connection.close();
    }
}

JNDI数据库连接

每次都创建连接, 太重型, 在上次的学习里了解过了第三方库的连接池, 现在来看看如何在Web容器中配置DataSource.

数据源由Web容器来提供可以非常方便的供Web应用来使用. Java程序可以通过JNDI技术, 通过javax.naming包来检索对象并且使用. Tomcat可以把DataSource配置成一种JNDI资源.

先来看看如何配置, 需要两个配置, 一个是配置context.xml 这个属于tomcat的配置, 还一个是配置web.xml这个属于web应用的配置. 至于context.xml的位置也有讲究, 这里就只在/conf/下直接配置用于整个Web容器的context.xml:

<Context>
    ......
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
    <WatchedResource>WEB-INF/tomcat-web.xml</WatchedResource>
    <WatchedResource>${catalina.base}/conf/web.xml</WatchedResource>
    <Resource name="jdbc/bookDB" type="javax.sql.DataSource" password="fflym0709" driverClassName="com.mysql.cj.jdbc.Driver"
          maxIdle="5" maxWait="5000" username="root" url="jdbc:mysql://localhost:3306/BookDB?useUnicode=true&characterEncoding=UTF-8"
          maxActive="10"/>
    ......
</Context>

如此配置之后, 容器的环境中就有了一个叫做jdbc/bookDB的名称的对象, 不过还需要在web-xml中也配置一下:

<resource-ref>
    <res-ref-name>jdbc/bookDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

这样就能被当前应用识别了, 其中的名称和对应的类型都要和context.xml中保持一致.

之后在程序中, 通过 javax.naming类来查找对象并使用:

Context context = new InitialContext();
DataSource dataSource = (DataSource) context.lookup("java:comp/env/jdbc/bookDB");
Connection connection = dataSource.getConnection();

橙色部分是必须加上去的, 红色部分就是刚才自己定义的名称, 通过这个名称查找可以得到一个对象, 由于已经知道是DataSource对象, 就可以实行强制转换, 然后直接获取连接就可以了.

和其他的连接池库一样, 获取的connection实际上是一个Connection的代理类,对其调用.close()是归还进池子里.

使用数据源的还一个好处就是会自动关闭由MySQL驱动程序运行的Abandoned Connection Cleanup Thread 线程. 否则在退出Web应用的时候可以看到这个线程没有被终止, 会报错.