JDBC Apache—DBUtils 详解(通俗易懂)

news2025/2/13 21:26:01

目录

一、前言

二、Apache—DBUtils的引入

        1.传统使用ResultSet的缺点 : 

        2.改进方法 : 

        3.改进方法的模拟实现 : 

三、Apache—DBUtils的使用

        1.基本介绍 : 

        2.准备工作 : 

        3.DBUtils查询(DQL) : 

        4.query方法源码分析 : 

        5.DBUtils处理(DML) : 

四、总结


一、前言

  • 第六节内容,up打算和大家分享一下JDBC——ApacheDBUtils相关的内容。
  • 注意事项——代码中的注释也很重要;不要眼高手低;点击文章的侧边栏目录或者文章开头的目录可以进行跳转。
  • 良工不示人以朴,所有文章都会适时补充完善。大家如果有问题都可以在评论区进行交流或者私信up。 感谢阅读!

二、Apache—DBUtils的引入

        1.传统使用ResultSet的缺点 : 

        返回的结果集ResultSet与Connection是关联的,当调用Connection的close方法关闭连接后(放回连接池),ResultSet对象就不能用了。那么,如果出现“要求结果集复用”的需求,显然是办不到的。PS : 如果在关闭连接后仍调用ResultSet,会报异常,如下图所示 : 

        就算是先用完ResultSet,再关闭Connection,ResultSet也只是使用了一次,这种对结果集的使用方式显然不利于数据的管理(说没就没)。

        传统使用ResultSet结果集的过程中,也让人感到不爽,如下图所示:

        我们只能使用固定的格式getXxx来获取表中对应的字段,不但容易在传入形参时出纰漏,而且getInt,getString等也不符合我们“见名知意”的原则。 

        2.改进方法 : 

        之前在JDBC API详解中,我们提到——ResultSet在使用过程中,底层实际是将表中的记录存放在了ArrayLIst集合中。而我们知道,ArrayList底层其实就是一个Object类型的数组elementData。既然如此,我们为什么不把ResultSet结果集中的记录取出来,专门放到一个存放表记录的ArrayList集合中去呢?

        于是,在这种念头的鼓动下,我们想出了以下的牛逼方法(不是我想出来的😂)——

创造一个Java类用于对应一张表,该类中所有的属性对应表中的所有字段,即该类的每个对象都表示了表中的一条记录查询到表中有几条记录,就创建几个该类的实例,不同实例的属性可以自行设置。这样一来,我们只需要将该类的对象存放在ArrayList集合中,就实现了数据的“迁移”,结果集中的数据也得以复用。

        以上,这样的一个类我们称为POJO, 或者Domain,或者一个你早已听过的名字——JavaBean类。

        Apache—DBUtils就是为实现这种需求而诞生的一个工具类,它可以完美解决以上的问题。

        3.改进方法的模拟实现 : 

                在使用Apache—DBUtils工具类之前,我们先简单模拟一下该方法。遵循上文“改进方法”中的思想。我们试着来查询fruit表,fruit表如下 : 

                Fruit的JavaBean类如下 : 

package apache_dbutils;

/**
    MySQL的字段类型与JavaBean类属性的对应关系————
    (1) 数值型——>包装类(允许为null)
    (2) 字符串型——>String类
    (3) 日期类型——>Date类
 */
public class Fruit {
    private int id;
    private String name;
    private int sweetness;

    public Fruit() {
    }
    public Fruit(int id, String name, int sweetness) {
        this.id = id;
        this.name = name;
        this.sweetness = sweetness;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSweetness() {
        return sweetness;
    }

    public void setSweetness(int sweetness) {
        this.sweetness = sweetness;
    }

    @Override
    public String toString() {
        return "\nFruit{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sweetness=" + sweetness + 
                "};";
    }
}

