JDBC BasicDAO详解(通俗易懂)

简介: JDBC 第七节 详解BasicDAO 通俗易懂!

目录

一、前言

二、BasicDAO的引入

       1.为什么需要BasicDAO?

       2.BasicDAO示意图 :

三、BasicDAO的分析

       1.基本说明 :

       2.简单设计 :

四、BasicDAO的实现

       0.准备工作 :

       1.工具类 :

       2.JavaBean类 :

       3.BasicDAO类 / StusDAO类 :

       4.测试类 :

五、总结


一、前言

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

    二、BasicDAO的引入

           1.为什么需要BasicDAO?

           在传统JDBC程序的基础上,为了优化连接,我们引入了连接池的概念,并且学习了Druid(德鲁伊)连接池的使用;而在此基础上,为了优化对结果集的操作,我们又引入了ApacheDBUtils工具类。靠着DBUtils + Druid,JDBC程序的编写流程和效率得到了极大优化。

           但是,即便如此,"DBUtils + Druid"依然存在了一些不可忽视的问题。如下——

           在JDBC四部曲的“执行SQL”步骤中,SQL是已经写死的,要操作的表和字段均已固定,而不能通过参数传入,通用性差

           对于DQL,如果有返回结果集的需要,返回值类型是无法确定的,需要使用泛型

           很多情况下,需要同时操作多张表业务需求复杂,不可能只靠一个Java类来实现

           2.BasicDAO示意图 :

                   示意图如下 : (从下往上看!

    image.gif编辑

                   初看时可能会感觉这张图复杂,这里稍微解读一下。

                   先看最下面,这一块区域 : (Domain, POJO, JavaBean)

    image.gif编辑

                   在上一小节DBUtils详解中我们说过这个JavaBean,就是把每个要操作的表都对应一个JavaBean类,类中的属性是表中字段的映射。这么做是为了将来把多个保存了表数据信息的JavaBean实例封装到一个集合中,便于数据的管理和复用。

                  再看中间那一部分 : (DAO)

    image.gif编辑

                   其实这里的DAO就是指“数据访问对象”(Data Access Object),当然我们后面是会讲到的。 DAO的设计理念是为每一张要操作的表都单独设置一个对应的DAO,以完成对该表的CRUD操作,不同DAO只完成它对应那张表的操作,以达到“各司其职,各尽其责”的效果,使表的操作更具有针对性,业务更清晰。

                   由于不同的DAO都存在共有的操作,例如获取连接,释放资源。因此根据OOP的设计理念,我们将这些共有的操作单独再封装到一个DAO中,也就是BasicDAO了,然后让操作表的DAO去继承BasicDAO。

                   再看最上面,最右上方这一块区域 : (应用区)

    image.gif编辑

                   有了DAO之后,我们想访问那张表,直接去操作该表对应的DAO即可(复读机)。

                   当然,up画的示意图中只划分了三部分,因为咱们还是基础入门阶段,实际上可能会有5个,6个甚至更多部分(比如业务层,界面层等等),大家了解一下即可。


    三、BasicDAO的分析

           1.基本说明 :

           DAO(Data Access Object),指数据访问对象,用于完成对表中数据的访问。

           BasicDAO作为这样的一个通用类,专门用于和数据库交互,即完成对数据库中表的crud操作;在BasicDAO的基础上,可以实现一张表对应一个DAO,以更好地完成功能。

           eg : employee表 <=> Employee.java类(JavaBean)<=> EmployeeDAO.java。

           2.简单设计 :

           如果我们想设计并使用一个BasicDAO,可以在同一包下建多个子包,不同的子包存放不同功能的类或接口,此处我们只建立4个子包,up以src目录下的dao_ex包为演示包,如下图所示 :

    image.gif 编辑

           在dao包下分别建立domain, dao, utils, test四个子包,它们的功能如下——

           dao_ex.domain : 用于存放相关JavaBean类;

           dao_ex.dao : 用于存放XxxDAO 和 BasicDAO;

           dao_ex.utils : 用于存放相关工具类;

           dao_ex.test : 用于存放DAO的测试类。

           建立子包情况如下图所示 :

    image.gif 编辑

    四、BasicDAO的实现

           0.准备工作 :

                   up就以stus表为例,如下图所示 :

    image.gif编辑

                   我们来逐个完成stus表对应的工具类,JavaBean类,DAO类和测试类。

           1.工具类 :

                   此处的“工具类”其实就是我们之前在“连接池”小节中讲过的德鲁伊连接池的工具类,我们直接拿来用就行。

                   工具类JDBCUtilsDruid类,代码如下 :

    package dao_ex.utils;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import javax.sql.DataSource;
    import java.io.FileInputStream;
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    import java.util.Properties;
    public class JDBCUtilsDruid {
        private static DataSource dataSource;
        static {
            Properties properties = new Properties();
            try {
                properties.load(new FileInputStream("src\\druid.properties"));
                dataSource = DruidDataSourceFactory.createDataSource(properties);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        //获取连接
        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
        //释放资源
        public static void close(ResultSet resultSet, Statement statement, Connection connection) {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (statement != null) {
                    statement.close();
                }
                if (connection != null) {
                    connection.close();
                }
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }

    image.gif

          2.JavaBean类 :

                   对应于stus表中的id,name,sex,score四个字段,我们可以创建其对应的JavaBean类Stus类,并在其中建立四个字段,并给出它们的getter和setter方法,同时重写toString方法。

                   Stus类代码如下 :

    package dao_ex.domain;
    /**
        stus表对应的JavaBean类
     */
    public class Stus {
        private Integer id;
        private String name;
        private String sex;
        private Double score;
        public Stus() {
        }
        public Stus(Integer id,String name,String sex,Double score) {
            this.id = id;
            this.name = name;
            this.sex = sex;
            this.score = score;
        }
        public Integer getId() {
            return id;
        }
        public void setId(Integer id) {
            this.id = id;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = sex;
        }
        public Double getScore() {
            return score;
        }
        public void setScore(Double score) {
            this.score = score;
        }
        @Override
        public String toString() {
            return "Stus{" +
                    "id = " + id +
                    ", name '= " + name + '\'' +
                    ", sex = '" + sex + '\'' +
                    ", score = " + score +
                    '}';
        }
    }

    image.gif

           3.BasicDAO类 / StusDAO类 :

                   因为XxxDAO类是在BasicDAO类的基础上实现的,因此我们要先开发BasicDAO。

                   BasicDAO类代码如下 : (注意看注释!

    package dao_ex.dao;
    import connection_pool.druid.JDBCUtilsDruid;
    import org.apache.commons.dbutils.QueryRunner;
    import org.apache.commons.dbutils.handlers.BeanHandler;
    import org.apache.commons.dbutils.handlers.BeanListHandler;
    import org.apache.commons.dbutils.handlers.ScalarHandler;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.List;
    /**
        (1)因为不确定将来会操作哪张表的DAO类,因此BasicDAO要使用泛型。
        (2)设置形参后,可以指定地传入一个要执行的SQL.
        (3)注意:实现不同功能DQL的不同方法内,调用query方法时,传入的ResultSetHandle接口的实现类不同。
     */
    public class BasicDAO<T> {
        //定义一个QueryRunner属性
        private QueryRunner queryRunner = new QueryRunner();
        //1.执行DML的方法
        /**
         * @param sql : 要执行的SQL
         * @param parameters : 为SQL中的?进行赋值的可变参数
         * @return : 返回受影响的行数
         */
        public int update(String sql, Object... parameters) {
            //获取链接
            Connection connection = null;
            try {
                connection = JDBCUtilsDruid.getConnection();
                int affectedRows = queryRunner.update(connection,sql,parameters);
                return affectedRows;
            } catch (SQLException e) {
                throw new RuntimeException(e);  //编译异常转换为运行异常
            } finally {
            //释放资源
                JDBCUtilsDruid.close(null, null, connection);
            }
        }
        //2.执行“返回多条记录的”DQL的方法(同样使用泛型)
        /**
         * @param sql : 要执行的SQL
         * @param clazz : 根据传入的Class对象类型进行反射,
         *                以确定对应的JavaBean实例类型,即List集合中存放什么类型。
         * @param parameters : 为SQL中的?赋值的可变参数
         * @return : 返回一个ArrayList类对象(eg : 返回了一个存放Stus实例的ArrayList集合)
         */
        public List<T> queryMultiply(String sql, Class<T> clazz, Object... parameters) {
            //获取连接
            Connection connection = null;
            try {
                connection = JDBCUtilsDruid.getConnection();
                List<T> query = queryRunner.query(connection,sql,new BeanListHandler<T>(clazz),parameters);
                return query;
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
            //释放资源
                JDBCUtilsDruid.close(null,null,connection);
            }
        }
        //3.执行“返回单条记录的”DQL的方法
        /**
         * @param sql : 同上
         * @param clazz : 同上,但只是作为单个实例来接收表中的一行数据(一条记录),没有集合
         * @param parameters : 同上
         * @return : 返回一个对应的JavaBean实例(该实例的属性保存了表中某一条记录的全部信息)
         */
        public T querySingle(String sql, Class<T> clazz, Object... parameters) {
            //获取连接
            Connection connection = null;
            try {
                connection = JDBCUtilsDruid.getConnection();
                return queryRunner.query(connection, sql, new BeanHandler<T>(clazz), parameters);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
            //释放资源
                JDBCUtilsDruid.close(null,null,connection);
            }
        }
        //4.执行“返回单行单列的”DQL的方法(即返回单值)
        /**
         * @param sql : 同上
         * @param parameters : 同上
         * @return : 由于仅返回单行单列,其实就是某一个具体的值,因此只需要使用Object类型做接受即可。
         */
        public Object queryScalar(String sql, Object... parameters) {
            //获取连接
            Connection connection = null;
            try {
                connection = JDBCUtilsDruid.getConnection();
                return queryRunner.query(connection,sql, new ScalarHandler(), parameters);
            } catch (SQLException e) {
                throw new RuntimeException(e);
            } finally {
            //释放资源
                JDBCUtilsDruid.close(null,null,connection);
            }
        }
    }

    image.gif

                   BasicDAO开发完毕后,我们可以创建负责stus表操作的StusDAO,并令其去继承BasicDAO。StusDAO代码如下 :

    package dao_ex.dao;
    import dao_ex.domain.Stus;
    /**
        StusDAO————负责管理stus表的DAO
     */
    public class StusDAO extends BasicDAO<Stus> {
        /*
            StusDAO继承BasicDAO之后,便拥有了BasicDAO的全部非私有成员。
            若业务需求复杂,也可以在StusDAO下自定义特有方法。
         */
    }

    image.gif

          4.测试类 :

                   在test包下创建用于测试DAO的TestDAO类,在TestDAO类中完成对StusDAO的访问。

                   TestDAO类代码如下 :

    package dao_ex.test;
    import dao_ex.dao.StusDAO;
    import dao_ex.domain.Stus;
    import org.testng.annotations.Test;
    import java.util.List;
    /**
        (1)关闭连接的操作封装在了BasicDAO的方法中;
        (2)关闭PreparedStatement和ResultSet的操作封装在了query方法中;
     */
    public class TestDAO {
        @Test
        public void testStusDAO() {
        //1.测试“返回多条记录”的DQL的执行
            String sql1 = "SELECT * FROM stus " +
                            "WHERE `id` >= ?;";
            StusDAO stusDAO = new StusDAO();
            List<Stus> stusData = stusDAO.queryMultiply(sql1, Stus.class, 1);
            for (Stus stusDatum : stusData) {
                System.out.println(stusDatum);
            }
            System.out.println("------------------------------------------------");
        //2.测试“返回单条记录”的DQL的执行
            String sql2 = "SELECT * FROM stus " +
                            "WHERE `id` = ?;";
            Stus stus = stusDAO.querySingle(sql2, Stus.class, 1);
            System.out.println(stus);
            System.out.println("------------------------------------------------");
        //3.测试“返回单行单列(即返回单值)”的DQL的执行
            String sql3 = "SELECT name FROM stus " +
                            "WHERE `id` = ?;";
            Object o = stusDAO.queryScalar(sql3, 1);
            System.out.println(o);
            System.out.println("------------------------------------------------");
        //4.测试DML的执行(INSERT, DELETE, UPDATE)
            String sql4 = "INSERT INTO stus " +
                                "VALUES " +
                                "(NULL, ?, ?, ?);";
            String sql5 = "UPDATE stus " +
                                "SET `name` = ? " +
                                "WHERE `name` = ?;";
            String sql6 = "DELETE FROM stus " +
                                "WHERE `id` = ?;";
            int update1 = stusDAO.update(sql4, "Wood", "male", 399.0);
            int update2 = stusDAO.update(sql5, "Karry", "Cyan");
            int update3 = stusDAO.update(sql6, 4);
            System.out.println("插入操作的DML执行成功了吗?" + (update1 > 0 ? "yes!" : "no!"));
            System.out.println("修改操作的DML执行成功了吗?" + (update2 > 0 ? "yes!" : "no!"));
            System.out.println("删除操作的DML执行成功了吗?" + (update3 > 0 ? "yes!" : "no!"));
        }
    }

    image.gif

                   运行结果 :

    image.gif编辑

                   我们再来查询一下stus表,看看DML操作有没有对stus造成影响,如下图所示 :

    image.gif编辑

                   OK,没有问题。


    五、总结

      • 🆗,以上就是JDBC系列博文第七节的全部内容了。
      • 总结一下,我们先是围绕“德鲁伊连接池 + 工具类”存在的弊端——即“SQL的优化”引入了DAO的概念;接着,又画出了BasicDAO的基本示意图;然后,进行了BasicDAO的基本设计,主要分为DAO, Domain, Utils, Test四部分;最后,我们以stus表为例,演示了BasicDAO的具体使用。大家一定要掌握BasicDAO的基本示意图,以及BasicDAO的设计逻辑。
      • 下一节内容——JDBC 望舒客栈项目,我们不见不散。感谢阅读!              

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

      目录
      相关文章
      |
      SQL 安全 Java
      JDBC Apache—DBUtils 详解(通俗易懂)
      JDBC 第六节 ApacheDBUtils详解!
      190 0
      |
      监控 druid Java
      JDBC 连接池 详解(通俗易懂)
      JDBC 第五节 详解连接池 通俗易懂!
      391 0
      |
      SQL Java 关系型数据库
      JDBC 事务和批处理 详解(通俗易懂)
      JDBC 第四节 事务和批处理详解!
      224 0
      |
      SQL Java 数据库连接
      JDBC Utils 详解(通俗易懂)
      JDBC 第三节 Utils工具类详解!
      64 0
      |
      SQL 存储 Java
      JDBC API 万字详解(通俗易懂)
      JDBC 第二节 详解API 通俗易懂!
      270 0
      |
      SQL Oracle Java
      JDBC 拾枝杂谈—入门篇(通俗易懂)
      JDBC 第一节 拾枝杂谈 带你快速入门!
      127 0
      |
      2月前
      |
      Java 关系型数据库 数据库连接
      实时计算 Flink版操作报错之在使用JDBC连接MySQL数据库时遇到报错,识别不到jdbc了,怎么解决
      在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
      |
      2月前
      |
      Java 关系型数据库 MySQL
      JDBC实现往MySQL插入百万级数据
      JDBC实现往MySQL插入百万级数据
      |
      2月前
      |
      SQL 关系型数据库 MySQL
      Spring_jdbc数据连接池(mysql实现增、删、改、查)
      Spring_jdbc数据连接池(mysql实现增、删、改、查)
      32 0
      |
      20天前
      |
      Java 关系型数据库 MySQL
      使用MySQL JDBC连接数据库
      使用MySQL JDBC连接数据库