【JavaEE基础学习打卡07】JDBC之应用分层设计浅尝!


前言

📜 本系列教程适用于JavaWeb初学者、爱好者,小白白。我们的天赋并不高,可贵在努力,坚持不放弃。坚信量最终引发质变,厚积薄发。
🚀 文中白话居多,尽量以小白视角呈现,帮助大家快速入门。
🎅 我是 蜗牛老师,之前网名是 Ongoing蜗牛,人如其名,干啥都慢,所以更新也慢。希望大家多多支持,让我动力十足!

本篇文章我们将初步了解实际的项目开发,了解应用分层,把握项目代码结构。介绍的应用分层也是目前广泛使用的一个结构,本次学习会实现其中的实体层和数据访问层(DAO 层),了解 O/R 映射概念。那么在之后的实际项目中这两层也是必备的基础结构。同时也可以对 JDBC 编程进一步巩固复习。


一、简单说说应用分层

应用分层其实是一种软件设计模式,它会将一个应用分解为多个层次结构,每个层次结构都自己特定的职责。分层设计模式主要是为了提高代码的可重用性、可维护性和可测试性。

比如一个小型公司和一个中型公司。小公司很多时候都是一个人身兼数职,即负责前台接待,也负责招聘、培训等事项,这就导致业务不专精、效率低下,好处就是节约了成本。那么一个中型公司呢?通常会将不同的职责划分给不同的部门或职位,比如销售部负责销售业务,人力资源部负责招聘、培训等人力资源管理工作。这样可以使得各个部门或职位各司其职,提高工作效率。

生活中很多行为都类似于应用分层,即将一个整体分开,使得各个部分能够各司其职,提高效率和便利性。

对于应用程序来说,也并非一定要分层,但是经过了无数前辈踩坑,总结得出的经验来看,分层的优势更多。接下来就来介绍目前比较流行的分层规范:

  • 实体层: 定义应用程序中的数据模型,表示应用程序中的各种对象和数据结构。
  • 数据访问层: 也称为持久化层,负责与数据库或其他数据存储系统进行数据交互。它封装了数据库访问的细节,提供对数据库的查询和更新等操作。
  • 业务逻辑层: 也称为服务层,该层为程序的核心业务逻辑。它负责处理数据的处理、业务规则的实现以及与数据库访问层的交互。
  • 请求处理层: 负责与用户进行交互,接收用户请求并作出响应。主要是对访问控制进行转发,各类基础参数校验,或者不复用的业务简单处理等。

以下是各层的简单结构图:

在这里插入图片描述

一般是上层依赖于下层,如:请求处理层依赖或调用业务逻辑层,业务逻辑层依赖或调用数据访问层。

通过将应用程序划分为多个层次,每个层次可以独立进行修改和扩展,而不影响其他层次的代码。这种分层设计也有助于提高应用程序的模块化程度,便于团队协同开发和测试。

二、实体层

实体层是定义应用程序中的数据模型,表示应用程序中的各种对象和数据结构。其实我们的应用程序从另一个角度来说就是数据的流转,比如用户输入用户名密码,经过我们的程序最终流转到数据库保存起来,或是我们查看网站的某一页面,也是将数据从数据库取出来,最终流转到页面进行展示。这些数据在程序中就是各种对象和数据结构。

这就不得不提 O/R 映射了。

1.O/R映射

O/R 映射(Object/Relational Mapping)是一种技术,用于将对象模型与关系模型进行映射,从而将对象持久化到关系数据库中。简单来说,O/R 映射就是将Java程序中的对象自动持久化到关系数据库中。

O/R 映射的目的是解决面向对象与关系数据库存在的互不匹配的现象。它通过描述对象和数据库之间映射的元数据,将对象模型转换为关系模型,以及将关系模型转换为对象模型。

对象和关系数据是业务实体的两种表现形式,业务实体在内存中表现为对象,在数据库中表现为关系数据。

在这里插入图片描述
数据库中的数据要在程序中流转使用,就需要想办法在程序中去表示数据库中的数据。怎么表示呢?O/R 映射。

2.O/R映射实践

O/R 映射也就是对象-关系映射,具体操作是将数据库的表映射成程序中的类,表中列映射为类的属性。

