美文网首页程序员
JDBC 环境搭建与开发

JDBC 环境搭建与开发

作者: 夏海峰 | 来源:发表于2018-08-29 15:01 被阅读106次

    数据库是后端最重要的持久化组件。JDBC 是数据库开发的基础,通过 JDBC 就可以实现对后端数据库的访问。

    1、访问数据库最常见的几种方式(以 MySQL 为例)

    1. 图形化工具:Workbench,用户通过点击和拖拽就能实现对 MySQL 数据库的访问和操作,它的优点是页面友好、操作简单;缺点是无法满足较为复杂的查询需求。它满足了人对数据库的访问需求。
    2. 命令行工具:在命令行中输入完整的 SQL 语句实现更为复杂的查询需求,这就要求用户能熟练地使用 SQL,门槛相对较高。
    3. JDBC 驱动程序:Connector/J,使用相关驱动和 API 即可实现对数据库的访问。它满足了程序对数据库的访问需求。
    三种访问数据库的方式

    本节课我们要学习的重点是第3种方式,即通过 Java 应用程序来访问数据库。

    2、什么是 JDBC ?应用程序是如何与数据库连接的?

    不同的数据库,所支持的连接协议也是不相同的,数据库驱动程序所提供的 API 接口也不相同,这给“跨数据库迁移”带来了诸多不便。JDBC 存在的意义,就是统一了 API 接口,从而可以方便地对任意数据库进行访问。JDBC 帮助我们屏蔽了应用程序与数据库之间交互协议的实现细节,我们只需要使用 JDBC 提供的标准 API ,即可实现对任意数据库的访问,且无需关心底层的实现方式。

    对 Java 程序来讲,JDBC 就是一个普通的 Java 类库(JAR包),在应用程序中引用 JDBC JAR包所提供的类和方法,通过操作 Java对象的方式就可以操作数据库中的数据。

    对 数据库厂商来讲,JDBC 就是一套接口规范,每个厂商的数据库驱动都必须实现 JDBC 中所定义的数据库接口,如此才能在 Java程序中实现数据库连接与访问。

    JDBC 的作用

    3、JDBC 有哪些优势?

    1、它使得Java程序对数据库的访问变得更加简单,一套接口即可实现对任意数据库的访问。
    2、使用 JDBC,大大地提升了数据库开发的效率。
    3、基于 JDBC的接口规范,使得 Java程序在面对不同的数据库时,具有了强大的跨平台可移植性。
    4、JDBC 只是定义了最最基础的 API 功能,我们可以基于 JDBC 基本功能再去封装更为简单、更为便捷的框架。

    4、JDBC 的体系架构是怎样的?

    JDBC 的体系架构分为两层,分别是上层的 JDBC API 层,下层的 JDBC 驱动层。API 层 主要用于与数据库进行通信;驱动层主要用于与具体的数据库建立连接,这些驱动层 Driver 一般都是由数据库厂商提供的。

    JDBC 的体系架构

    5、什么是 JDBC 驱动?如何搭建 JDBC 的开发环境?

    JDBC 的 API 默认是已经集成在 JDK 中了,无需额外地安装。我们需要下载并安装的是 具体的数据库驱动。对 MySQL 数据库来讲,我们需要下载并安装的是 Connector/J ,这个驱动实际上是一个 JAR包,我们需要把它加入到 Java应用工程中去,或者将其添加到 CLASSPATH 环境变量中去,但是我们通常推荐使用 Maven 来管理数据库驱动程序 JAR包。

    下载 MySQL 的 JDBC 驱动
    配置 Connector/J 驱动JAR包的环境变量

    6、JDBC API 所涉及到的几个非常重要的 Java 类

    这些 JDBC 的 API 类,其继承关系如下图所示,其中 Driver 是驱动程序最顶层的抽象接口,它定义了驱动程序所有必须的基本功能。

    JDBC API 的继承关系

    7、认识 DriverManager 类:

    DriverManager 是 JDBC 驱动程序的管理类,使用 Class.forName() 可以向 DriverManager 装载并注册一个驱动程序。使用 DriverManager 对象的 getConnection() 方法就可以建立到指定数据库的连接。

    DriverManger 类

    getConnection() 接收三个参数,分别是 DB_URL,USERNAME,PASSWORD。下面重点解释一下连接数据库所用的 DB_URL:

    什么是 DB_URL ?

    它是数据库唯一的通信标识符,通过它可以确定唯一的数据库实例。DB_URL 的组成部分如下图所示:

    DB_URL 的组成

    其中“子协议”部分决定了数据库的类型。如下图是三种常用数据库的 DB_URL 格式:

    常用数据库的 DB_URL

    8、认识 Connection 类:

    Connection对象代表着 Java应用程序与后端数据库的一条物理连接,基于这条连接,我们可执行一些 SQL 语句,它最常用的方法是 conn.createStatement(),该方法返回一个 Statement 对象。

    Connection 类

    9、认识 Statement 类:

    Statement 对象,可以理解成是一个 SQL 窗器,在这个窗器中可以执行 SQL 语句,以实现增删改查的功能。执行该对象的 executeQuery() 方法,可以得到一个查询结果的对象,即 ResultSet 对象。如果是执行该对象的 增、删、改的方法,返回的结果是一个 int 类型的值,这个值代表的意思是当前操作所影响到的行数。

    Statement 类

    10、认识 ResultSet 类:

    ResultSet 对象代表着 SQL 查询的结果集。关系型数据库可以看成一个二元表。ResultSet 对象的内部有一个指针,用于指向结果集的行记录,默认指向第一条行记录。调用 ResultSet 对象的 next() 方法可以让指针指向下一条行记录。调用 previous() 方法让指针指向上一行。调用 absolute() 方法让指针指向指定的行。调用 beforeFirst() 方法让指针指向第一行的上一条记录。调用 afterLast() 方法,让指针指向最后一行的下一条记录。

    ResultSet 类

    遍历 ResultSet 对象,即可读取结果集中的每一行记录,相关方法如下表:


    ResultSet 类

    11、认识 SQLException 类:

    在执行 SQL 语句的过程,MySQL 服务器有可能会抛出一些异常,SQLException 对象即代表着这些异常,捕获这些异常后,就是一个完整的 JDBC 工作流程了。

    12、JDBC 开发的五个基本步骤

    JDBC 开发基本满足如下的五个步骤,对每个步骤的准确理解至关重要。尤其是 第 5步(清理环境资源)相当重要,数据库连接是非常宝贵的资源,必须在使用完毕后及时地关闭

    JDBC 开发的 5 个基本步骤

    JDBC 开发的 5个基本步骤, 示例如下:

    package cn.geekxia;
    
    import java.sql.*;
    
    public class HelloMySQL {
    
        public final static String JDBC_DRIVER = "com.mysql.jdbc.Driver";
        public final static String DB_URL = "jdbc:mysql://localhost/world";
        public final static String USER = "root";
        public final static String PASS = "xxxxxxxxxxxxx";
        public static void helloMysql() throws ClassNotFoundException {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            
            // 第1步:装载 JDBC 驱动程序
            Class.forName(JDBC_DRIVER);
            // 第2步:建立到 MySQL数据库的连接,这有可能出现异常,这里必须要进行相应的异常处理
            try {
                conn = DriverManager.getConnection(DB_URL, USER, PASS);
                // 第3步:执行 SQL 语句
                stmt = conn.createStatement();
                rs = stmt.executeQuery("select * from country");
                // 第4步:遍历 SQL 的查询结果
                while (rs.next()) {
                    String name = rs.getString("Name");
                    int id = rs.getInt("Code");
                    System.out.println(id + " : " + name);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 第5步:清理数据库连接资源,放在 finally 中执行,以确保无论是否发生了 SQLException,都能够清理掉这些连接资源,以减少程序的性能耗费。清理资源也有可能发生异常,因此这里也需要进行异常捕获。
                try {
                    if (conn != null)
                    conn.close();
                    if (stmt != null)
                    stmt.close();
                    if (rs != null)
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) throws ClassNotFoundException {
            helloMysql();  // 测试执行
        }
    }
    

    上述代码中,尤其要对 第5步 进行说明:清理数据库连接资源,以减少程序的性能耗费,这是非常重要的。那么为什么要放在 finally 中去执行资源清理工作呢?放在 finally 中执行,是为了确保无论是否发生了 SQLException 异常,都能够清理掉这些连接资源。清理资源的过程,也有可能发生异常,因此这里也需要进行异常捕获。



    13、如何解决 JDBC 读取数据记录过多的问题?

    Java程序是运行在 JVM 中的,JVM 是有内存大小限制的。在我们使用 JDBC 读取数据库中的数据时,一定要考虑 JVM 分配给 Java 程序的内存大小是否满足要求,否则将有可能报“内存溢出”的错误。

    情景描述:当 SQL 过滤条件比较弱,一次 JDBC 查询读取的数据记录过多时。或者需要读取数据表中所有的记录时。这时,JDBC 读取的数据量有可能达到成千上万条,数据量如果过大,就有可能导致 JVM 内存溢出。

    数据记录太多,导致内存溢出

    在这种情景下,就需要使用到 JDBC 游标来解决问题了。那么什么是 JDBC 游标?它到底是如何避免“JVM 内存溢出”问题的呢?

    什么是 JDBC 游标?什么是 fetch size ?

    对庞大的数据记录,为了避免内存溢出,我们可以分批来读取数据库中的数据记录,这每一批的数据即为一个 fetch size。支持这种数据读取方式的技术,即为 JDBC 游标。如下图示:

    JDBC 游标 与 fetch size

    如何开启 JDBC 游标呢?

    在 JDBC 与数据库连接的 DB_URL 上,添加一个 useCursorFetch 属性即可开启 JDBC 游标,如下图的 DB_URL 所示:

    如何开启 JDBC 游标?

    如何使用 JDBC 游标?使用 PreparedStatement 类:

    使用 PreparedStatement 对象代替 Statement 对象。PreparedStatement 实际上是继承自 Statement 的。PreparedStatement 是专门为 JDBC 游标而设计的,以解决读取大量数据记录可能导致的“内存溢出”问题。

    PreparedStatement 类

    使用 JDBC 游标分批读取数据库记录,示例代码如下:

    // UseCursorFetch.java
    package cn.geekxia;
    import java.sql.*;
    
    public class UseCursorFetch {
    
        public final static String JDBC_DRIVER = "com.mysql.jdbc.Driver";
    
        // 开启 JDBC 游标, useCursorFetch=true
        public final static String DB_URL = "jdbc:mysql://localhost/world?useCursorFetch=true";
    
        public final static String USER = "root";
        public final static String PASS = "x-448914712";
        public static void helloMysql() throws ClassNotFoundException {
            Connection conn = null;
            // 使用游标
            PreparedStatement stmt = null;
            ResultSet rs = null;
            
            // 第1步:装载 JDBC 驱动程序
            Class.forName(JDBC_DRIVER);
            // 第2步:建立到 MySQL数据库的连接,这有可能出现异常,这里必须要进行相应的异常处理
            try {
                conn = DriverManager.getConnection(DB_URL, USER, PASS);
    
                // 第3步:使用 JDBC 游标 执行 SQL 语句
                stmt = conn.prepareStatement("select * from country");
                stmt.setFetchSize(10);  // 每次读取 10 条数据记录
                rs = stmt.executeQuery();
    
                // 第4步:遍历 SQL 的查询结果
                while (rs.next()) {
                    String name = rs.getString("Name");
                    int id = rs.getInt("Code");
                    System.out.println(id + " : " + name);
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 第5步:清理数据库连接资源,放在 finally 中执行,以确保无论是否发生了 SQLException,都能够清理掉这些连接资源,以减少程序的性能耗费。清理资源也有可能发生异常,因此这里也需要进行异常捕获。
                try {
                    if (conn != null)
                    conn.close();
                    if (stmt != null)
                    stmt.close();
                    if (rs != null)
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) throws ClassNotFoundException {
            helloMysql();
        }
    }
    

    上述代码的核心变化有两点,一个是给 DB_URL 追加了 useCursorFetch 属性,另一个是使用 PreparedStatement 对象代替了 Statement 对象



    14、如何解决 JDBC 读取大数据量 字段的问题?

    情景描述:我们并不推荐在数据表中存储较大数据量的字段,比如图片、博客文章等。但是实际应用开发中,为了方便性我们还是经常会把大数据量的字段存入到数据库中去。在上一讲中,我们讨论了读取过多的数据记录有可能导致 JVM 内存溢出。事实上,即使是读取单条数据记录时,如果某个字段的数据量过大,同样会导致 JVM 内存溢出。如此看来,读取大数据量字段,也必须是我们在 JDBC 开发过程中不可忽视的问题。

    单个字段数据量过大,导致内存溢出

    这种情景下,我们采用的解决方案是“流方式”数据读取,即把大数据量的字段以二进制的方式进行分段读取,


    流方式 读取数据量过大的字段

    使用“流方式”读取数据库数据,示例代码如下:

    示例代码:流方式 读取数据库数据
    核心做法是,使用 ResultSet 对象的 getBinaryStream() 方法来读取二进制流数据



    15、如何解决 JDBC 向数据库插入大量数据记录而导致速度过慢的问题?

    情景描述:如果有大量数据记录需要插入到数据库,如果使用通常的一条一条的 SQL 插入语句去实现,这将导致插入速度非常慢。慢的原因是,每插入一条数据都需要向数据库发送一条 SQL 语句并执行一次 SQL 语句,如果插入成千上万条数据,那么就需要向数据库发送成千上万次 SQL,因此这样做的效率是很低的。

    这种情景下,我们的解决方案是使用“批处理”插入数据,即向数据库一次同时发送多条 SQL 语句 ,同时插入多条数据。这样做,可以大大地减少向数据库发送 SQL 语句的次数,因此能够提升向数据库插入数据的效率。

    批处理,解决大量数据插入数据库的问题
    批处理,所用的 API 是什么?

    批处理 所使用的 JDBC API 是 Statement / PreparedStatement 对象的 addBatch() / executeBatch() / clearBatch() 方法。

    使用 Statement / PreparedStatement 对象

    什么是 batch ?

    一个 batch 就是一组需要同时发送到数据库的多条 SQL 语句。

    “批处理”解决大量数据插入数据库的问题,示例代码如下:

    // UseBatch.java
    package cn.geekxia;
    import java.sql.*;
    
    public class UseBatch {
        public final static String JDBC_DRIVER = "com.mysql.jdbc.Driver";
        public final static String DB_URL = "jdbc:mysql://localhost/world";
        public final static String USER = "root";
        public final static String PASS = "x-448914712";
        public static void helloMysql() throws ClassNotFoundException {
            Connection conn = null;
            Statement stmt = null;
            ResultSet rs = null;
            
            // 第1步:装载 JDBC 驱动程序
            Class.forName(JDBC_DRIVER);
            // 第2步:建立到 MySQL数据库的连接,这有可能出现异常,这里必须要进行相应的异常处理
            try {
                conn = DriverManager.getConnection(DB_URL, USER, PASS);
    
                // 第3步:批处理,同时执行多条 SQL 语句
                stmt = conn.createStatement();
                String[] users = {"name1", "name2", "name3"};
                // 把三条 SQL 语句同时添加到 batch 中去
                for (String user: users) {
                    stmt.addBatch("insert into user(username) values("+user+")");
                }
                // 把三条 SQL 同时发送至数据库并执行
                stmt.executeBatch();
                // 执行完毕后,清理 batch,为下一次执行 batch 而准备
                stmt.clearBatch();
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                // 第5步:清理数据库连接资源,放在 finally 中执行,以确保无论是否发生了 SQLException,都能够清理掉这些连接资源,以减少程序的性能耗费。清理资源也有可能发生异常,因此这里也需要进行异常捕获。
                try {
                    if (conn != null)
                    conn.close();
                    if (stmt != null)
                    stmt.close();
                    if (rs != null)
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        public static void main(String[] args) throws ClassNotFoundException {
            helloMysql();
        }
    }
    

    上述代码的核心做法时,使用循环把多条 SQL 语句打包至一个 batch 中去,然后把 batch 发送至数据库一起执行,执行完 batch 后记得清理掉它即可



    16、关于中文乱码的问题

    如果 JDBC 中设置的字符集和数据库设置的字符集不一致,这将导致中文数据的乱码问题。

    查询数据库 Servler 的字符编码列表:
    show variables like '%character%';
    
    查询数据库 Server 的字符编码列表

    在创建数据表时,还可以为数据表、字段分别设置 字符编码格式。当数据库 Server、数据库、表、字段都同时设置了字符编码时,它们优先级如下图所示,即越小的单元优先级越高。

    字符编码的优先级
    如果数据库 Server、数据库、表、字段同时设置了字符编码格式,则为字段设置的编码格式优先起作用,依次类推

    那么该如何给 JDBC 设置字符编码格式以保证它与数据库的字符格式一致呢?

    做法是,在 JDBC 与数据库连接的 DB_URL 的末尾添加上字符集属性即可,如下图所示。

    为 JDBC 设置字符编码
    public final static String DB_URL = "jdbc:mysql://localhost/world?characterEncoding=uft8";
    

    通常为了避免中文乱码的情况,建议把数据库的编码格式和 JDBC 的编码格式都设置为 utf8



    附:在 Windows系统上搭建 JDBC开发环境
    在 Windows电脑上搭建 JDBC开发环境,所使用的工具有Java、Tomcat、MySQL、JDBC驱动程序。
    JDBC环境搭建-Windows版

    第1步:下载并安装 JavaSE,并配置相关环境变量

    下载 JDK 并配置两个环境变量:
    JAVA_HOME = D:\Java\JDK (JDK 所在的安装目录 )
    PATH = D:\Java\JDK\bin (JRE 所在的 bin 目录)

    第2步:下载并安装 Tomcat,配置 CATALINA_HOME 环境变量

    下载 Tomcat 并配置环境变量
    CATALINA_HOME = D:\Tomcat

    使用图形化的Tomcat工具对其进行启动与停止。在 D:\Tomcat\conf\server.xml 中可以对Tomcat进行相关自定义配置。
    Java程序的部署目录:D:\Tomcat\webapps\ROOT\WEB-INF
    静态资源文件部署目录:D:\Tomcat\webapps\ROOT

    第3步:下载并安装 MySQL

    下载 MySQL
    MySQL 官方教程

    第4步:配置 Java开发的第三方JAR包

    下载 servlet.jar包,下载 Connector/驱动包
    配置第三方JAR包的 CLASSPATH 环境变量:
    D:\Tomcat\lib\servlet-api.jar
    D:\Tomcat\lib\mysql-connector-java-8.0.12.jar
    Java程序运行时,会从 CLASSPATH 环境中寻找所需要的第三方JAR包。

    示例代码如下:

    package cn.geekxia;
    
    import java.util.Date;
    import javax.servlet.ServletException;
    import java.io.*;
    import javax.servlet.http.*;
    import java.sql.*;
    
    public class User extends HttpServlet {
        public static final String URL = "jdbc:mysql://localhost/world?serverTimezone=UTC&useSSL=false&user=root&password=xxxx";
    
        protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
            res.setContentType("text/html;charset=UTF-8");
            PrintWriter out = res.getWriter();
    
            Connection conn = null;
            Statement stmt = null;
            try {
                Class.forName("com.mysql.jdbc.Driver");   // 装载JDBC驱动程序
                conn = DriverManager.getConnection(URL);  // 创建数据库连接
                stmt = conn.createStatement();  
                String sql = "SELECT * FROM country;";     // 执行查询语句
                ResultSet rs = stmt.executeQuery(sql);
    
                while(rs.next()) {
                    String name = rs.getString("Name");
                    out.println("<span> "+name+" </span>");
                }
            } catch(SQLException e) {
                e.printStackTrace();
            } catch(Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (stmt != null) {
                        stmt.close();   // 如果存在 stmt,就关闭。以节省数据库连接资源。
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
                try {
                    if (conn != null) {
                        conn.close();   // 如果存在 conn,就关闭。以节省数据库连接资源。
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    部署描述符 web.xml ,代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0">
        <display-name>GeekXia</display-name>
        <servlet>
            <servlet-name>User</servlet-name>
            <servlet-class>
                cn.geekxia.User
            </servlet-class>
        </servlet>
        <servlet-mapping>
            <servlet-name>User</servlet-name>
            <url-pattern>/User</url-pattern>
        </servlet-mapping>
    </web-app>
    

    编译.java 代码,并放置至指定的 ./classes/cn/geekxia 文件目录:

    javac -d ./classes/cn/geekxia User.java
    

    重启 Tomcat,并在浏览器地址栏中输入 http://localhost:8080/User ,即可看到从 MySQL数据库中查询到的数据。


    本节完!!

    相关文章

      网友评论

        本文标题:JDBC 环境搭建与开发

        本文链接:https://www.haomeiwen.com/subject/rttkwftx.html