zoukankan      html  css  js  c++  java
  • JDBC工具类——JdbcUtils(1)

    JDBC工具类——JdbcUtils(1)

    前言

    本系列文章介绍JDBC工具类——JdbcUtils的封装,部分实现参考了Spring框架的JdbcTemplate

    完整项目地址:https://github.com/byx2000/JdbcUtils

    分离数据库配置

    根据上一篇文章的分析,我们知道,不应该把数据库配置信息硬编码在Java代码中,因为这会导致维护困难。为了解决这个问题,可以把数据库的配置信息单独放在一个外部的配置文件中,然后工具类通过读取配置文件来获取数据库配置。

    数据库的配置信息主要包括数据库驱动类名、连接字符串、用户名、密码,这四个配置可以单独放在一个db.properties文件中,而db.properties文件可以放在resources文件夹中,这样工具类就可以从classpath下读取配置文件

    以下是db.properties的内容:

    # JDBC驱动类
    jdbc.driver=org.sqlite.JDBC
    # 连接字符串
    jdbc.url=jdbc:sqlite::resource:test.db
    # 用户名
    jdbc.username=
    # 密码
    jdbc.password=
    

    JdbcUtils的静态代码块中读取db.properties中的相关配置,并加载驱动:

    public class JdbcUtils
    {
        private static String url;
        private static String username;
        private static String password;
    
        static
        {
            try
            {
                // 读取resources目录下的db.properties文件
                InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties");
                Properties properties = new Properties();
                properties.load(in);
    
                // 读取驱动类名、连接字符串、用户名和密码
                String driver = properties.getProperty("jdbc.driver");
                url = properties.getProperty("jdbc.url");
                username = properties.getProperty("jdbc.username");
                password = properties.getProperty("jdbc.password");
    
                // 加载驱动
                Class.forName(driver);
            }
            catch (NullPointerException | IOException e)
            {
                throw new RuntimeException("找不到db.properties文件,请在resources目录下创建db.properties文件,并写入数据库配置", e);
            }
            catch (ClassNotFoundException e)
            {
                throw new RuntimeException("找不到数据库驱动类", e);
            }
        }
        ...
    }
    

    封装连接获取

    每次使用JDBC之前都要获取连接,而获取连接的代码都是固定的,因此可以提取成一个公共方法。

    JdbcUtils中封装一个getConnection方法用于获取连接:

    public class JdbcUtils
    {
        ...
        public static Connection getConnection() throws SQLException
        {
            return DriverManager.getConnection(url, username, password);
        }
        ...
    }
    

    客户代码可以通过调用JdbcUtils.getConnection()来获取连接。

    注意,当客户代码第一次调用JdbcUtils中的方法时,JdbcUtils中的静态代码块将会执行,此时数据库的配置信息会被读取,JdbcUtils.getConnection也就能成功获取到连接。

    封装资源释放

    在查询操作中,需要依次释放ResultSetStatementConnection

    在更新操作中,需要依次释放StatementConnection

    针对查询和更新操作对释放资源的不同处理,在JdbcUtils中添加两个close方法

    public class JdbcUtils
    {
        ...
        public static void close(ResultSet rs, Statement stmt, Connection conn)
        {
            if (rs != null)   try { rs.close(); }   catch (SQLException ignored) {}
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
        }
    
        public static void close(Statement stmt, Connection conn)
        {
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
        }
        ...
    }
    

    查询操作中可以通过调用JdbcUtils.close(rs, stmt, conn)来释放资源,而更新操作中可以通过调用JdbcUtils.close(stmt, conn)来释放资源

    封装语句创建

    首先观察一段典型的创建语句代码:

    String sql = "INSERT INTO users(username, password) VALUES(?, ?)";
    stmt = conn.prepareStatement(sql);
    stmt.setObject(1, "byx");
    stmt.setObject(2, "123456");
    

    基本流程如下:

    • 得到一条带参数的sql语句
    • 从连接获取PreparedStatement
    • 设置PreparedStatement的参数

    其中,sql语句以及参数应该由用户程序指定,而conn来自之前JdbcUtils.getConnection方法,所以可以将他们都设置为方法参数。

    JdbcUtils中添加一个createPreparedStatement方法,用于创建PreparedStatement

    public class JdbcUtils
    {
        ...
        public static PreparedStatement createPreparedStatement(Connection conn, String sql, Object... params) 
            throws SQLException
        {
            PreparedStatement stmt = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; ++i)
            {
                stmt.setObject(i + 1, params[i]);
            }
            return stmt;
        }
        ...
    }
    

    其中,conn表示数据库连接,sql表示带参数的sql字符串,params是一个不定参数数组,用于传递sql中的参数值。

    外部程序可以通过调用JdbcUtils.createPreparedStatement(conn, "XXX", ...)来创建一个PreparedStatement

    总结

    到目前为止,我们已经成功将JDBC操作中的公共代码抽取成了几个独立的函数,JDBC的使用已经得到一定的简化了

    查询操作的代码如下:

    Connection conn = null;
    PreparedStatement stmt = null;
    ResultSet rs = null;
    
    try
    {
        // 获取连接
        conn = JdbcUtils.getConnection();
    
        // 构造语句
        stmt = JdbcUtils.createPreparedStatement(conn,
                "SELECT * FROM users WHERE password = ?",
                "456");
    
        // 执行语句,获取结果集
        rs = stmt.executeQuery();
    
        // 处理结果集
        while (rs.next())
        {
            ...
        }
    }
    catch (Exception e)
    {
        // 处理异常
        ...
    }
    finally
    {
        // 释放资源
        JdbcUtils.close(rs, stmt, conn);
    }
    

    更新操作的代码如下:

    Connection conn = null;
    PreparedStatement stmt = null;
    
    try
    {
        // 获取连接
        conn = JdbcUtils.getConnection();
    
        // 构造语句
        stmt = JdbcUtils.createPreparedStatement(conn,
                "INSERT INTO users(username, password) VALUES(?, ?)",
                "byx", "123456");
    
        // 执行语句,获取影响行数
        int count = stmt.executeUpdate();
    }
    catch (Exception e)
    {
        // 处理异常
        ...
    }
    finally
    {
        // 释放资源
        JdbcUtils.close(stmt, conn);
    }
    

    但是,这样的封装还是非常浅的,因为代码中还存在着许多结构上的重复。例如,所有查询操作都需要依次进行获取连接、创建语句、处理结果集、释放资源这几个操作,其中只有处理结果集是会变化的,其他步骤对于所有查询操作来说都是相同的。同时,整个操作过程需要用try{...}catch{...}finally{...}结构包起来,这些结构上的重复无法通过提取公共方法的方式抽象出来。

    在下一篇文章中,将介绍如何对JDBC使用中出现的重复结构进行封装。

  • 相关阅读:
    No.013-Python-学习之路-Day10-事件驱动及异步IO
    No.012-Python-学习之路-Day9-GIL|Thread|Process|Coroutine
    No.011-Python-学习之路-Day8-Socket网络编程
    No.010-Python-学习之路-Day7-面向对象的进阶<类中高级方法|类创建及实例的生成过程|反射>|异常处理|动态的模块导入|断言
    Python-Modules-pygame的学习记录
    No.009-Python-学习之路-Day6-面向对象的特性及语法
    No.008-Python-学习之路-Day5-random|os|sys|shutil|shelve|xml|PyYAML|ConfigParser|hashlib|RE
    No.007-Python-学习之路-Day5-模块简介|time&datetime
    【转】git使用教程
    web调试工具
  • 原文地址:https://www.cnblogs.com/baiyuxuan/p/14329584.html
Copyright © 2011-2022 走看看