具体如何操作呢?这是数据库中的teacher 表。

在这里插入图片描述

现在将其映射到程序中的类。

在这里插入图片描述

从图上看出 O/R 映射不难,很简单,就是表名变为类名(类名为大驼峰命名法),列变为属性,列名为属性名(小驼峰命名法),同时也要注意数据库数据类型转为对应的程序中数据类型。那我们接来实现该类吧。

首先准备好开发环境,添加了 MySQL 驱动 jar 包,新建了 com.entity 包,entity 是实体的意思,该包就代表实体层,存放我们的实体类,也就是 O/R 映射类。

在这里插入图片描述

com.entity 包下新建 Teacher 类。

public class Teacher {

    /**
     * 唯一标识
     */
    private Integer id;
    /**
     * 姓名
     */
    private String name;
    /**
     * 性别
     */
    private String sex;
    /**
     * 年龄
     */
    private Integer age;

    /**
     * 无参构造器
     */
    public Teacher() {
    }

    /**
     * 提供setter和getter方法
     */

Teacher 类中编写 teacher 表中列的对应属性,一般设置为私有,同时要添加无参构造函数和 settergetter 方法。(IDEA 中使用 Alt+insert 快捷键快捷生成构造函数和 settergetter 方法)

这样 O/R 映射类就完成了。之后数据库中的一条数据就可以对应 Java 中的一个对象。

三、数据访问层

1.DAO层

DAO 层是 Data Access Object(数据访问对象)的缩写,DAO 层通常位于业务逻辑层与数据库层之间,主要用于处理与数据库相关的操作,封装对数据库的访问细节,提供简单、统一的接口供上层业务逻辑调用。它主要作用是实现数据访问逻辑与业务逻辑的分离,提高代码的可重用性、可维护性和可测试性。在典型的三层架构中,DAO 层位于持久化层(Persistence Layer)中,负责与数据库进行交互。它通常包含一些数据访问接口(接口定义了访问数据库的方法)和其对应的实现类。通过 DAO 层,应用程序可以通过调用相应的方法来执行数据库操作,如插入数据、更新数据、删除数据、查询数据等。

DAO 层很简单,就是与 MySQLOracleHbaseOB 等数据库进行交互,目前我们使用 Java 中的 JDBC 去操作数据库,那么就在 DAO 层封装数据库的增删改查操作,同时为了简化 DAO,不使用接口和接口实现的组合,直接一个类搞定。

在这里插入图片描述

2.DAO层实战

新建 com.dao 包,包下新建 TeacherDao 类,该类名很好理解吧,表明该类服务于 Teacher。知道了这样的规律后,那么 student 表呢?实体类为 StudentDAO 层为 StudentDao 类。

TeacherDao 类下会封装对 teacher 表的数据库操作,这里编写新增操作和单条记录查询操作。同时为了便于学习入门,不会过渡封装,以免造成代码不易理解和阅读。

直接上代码:

public class TeacherDao {

    /**
     * 数据库URL
     */
    private static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/test_jdbc?serverTimezone=Asia/Shanghai";
    /**
     * 数据库用户名
     */
    private static final String USER = "root";
    /**
     * 数据库密码
     */
    private static final String PASSWORD = "root";

