首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >【MySQL】JDBC && DAO && 封装工具类 && 多表查询时的注意事项

【MySQL】JDBC && DAO && 封装工具类 && 多表查询时的注意事项

原创
作者头像
lirendada
发布2026-05-22 16:47:57
发布2026-05-22 16:47:57
770
举报
文章被收录于专栏:MySQLMySQL

Ⅰ. 什么是 JDBC

JDBCJava Data Base Connectivity, Java数据库连接)是 Java 程序和数据库之间的桥梁,包含了一套 Java 定义的用于执行 SQL 语句的接口,使开发者能够编写数据库的程序。

JDBC 的主要作用是:与数据库建立连接、发送 SQL 语句和处理数据库执行结果。

客户端操作数据库的过程主要分为以下几步:

  1. 连接到数据库服务
  2. 发送 SQL 语句
  3. 得到返回结果并显示
  4. 关闭连接

Java 采取的做法是把以上操作步骤定义了相应的接口,具体的实现交给数据库厂商去做,Java 程序员只需要按照需要调用接口中定义的方法即可,这样不论使用什么数据库,都对于 Java 程序没有任何影响,即便是换一个数据库,也只需要换一下相应厂商的实现依赖。

JDBC 使用过程可以概括为:

  1. 加载数据库厂商的驱动包
  2. 建立连接
  3. 创建 Statement
  4. 执行 SQL
  5. 处理结果释放资源和关闭连接。

Ⅱ. 使用 JDBC

一、创建 Maven 工程并配置国内镜像

Maven 相当于 Java 世界里的应用(依赖)市场,管理市面上所有的应用(依赖),自动化地帮你拉库、编译、打包、测试、部署,一键搞定项目构建

创建 maven 工程如下图所示:

下面是工程的目录构成:

然后到 maven仓库 查找 MySQL Connector/j,然后点进去复制 maven 代码,如下所示:

复制到 pom.xmldependencies 依赖中,如下图所示:

下载完成就能使用了!

点击编译器右边的 maven 图标,可以看到下面的自动化操作,如下所示,这些后面会系统学!

在下载依赖的时候,可能速度比较慢,这时候就需要去修改配置文件 settings.xml 中的源,改成阿里云镜像,速度会加快:

代码语言:javascript
复制
<mirrors>
    <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>*</mirrorOf>
        <name>阿里云公共仓库</name>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
    <mirror>
        <id>central</id>
        <mirrorOf>*</mirrorOf>
        <name>aliyun central</name>
        <url>https://maven.aliyun.com/repository/central</url>
    </mirror>
    <mirror>
        <id>spring</id>
        <mirrorOf>*</mirrorOf>
        <name>aliyun spring</name>
        <url>https://maven.aliyun.com/repository/spring</url>
    </mirror>
</mirrors>

二、建立数据库连接

DriverManager 驱动管理类,⽤于管理 JDBC 驱动程序,可以从驱动程序中获取数据库连接,始于 JDK1.1

DataSource 数据源是 DriverManager 的替代方案,始于 JDK1.4,是获取数据库连接的首选方法,推荐使用。

这两者都可以获取到数据库连接,只不过连接管理方式和资源利用效率不同,如下所示:

  • DriverManager 每次调用 getConnection 方法都会初始化一个新的连接,使用完成后会关闭真实连接,导致资源浪费。
  • DataSource 使用了连接池的技术,会在初始化时创建一定数量的数据库连接,这些连接可以重复使用,关闭时并不是真正关闭连接,而是将连接归还给连接池,以供后续使用,有效地提高资源利用率和性能。

① 通过 DriverManager 静态方法获取

代码语言:javascript
复制
// 1. 加载驱动注册程序,只有注册了驱动才能使用
Class.forName("com.mysql.cj.jdbc.Driver");

// 2. 获取数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/javalearning?useSSL=false&serverTimezone=UTC&characterEncoding=utf8",
        "root", "123123");

注意事项:

  1. MySQL8 之后驱动类名是 com.mysql.cj.jdbc.Driver,而不是旧版本的 com.mysql.jdbc.Driver
  2. JDBC 连接的 url 的格式是:jdbc:mysql://主机:端口/数据库名?参数键=值&参数键=值

② 通过 DataSource 对象获取(推荐⭐⭐⭐)

代码语言:javascript
复制
// 1. 设置mysql数据源的连接信息
MysqlDataSource mds = new MysqlDataSource();
mds.setUrl("jdbc:mysql://localhost:3306/javalearning?useSSL=false&serverTimezone=UTC&characterEncoding=utf8");
mds.setUser("root");
mds.setPassword("123123");