                接着我们在测试类中试着将Fruit类对象封装到ArrayList集合中,up以Advanced_Demo类为演示类,代码如下 : 

package apache_dbutils;

import connection_pool.druid.JDBCUtilsDruid;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class Advanced_Demo {
    public static void main(String[] args) {
//JDBC核心四部曲
    //1.注册驱动(底层根据druid.properties配置文件自动注册了驱动)
        //Class.forName("com.mysql.cj.jdbc.Driver");

    //2.获取连接
        Connection connection = null;

    //3.执行SQL
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        ArrayList<Fruit> fruitArrayList = new ArrayList<>();
        String sql = "SELECT * FROM fruit " +
                        "WHERE `id` >= ?;";

        try {
            connection = JDBCUtilsDruid.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setInt(1, 0);

            resultSet = preparedStatement.executeQuery();
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("name");
                int sweetness = resultSet.getInt("sweetness");

                fruitArrayList.add(new Fruit(id, name, sweetness));
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
    //4.释放资源
            JDBCUtilsDruid.close(resultSet, preparedStatement, connection);
        }

        //关闭Connection后,轻松调用集合
        System.out.println("List = " + fruitArrayList);
        System.out.println("===================================");

        for (int i = 0; i < fruitArrayList.toArray().length; i++) {
            System.out.print("id = " + fruitArrayList.get(i).getId());
            System.out.print("\tname = " + fruitArrayList.get(i).getName());
            System.out.println();
        }
        /*
            在某些情况下,我们甚至可以返回该集合其他调用者使用。
            eg : return fruitArrayList;
         */
    }
}

                运行结果 : 


三、Apache—DBUtils的使用

        1.基本介绍 : 

        commons-dbutils是Apache组织提供的一个开源的JDBC工具类库,它是对JDBC的封装,使用dbutils能极大简化JDBC编程的工作量。

        dbutils中常用的类和接口如下——

  • QueryRunner类 : 该类封装了SQL的执行,并且是线程安全的;可以实现增删查改,并且支持批处理。
  • ResultSetHandler接口 : 该接口用于处理java.sql.ResultSet,将数据按照要求转换为另一种格式。常见实现类如下 :         
    1. ArrayHandler : 将结果集中的第一行数据转换成对象数组。
    2. ArrayListHandler : 将结果集中的每一行数据转换成对象数组,再存入List中。
    3. BeanHandler : 将结果集中的第一行数据封装到一个对应的JavaBean实例中(适用于返回单条记录的情况)。
    4. BeanListHandler : 将结果集中的每一行数据都封装到对应的JavaBean实例中,再存放到List集合中。
    5. ColumnListHandler : 将结果集中某一列的数据存放到List中。
    6. KeyedHandler(name) : 将结果集中的每行数据都封装到Map里,然后将所有的map再单独存放到一个map中,其key为指定的key。
    7. MapHandler : 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值。
    8. MapListHandler : 将结果集中的每一行数据都封装到Map里,再存入List。
    9. ScalarHandler : 将结果集中的一列映射为一个Object对象,适用于返回单行单列的情况。

        2.准备工作 : 

                先去谷歌搜一下commons-dbutils-jar,进入官网下载Apache_DBUtils的jar包,如下图所示 : 

                注意要选择Binaries,而不是Source,否则你下载的文件中没有jar包。
                接着还是老规矩,将jar包复制到IDEA中存放jar包的目录下,然后导入当前项目,如下图所示 : 

        3.DBUtils查询(DQL) : 

                仍然操作fruit表,如下所示 : 