    /**
     * 获取数据库连接
     *
     * @return
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public static Connection getConnection() throws ClassNotFoundException, SQLException {
        // 加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 获取数据库连接
        Connection connection = DriverManager.getConnection(DB_URL, USER, PASSWORD);
        return connection;
    }

    /**
     * 关闭结果集、声明、连接等资源
     *
     * @param rs
     * @param pstmt
     * @param conn
     */
    public static void close(ResultSet rs, PreparedStatement pstmt, Connection conn) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (pstmt != null) {
            try {
                pstmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 新增教师信息
     *
     * @param teacher
     * @return
     */
    public int executeInsert(Teacher teacher) throws SQLException, ClassNotFoundException {
        // 获取数据库连接
        Connection connection = getConnection();
        // 新增SQL
        String sql = "insert into teacher (name, sex, age) value (?, ?, ?)";
        // 预编译SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 设置参数
        preparedStatement.setString(1, teacher.getName());
        preparedStatement.setString(2, teacher.getSex());
        preparedStatement.setInt(3, teacher.getAge());
        // 执行SQL
        int num = preparedStatement.executeUpdate();
        // 关闭资源
        close(null, preparedStatement, connection);

        return num;
    }

    /**
     * 根据id查询教师信息
     *
     * @param id
     * @return
     * @throws SQLException
     * @throws ClassNotFoundException
     */
    public Teacher executeSelect(int id) throws SQLException, ClassNotFoundException {
        // Teacher类实例化,一个Teacher对象来存储一条数据库教师记录
        Teacher teacher = new Teacher();
        // 获取数据库连接
        Connection connection = getConnection();
        // 根据ID查询SQL
        String sql = "select * from teacher where id = ?";
        // 预编译SQL
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        // 设置参数
        preparedStatement.setInt(1, id);
        // 执行SQL
        ResultSet resultSet = preparedStatement.executeQuery();
        // 处理结果集
        while (resultSet.next()){
            teacher.setId(resultSet.getInt("id"));
            teacher.setName(resultSet.getString("name"));
            teacher.setSex(resultSet.getString("sex"));
            teacher.setAge(resultSet.getInt("age"));
        }
        // 关闭资源
        close(resultSet, preparedStatement, connection);

        return teacher;
    }
}

这是一个简单的 Java 类,名为 TeacherDao,它用于与 MySQL 数据库进行交互。

  • DB_URLUSERPASSWORD:这三个静态常量分别存储了数据库的URL、用户名和密码。
  • getConnection():这个方法用于获取数据库的连接。它首先加载 MySQLJDBC 驱动程序,然后使用驱动管理器获取数据库连接。
  • close(ResultSet rs, PreparedStatement pstmt, Connection conn):这个方法用于关闭结果集、声明和连接。如果这些对象不为 null,则尝试关闭它们。这是一个良好的编程习惯,可以避免资源泄漏,当然最好放在 finally 块中。
  • executeInsert(Teacher teacher):这个方法接收一个 Teacher 对象,并将其信息插入到数据库中的 teacher 表中。它首先获取数据库连接,然后创建一个预编译的 SQL 语句,该语句使用问号作为占位符来代替实际的值。然后,它使用 PreparedStatement 对象设置实际的值,并执行 SQL 语句。最后,它关闭结果集、声明和连接,并返回执行更新操作的数量。
  • executeSelect(int id):从数据库的 teacher 表中查询并返回具有特定 IDTeacher 对象。首先创建一个新的 Teacher 对象,该对象将用于存储从数据库中检索的教师记录。获取数据库连接。创建一个 SQL 查询字符串,该字符串包含一个占位符(?)。然后使用连接创建一个预编译的 SQL 语句,使用setId()setName()setSex()setAge() 方法设置查询结果,并将结果存储在 Teacher 对象中。通过调用 ResultSet 对象的 next() 方法遍历查询结果。每次迭代时,都会从结果集中获取下一个记录,并将其存储在 Teacher 对象中。最后,它关闭结果集、声明和连接,并将含有教师记录的 Teacher 对象进行返回。

我们编写测试类:

public class Test {

    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        TeacherDao teacherDao = new TeacherDao();

        // 构造新增的教师信息
        Teacher teacher = new Teacher();
        teacher.setName("张八一");
        teacher.setSex("男");
        teacher.setAge(36);

        // 将教师信息新增到数据库表中
        int num = teacherDao.executeInsert(teacher);
        System.out.println("执行影响的记录数:" + num);

        // 查询教师的id
        int id = 2;
        // 根据id查询教师信息
        Teacher teacher1 = teacherDao.executeSelect(id);
        // 打印教师信息
        System.out.println("查询出的数据为:" + teacher1.getName() + "," + teacher1.getSex() + "," + teacher1.getAge());
    }
}

执行结果如下:

执行影响的记录数:1
查询出的数据为:李四,男,27

大家可以自行尝试,多多练习,多多测试!


总结

应用分层: 实体层、数据访问层、业务逻辑层、请求处理层
O/R映射: 是指将数据库的表映射成程序中的类,表中列映射为类的属性。
DAO层: 封装对数据库访问的一些操作。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程火箭车

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值