JDBC BasicDAO详解(通俗易懂)

举报
Cyan_RA9 发表于 2023/07/16 12:15:07 2023/07/16
【摘要】 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示意图 : 

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

编辑

                初看时可能会感觉这张图复杂,这里稍微解读一下。
                先看最下面,这一块区域 : (Domain, POJO, JavaBean)

编辑

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

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

编辑

                其实这里的DAO就是指“数据访问对象”(Data Access Object),当然我们后面是会讲到的。 DAO的设计理念是为每一张要操作的表都单独设置一个对应的DAO,以完成对该表的CRUD操作,不同DAO只完成它对应那张表的操作,以达到“各司其职,各尽其责”的效果,使表的操作更具有针对性,业务更清晰。
                由于不同的DAO都存在共有的操作,例如获取连接,释放资源。因此根据OOP的设计理念,我们将这些共有的操作单独再封装到一个DAO中,也就是BasicDAO了,然后让操作表的DAO去继承BasicDAO。

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

编辑

                有了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包为演示包,如下图所示 : 

编辑

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

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

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

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

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

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

编辑

四、BasicDAO的实现

        0.准备工作 : 

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

编辑

                我们来逐个完成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);
        }
    }
}

        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 +
                '}';
    }
}

        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);
        }
    }
}

                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下自定义特有方法。
     */
}

        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!"));
    }
}

                运行结果 : 

编辑

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

编辑

                OK,没有问题。


五、总结

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

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

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。