                up以ApacheDBUtils_Demo1类为演示类,代码如下 : (注意看注释)

package apache_dbutils;

import connection_pool.druid.JDBCUtilsDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;

/**
 * @author : Cyan_RA9
 * @version : 21.0
 */
public class ApacheDBUtils_Demo1 {
    public static void main(String[] args) throws SQLException {
    //核心四部曲
        //1.注册驱动(根据druid.properties配置文件自动注册)
        //Class.forName("com.mysql.cj.jdbc.Driver");

        //2.获取连接
        Connection connection = null;
        connection = JDBCUtilsDruid.getConnection();

        //3.执行SQL
        QueryRunner queryRunner = new QueryRunner();
        String sql = "SELECT * FROM fruit WHERE `id` >= ?;";

        List<Fruit> query = queryRunner.query(connection, sql, new BeanListHandler<>(Fruit.class), 2);
        /*
            query方法复制执行SQL, 并将得到的结果集封装到ArrayList集合中, 然后返回;
            PS : 底层得到的ResultSet结果集以及PreparedStatement会在query方法中自动关闭。
            方法参数解读————
            (1)connection : 当前使用的连接
            (2)sql : 需要被执行的SQL
            (3)new BeanListHandler<>(Fruit.class) : 底层通过反射机制获取到Fruit类的信息;
                将ResultSet结果集中的每一条记录都封装到一个Fruit对象中,最后将这些对象
                封装到ArrayList集合中。
            (4)2 : 可变参数,传给sql中的?
         */

        System.out.println("query = " + query);

        //4.释放资源
        JDBCUtilsDruid.close(null, null, connection);
    }
}

                运行结果 : 

        4.query方法源码分析 : 

                我们在query方法处下一个断点,如下图所示 : 

                接着进入Debug。我们先跳入new BeanListHandler<>(Fruit.class)看看,如下所示 :    

                一目了然。上文“Apahce—DBUtils”的基本介绍中,我们说过,ResultSetHandler接口用于处理ResultSet结果集,这不,BeanListHandler实现了该接口。
                并且,我们在query方法的形参列表new一个BeanListHandler时,是传入了一个Fruit类对应的Class对象(Fruit就是fruit表对应的JavaBean类),这个Class对象,最后会传递给BeanListHandler类的type成员变量,这么一来,BeanListHandler就可以通过反射get到了Fruit类的信息了。

                继续,我们回去,跳入query方法,如下 : 

                首次跳入其实是对我们可变参数中的2进行装箱,不管他。重新跳入query,如下 : 

                经典“包皮”结构,可以看到,我们传入的四个形参都历历在目,并且可以直观的看到最后的可变参数。 但是,这个"this.query(...)",又是在干嘛呢?不着急,我们可以发现,新调用的query方法无非就是多传入了一个false布尔变量而已。直接跳进去看看,如下图所示 : 

                这个query看似复杂,其实逻辑非常清楚,一开始就是一大串判断——连接是不是为空呀?要执行的sql是不是为空呀?传入的ResultSetHandler对象是不是为空呀?
                只要有一个环节出了问题,就给你抛出一个异常,并打印出相关信息。当然了,眼尖的小伙伴儿还会发现其中有两个if (closeConn)的判断,如果成立会关闭连接(放回连接池),这个"closeConn"其实就是之前多出来的那个false布尔变量,那肯定就不进入if语句了呗。

                我们继续往下看,最下面的else语句才是真正要完成执行SQL的操作滴。如所示 : 

        else {
            PreparedStatement stmt = null;
            ResultSet rs = null;
            T result = null;

            try {
                stmt = this.prepareStatement(conn, sql);
                this.fillStatement(stmt, params);
                rs = this.wrap(stmt.executeQuery());
                result = rsh.handle(rs);
            } catch (SQLException var33) {
                this.rethrow(var33, sql, params);
            } finally {
                try {
                    this.close(rs);
                } finally {
                    this.close(stmt);
                    if (closeConn) {
                        this.close(conn);
                    }
                }
            }
            return result;
        }

                噢,我们心心念念已久的PreparedStatement和ResultSet总算出现辽。可以看到,源码中也是先将对象置为null,之后才初始化的。值得一提的是这个result,直接告诉你结论,这个result就是query方法最终要返回的ArrayList类型的集合。
                继续,这里的prepareStatement方法传入了conn和sql两个实参,其实追进去会发现还是原来那一套——通过连接来获取prepareStatement对象,如下图所示 : 

                而fillStatement就是用你传入的可变参数来给sql中的?赋值

                再往下就是通过PreparedStatement对象来执行sql了,此处返回的是结果集。