// 2. 向上转型成DataSource接口
DataSource ds = mds;

// 3. 获取连接
connection = ds.getConnection();

三、创建语句对象

① 创建 Statement

Statement 是用于执行静态 SQL 语句并返回执行结果的对象。

代码语言:javascript
复制
// 通过connection获取statement对象
statement = connection.createStatement();

② 创建 PreparedStatement(推荐⭐⭐⭐)

这是预编译 SQL 语句对象,SQL 语句被预编译并存储在 PreparedStatement 对象中,可以使用该对象多次执行 SQL 语句,同时解决了SQL注入问题

在使用 sql 的时候? 号进行占位,然后通过 set 系列函数进行参数绑定

代码语言:javascript
复制
// 4. 定义sql语句并创建PreparedStatement对象
String sql = "select id, name, sno, gender, enroll_date, class_id from student where name = ?";
prestatement = connection.prepareStatement(sql);

// 5. 进行占位符绑定
prestatement .setString(1, "宋江");

四、执行 SQL 语句

  • 执行 select 查询时返回的是一个结果集,用 ResultSet 进行接收,通常用 executeQuery()接口。
  • 执行 insertupdatedelete 操作时,返回的是受影响的行数,用 int 类型接收然后判断正确即可,通常用 executeUpdate() 接口。
代码语言:javascript
复制
// 执行select语句, 并接收结果集 
ResultSet resultSet = statement.executeQuery("select id, name, sno, age, gender, enroll_date, class_id from student"); 

// 执行insert, update, delete语句,并接收受影响的行数 
int row = statement.executeUpdate("update student set age = 20 where id = 2");

五、处理结果集 ResultSet

如果返回的是一个结果集,也就是进行 select 查询,则需要遍历这个集合获取对应列的值。

此时使用结果集的 next() 函数,如果游标还没走到最后,则返回 true,而游标走到最后则会返回 false,然后可以使用一系列的 getxxx() 方法获取对应的字段,具体代码如下所示:

代码语言:javascript
复制
// 7. 处理结果集
while (resultSet.next()) {
    Long id = resultSet.getLong("id");
    String name = resultSet.getString("name");
    String sno = resultSet.getString("sno");
    boolean gender = resultSet.getBoolean("gender");
    Date enrollDate = resultSet.getDate("enroll_date");
    Long classId = resultSet.getLong("class_id");
    System.out.println(id + "\t" + name + "\t" + sno + "\t" + gender + "\t" + enrollDate + "\t" + classId + "\t");
}

六、释放资源

最后在 finally 代码块中进行资源释放,否则会出现内存泄漏问题:

代码语言:javascript
复制
finally {
    // 8. 释放资源
    if (resultSet != null) {
        try {
            resultSet.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (statement != null) {
        try {
            statement.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Ⅲ. DAO

DAOData Access Object(数据访问对象)的缩写,是 Java 等编程语言中常见的一种设计模式,用来封装对数据库的操作逻辑。

简单的说,DAO 就是专门负责 "跟数据库打交道" 的模块,SQL 操作从业务代码里分离出来,让业务代码更干净、更易维护、更专业。

比如我们要对学生表和班级表进行增删改查,以及一些特定要求的查询,那我们会创建以下内容:

比如 Classes 表,存放对应的数据字段,如下所示:

代码语言:javascript
复制
package com.liren.model;

public class Classes {
    private Long id;     // 主键编号
    private String name; // 班级名

    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

然后对应的 ClassesDAO.java 中实现对应的增删查改,如下所示:

代码语言:javascript
复制
package com.liren.dao;

import com.liren.model.Classes;
import utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * 专门针对班级表的数据库查询对象
 */
public class ClassesDAO {
    // 增加操作
    public void insert(Classes cs) {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = DBUtil.getConnection();
            String sql = "insert into class(id, name) values(?, ?)";
            ps = conn.prepareStatement(sql);
            ps.setLong(1, cs.getId());
            ps.setString(2, cs.getName());
            int ret = ps.executeUpdate();
            if(ret > 0) {
                System.out.println("插入班级成功!");
            } else {
                System.out.println("插入班级失败!");
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.closeConnection(null, ps, conn);
        }
    }
    
    // 删除、更新、查询操作......
}

Ⅳ. 工具类

上面提到将一些数据源初始化、关闭数据源等操作专门做成一个工具类,可以减少代码冗余,如下所示:

代码语言:javascript
复制
// DBUtil.java文件
package utils;

import com.mysql.cj.jdbc.MysqlDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class DBUtil {
    private static String url = "jdbc:mysql://localhost:3306/javalearning?useUnicode=true&characterEncoding=utf8";
    private static String user = "root";
    private static String password = "123123";

    private static DataSource ds = null;

    // 在类加载的时候初始化dataSource对象
    static {
        MysqlDataSource mds = new MysqlDataSource();
        mds.setURL(url);
        mds.setUser(user);
        mds.setPassword(password);
        ds = mds;
    }

    // 构造方法私有化,防止其他的地方通过new来创建这个类的对象
    private DBUtil() {}
    
    // 获取连接接口
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    
    // 获取关闭连接接口
    public static void closeConnection(ResultSet resultSet, Statement statement, Connection conn) {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Ⅴ. 多表查询时的注意事项

select 的时候难免会有多表查询,如下所示:

代码语言:javascript
复制
String sql = "select s.id, s.name, s.age, c.id c_id, c.name c_name from student s, class c where s.class_id = c.id and c.name = ?";

此时结果集该如何表示呢❓❓❓

这个时候可以Student 表中添加 Classes 对象,进行关联,如下所示:

代码语言:javascript
复制
package com.liren.model;

import java.util.Date;

public class Student {
    private Long id;
    private String name;
    private String sno;
    private Integer age;
    private byte gender;
    private Date enroll_date;
    private Long class_id;

    private Classes cs; // 与课程表关联

    public Classes getCs() {
        return cs;
    }

    public void setCs(Classes cs) {
        this.cs = cs;
    }

    // 一系列get、set方法
}

这样子只需要在结果集中构造出 Classes 对象,然后设置到 Student 中的 Classes 字段,最后拿到的 List<Student> 返回就能存在 Classes 的字段了!

代码语言:javascript
复制
package com.liren.dao;

import com.liren.model.Classes;
import com.liren.model.Student;
import utils.DBUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * 专门针对学生表的数据库访问对象
 */
public class StudentDAO {
    // 增删改查。。。

    // 还有专门针对学生表的业务逻辑处理方法
    // 比如下面的:根据班级名查询班级中所有的同学信息
    public List<Student> selectByClassname(String classname) {
        if(classname == null || classname.isEmpty()) {
            System.out.println("班级名不能为空!");
            return null;
        }

        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement ps = null;
        try {
            List<Student> ret = new ArrayList<Student>();
            conn = DBUtil.getConnection();
            String sql = "select s.id, s.name, s.age, s.sno, s.gender, s.enroll_date, c.id c_id, c.name c_name from student s, class c where s.class_id = c.id and c.name = ?";
            ps = conn.prepareStatement(sql);
            ps.setString(1, classname);
            rs = ps.executeQuery();
            
            // ❗❗❗❗❗❗❗❗下面是重点❗❗❗❗❗❗❗❗❗
            while(rs.next()) {
                // 封装Student对象
                Student s = new Student();
                s.setId(rs.getLong("id"));
                s.setName(rs.getString("name"));
                s.setAge(rs.getInt("age"));
                s.setSno(rs.getString("sno"));
                s.setGender(rs.getByte("gender"));
                s.setEnroll_date(rs.getDate("enroll_date"));
                s.setClass_id(rs.getLong("c_id"));
                
                // 封装Classes对象
                Classes c = new Classes();
                c.setId(rs.getLong("c_id"));
                c.setName(rs.getString("c_name"));

                // 设置Student对象中的Classes对象,完成两者的关联,然后添加到List中
                s.setCs(c);
                ret.add(s);
            }
            return ret;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            DBUtil.closeConnection(rs, ps, conn);
        }
    }
}

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Ⅰ. 什么是 JDBC
  • Ⅱ. 使用 JDBC
    • 一、创建 Maven 工程并配置国内镜像
    • 二、建立数据库连接
      • ① 通过 DriverManager 静态方法获取
      • ② 通过 DataSource 对象获取(推荐⭐⭐⭐)
    • 三、创建语句对象
      • ① 创建 Statement
      • ② 创建 PreparedStatement(推荐⭐⭐⭐)
    • 四、执行 SQL 语句
    • 五、处理结果集 ResultSet
    • 六、释放资源
  • Ⅲ. DAO
  • Ⅳ. 工具类
  • Ⅴ. 多表查询时的注意事项
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档