                紧接着是handle方法执行,可以肯定的是,handle方法最终返回的一定是ArrayList类型的对象。 我们追进去handle方法看看,如下图所示 : 

                又是经典包皮结构。别急,我们的目的很明确——找到return ArrayList的语句。继续往下追,如下图所示 : 

                双层包皮结构?再往下追!如下 : 

                诺,总算给我们找到了,的确是new了一个ArrayList类型的对象,并且最后给返回了。那么经过层层返回,最后这个ArrayList对象就会被赋值给query方法中的result,所以query方法最终也是返回了ArrayList类型的对象。 
                最后,query方法的finally语句中自动关闭了ResultSet和PreparedStatement,如下图所示 : 

        5.DBUtils处理(DML) : 

                这次我们来操作学生表stus(id为自增长主键),如下图所示 : 

                现要求——
                ①向表中新插入一条“id = 6;name = 'Carl';sex = 'male';score=499”的记录;
                ②修改name = Five的记录,令其score = 433;
                ③删除id = 5的记录。

                up以ApacheDBUtils_Demo2类为演示类,代码如下 : 

package apache_dbutils;

import connection_pool.druid.JDBCUtilsDruid;
import org.apache.commons.dbutils.QueryRunner;

import java.sql.Connection;
import java.sql.SQLException;

public class ApacheDBUtils_Demo2 {
    public static void main(String[] args) throws SQLException {
    //JDBC核心四部曲
        //1.注册驱动(底层会根据druid.properties配置文件中的信息自动注册驱动)
        //Class.forName("com.mysql.cj.jdbc.Driver");

        //2.获取连接
        Connection connection = null;
        connection = JDBCUtilsDruid.getConnection();

        //3.执行SQL
        String sql1 = "INSERT INTO stus " +
                            "VALUES " +
                            "(NULL,?,?,?);";
        String sql2 = "UPDATE stus " +
                            "SET score = ? " +
                            "WHERE `name` = ?";
        String sql3 = "DELETE FROM stus " +
                            "WHERE id = ?";
        QueryRunner queryRunner = new QueryRunner();
        int affectedRows1 = queryRunner.update(connection, sql1, "Carl", "male", 499.0);
        int affectedRows2 = queryRunner.update(connection, sql2, 433.0,"Five");
        int affectedRows3 = queryRunner.update(connection, sql3, 5);

        System.out.println("INSERT语句执行成功了吗?" + (affectedRows1 > 0 ? "Yes!" : "No!"));
        System.out.println("UPDATE语句执行成功了吗?" + (affectedRows2 > 0 ? "Yes!" : "No!"));
        System.out.println("DELETE语句执行成功了吗?" + (affectedRows3 > 0 ? "Yes!" : "No!"));

        //4.释放资源
        JDBCUtilsDruid.close(null,null,connection);
    }
}

                运行结果 : 

                查询一下stus表,如下 : 

                修改成功!


四、总结

  • 🆗,以上就是JDBC系列博文第六节的全部内容了。
  • 总结一下,我们从"数据管理"和“结果复用”两个角度分析了传统使用ResultSet的弊端,从而引出了本节要学习的ApacheDBUtils工具类,该工具类可以将结果集中的数据转移到表对应的JavaBean类的实例中,然后将多个实例封装到集合中,便可以达到“复用结果集”的目的。大家需要掌握QueryRunner中query方法和update方法的使用,以及熟悉ResultSetHandler接口的几个常用实现类。
  • 下一节内容——JDBC BasicDAO,我们不见不散。感谢阅读!

        System.out.println("END-----------------------------------------------------------------------------"); 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/645439.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web3下的去中心化契约

随着Web3的兴起&#xff0c;智能合约成为了这一新兴领域中最为重要的概念之一。智能合约是一种在区块链上执行的可编程代码&#xff0c;其作用类似于传统世界中的合约&#xff0c;但具有更多的灵活性和安全性。本文将介绍智能合约的基本概念、工作原理以及在Web3下的应用场景。…

如何提取视频里面的音频?简单三个方法即可完成!

分享3个简单易上手的视频提取音频方法&#xff0c;这些方法可以帮助你单独提取保存视频中的人物对话音频内容和背景音乐&#xff0c;并且提取成功的音频文件还可用于其他视频创作。 方法一&#xff1a;PR提取音频 Adobe Premiere Pro&#xff08;简称PR&#xff09;不仅可以用…

番外篇2 离线服务器 环境安装与配置

&#xff08;离线远程服务器旧版torch的卸载与安装问题&#xff09; Step4: 查看自己是否已经成功安装了Anaconda,输入此命令conda --version -------------------------------------------------------------------------------------------------------- Step1:离线创建con…

为什么浏览器突然打不开网页了?

苏生不惑第433 篇原创文章&#xff0c;将本公众号设为星标&#xff0c;第一时间看最新文章。 前几天写了什么是cookie总有人问我 Cookie 是什么&#xff1f;&#xff0c;说到Charles代理工具&#xff0c;但是为什么关了它就打不开网页呢&#xff1f;今天来说说这个。 Charles可…

企企通联合创始人兼总架构师杨华:剖析SRM顾问长期主义项目实践

近日&#xff0c;国产大飞机C919商业首飞成功引起广泛关注&#xff0c;此后&#xff0c;我们的出行选择中新增了一项“自己国家的大飞机”&#xff0c;给国人带来了更多的期待和自豪。 走难而正确的路&#xff0c;国产大飞机C919从项目立项到“一飞冲天”&#xff0c;花了十六年…

激活函数ReLU和SiLU的区别

文章目录 前言ReLU&#xff08;Rectified Linear Unit&#xff09;Leaky ReLUFReLU&#xff08;Flatten ReLU&#xff09;SiLU&#xff08;Sigmoid Linear Unit&#xff09;总结 前言 在这里&#xff0c;我就简单写一下两个激活函数的概念以及区别&#xff0c;详细的过程可以看…

2023年新风口,抖音的产业带服务商招募?怎么开通?

抖音电商致力于成为用户发现并获得优价好物的首选平台。众多抖音创作者通过短视频/直播等丰富的内容形式&#xff0c;给用户提供更个性化、更生动、更高效的消费体验。抖音电商积极引入优质合作伙伴&#xff0c;为品牌发展、商家变现提供多元的服务。 抖音产业带服务商招募区域…

操作系统常识

4.进程同步 1.什么是临界区&#xff1f;什么是临界资源 在计算机系统中&#xff0c;临界资源指的是被多个并发执行的线程或进程共享访问的某个资源&#xff0c;如共享内存区、共享文件等。 临界区指的是访问临界资源的那部分代码片段&#xff0c;它是一段需要保护的代码区域…

项目管理工具究竟能否提升效率?看看它们的作用和优势

随着各种类型的项目不断涌现&#xff0c;项目管理工具在现代社会变得越来越重要。作为一个项目经理&#xff0c;需要确保项目在时间和预算方面得到控制&#xff0c;并且达成预期的效果。在这个过程中&#xff0c;项目管理工具可以带来很多帮助。 首先 项目管理工具可以为项目经…

推动绿色计算 共迎绿色未来|2023开放原子全球开源峰会绿色基础设施技术分论坛圆满收官

6 月 11 日&#xff0c;2023 开放原子全球开源峰会绿色基础设施技术分论坛圆满举行。蚂蚁集团 4 位专家带来了蚂蚁在探索打磨“绿色计算”实践中的核心技术领域一线观察。 蚂蚁集团高级技术专家、数据中间件负责人李玉明 李玉明分享了《开源分布式事务框架 Seata 以及其在金融…

电商--抢购架构总结

文章目录 背景业务流程业务难点技术难点技术方案技术方向具体落地客户端流控网关流控容器流控后端接口流控数据库流控 流控总结优化读取加速异步化流程处理系统扩容 压测监控 总结参考文献 背景 这是个在做NFT电商项目时遇到的场景&#xff0c;要求运营可以商家某个系列的NFT商…

顺序栈与链栈

简介 栈和队列是两种重要的线性结构。从数据结构角度看&#xff0c; 栈和队列也是线性表&#xff0c; 其特殊性在于栈和队列的基本操作是线性表操作的子集&#xff0c; 它们是操作受限的线性表。 栈 (stack) 是限定仅在表尾进行插入或删除操作的线性表。 因此&#xff0c; 对…

计算字母出现次数【存在括号计算】

计算字母出现次数【存在括号计算】 此代码考虑到了本问题的大多可能情况&#xff0c;闲话少述&#xff0c;代码中的注释很丰富。 代码绝对可以解决你的问题&#xff01; 不行你就评论&#xff0c;回复速度超快 作者时间YaoChongChong2023年6月14日10&#xff1a;40 Descript…

T8151B T8310 T8311罗克韦尔自动化可信通信接口

​ T8151B T8310 T8311罗克韦尔自动化可信通信接口 T8151B T8310 T8311罗克韦尔自动化可信通信接口 DCS控制器正反作用怎么判断&#xff1f; dcs控制器的正反作用可以在工程师站更改&#xff0c;比如中控系统那就在工程师站的操作界面把控制系统上的调节阀位号点开就会有正反作…

【华为云分布式消息服务RocketMQ】

MD[华为云分布式消息服务RocketMQ] 华为云分布式消息服务RocketMQ,使用指南 说明1&#xff1a;华为云rocketmq默认是集群4.8版本&#xff0c;而非单机版。 说明2&#xff1a;华为云rocketmq兼容性较好&#xff0c;一般不需要进行SDK改造。 1.创建/购买分布式消息服务RocketM…

水文水动力模型在城市内涝、城市排水、海绵城市规划设计中教程

详情点击链接&#xff1a;水文水动力模型在城市内涝、城市排水、海绵城市规划设计中应用教程 一&#xff0c;CAD、GIS水力建模过程 1.1复杂城市排水管网系统快速建模&#xff1a;通过标准化的步骤&#xff0c;利用CAD数据、GIS数据建立SWMM模型。在建模的不同阶段发挥不同软…

生成AI(三)—创建自己的MidJorney

背景&#xff1a;MidJorney是面向互联网的图像AIGC产品&#xff0c;在政企内部&#xff0c;存在大量需求训练内部的知识作为自己的AIGC工具。基本需求是信息安全考虑&#xff0c;合规考虑。 目标&#xff1a;通过自准备的数据训练MidJorney同类模型&#xff0c;成为私有化部署…

【基于容器的部署、扩展和管理】3.9 云原生容器的安全性和合规性

往期回顾&#xff1a; 第一章&#xff1a;【云原生概念和技术】 第二章&#xff1a;【容器化应用程序设计和开发】 第三章&#xff1a;【3.1 容器编排系统和Kubernetes集群的构建】 第三章&#xff1a;【3.2 基于容器的应用程序部署和升级】 第三章&#xff1a;【3.3 自动…

关于Android的帧动画,补间动画,属性动画的使用和总结。(附源码)

说明&#xff1a;内容有点多&#xff0c;可以分块阅读&#xff0c;后续可能会拆分为三讲 一. Android的动画总结 一 . 帧动画 帧动画其实就是通过连续播放图片来模拟动画效果 以下是俩种实现方式&#xff1a; 1. xml文件的方式 首先在drawable下建立animation_lufi.xml <?…

视频剪辑需要学哪些软件 视频剪辑在哪里学

视频剪辑涉及到素材准备、视频的编辑与生成、格式的转换等方面&#xff0c;因此需要学习到的软件类型也不少。比如在准备素材时&#xff0c;可能会涉及到音频、图片等处理&#xff0c;以及特效的应用等。接下来&#xff0c;就让我们详细了解下视频剪辑需要学哪些软件&#xff0…