数据库操作

要对数据表中的数据进行操作,首先需要建立与数据库的连接。JDBC API 中提供的各种类可对数据表中的数据进行查找、添加、修改、删除等操作。本节以 MySQL 数据库为例,讲解几种常见的数据库操作。

连接数据库

要访问数据库,首先需要加载数据库的驱动程序(只需要在第一次访问数据库时加载一次),然后每次访问数据时创建一个 Connection 对象,接着执行操作数据库的 SQL 语句,最后在完成数据库操作后销毁前面创建的 Connection 对象,释放与数据库的连接。

【例17.1】连接本地的MySQL 8.0数据库(实例位置:资源包\TM\sl\17\1)

创建 ConnectionUtil 类,在主方法中加载 MySQL 8.0 的驱动包,并连接本地 MySQL 8.0 数据库,如果可正常连接,则输出成功提示。

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

public class ConnectionUtil {

	public static void main(String[] args) {

		Connection con = null; // 声明数据库连接对象
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");// 加载数据库驱动类
			// 通过访问数据库的URL,获取数据库连接对象
			con = DriverManager.getConnection(
					"jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&allowPublicKeyRetrieval=true",
					"root", "123456");
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}

		if (con != null) {// 如果数据库不为空
			System.out.println("数据库连接成功!");
			System.out.println(con);
		}
	}
}

运行结果如下:

     数据库连接成功!
     com.mysql.cj.jdbc.ConnectionImpl@5fdcaa40
  1. 如果驱动类名被写错,则会抛出 ClassNotFoundException 异常。开发者需要做两项检查:

    • 是否为项目引入了驱动JAR包。

    • 是否写错驱动类名。

  2. 如果 DriverManager 类无法连接数据库,则抛出 SQLException 异常。日常日志是由数据库方提供的,开发者需根据异常日志的具体内容来分析连接失败的原因,如账号密码错误、IP地址错误、URL格式不正确、数据库未开启连接服务等。

向数据库中发送SQL语句

例17.1只是获取与数据库的连接,要执行 SQL 语句,还需要创建 Statement 类对象。通过例17.1创建的连接数据库对象 con 的 createStatement() 方法可获得 Statement 对象,其语法如下:

     Statement stmt = con.createStatement();

处理查询结果集

有了 Statement 对象以后,可调用相应的方法实现对数据库的查询和修改,并将查询的结果集存储在 ResultSet 类的对象中。例如,执行 “select * from tb_stu” 语句,并保存查询的结果集,其语法如下:

     ResultSet res = stmt.executeQuery("select * from tb_stu");

运行结果为返回一个 ResultSet 对象。ResultSet 对象一次只可以看到结果集中的一行数据,使用该类的 next() 方法可将光标从当前位置移向下一行。

顺序查询

ResultSet 类的 next() 方法的返回值是 boolean 类型的数据,当游标移动到最后一行之后会返回 false。下面的实例就是将数据表 tb_emp 中的全部信息显示在控制台上。

【例17.2】查询数据库中tb_stu表中的所有数据(实例位置:资源包\TM\sl\17\2)

将 “资源包\TM\sl\17\test.sql” 脚本文件导入 MySQL 8.0 数据库中,并在 test 库中查询 tb_stu 表,则可以看到如图17.3所示结果。

创建 JDBCDemo 类,在主方法中连接 MySQL 8.0 数据库,将 tb_stu 表中的所有数据都输出到控制台中。

image 2024 03 05 19 14 42 269
Figure 1. 图17.3 数据库中的查询结果
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo {

	public static void main(String[] args) {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");// 加载数据库驱动类
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		try {
			// 通过访问数据库的URL,获取数据库连接对象
			Connection con = DriverManager.getConnection(
					"jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&allowPublicKeyRetrieval=true",
					"root", "123456");
			Statement stmt = con.createStatement();
			ResultSet res = stmt.executeQuery("select * from tb_stu");
			while (res.next()) { // 如果当前语句不是最后一条,则进入循环
				String id = res.getString("id"); // 获取列名是id的字段值
				String name = res.getString("name"); // 获取列名是name的字段值
				String sex = res.getString("sex"); // 获取列名是sex的字段值
				String birthday = res.getString("birthday"); // 获取列名是birthday的字段值
				System.out.print("编号:" + id); // 将列值输出
				System.out.print(" 姓名:" + name);
				System.out.print(" 性别:" + sex);
				System.out.println(" 生日:" + birthday);
			}
			con.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

运行结果如下:

     编号:1 姓名:张三 性别:男 生日:1998-02-06
     编号:2 姓名:李四 性别:女 生日:1995-06-28
     编号:3 姓名:王五 性别:女 生日:1999-11-23
     编号:4 姓名:赵六 性别:男 生日:2000-05-30

模糊查询

SQL 语句中提供了 LIKE 操作符以用于模糊查询,可使用 “%” 来代替 0 个或多个字符,使用下画线 “_” 来代替一个字符。例如,在查询姓张的同学的信息时,可使用以下 SQL 语句:

     select * from tb_stu where name like '张%'

【例17.3】找出所有姓张的同学(实例位置:资源包\TM\sl\17\3)

本例在例17.2的基础上进行修改,为查询语句添加 like 关键字,然后将姓张的同学的全部信息输出到控制台中。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo2 {

	public static void main(String[] args) {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");// 加载数据库驱动类
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		try {
			// 通过访问数据库的URL,获取数据库连接对象
			Connection con = DriverManager.getConnection(
					"jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&allowPublicKeyRetrieval=true",
					"root", "123456");
			Statement stmt = con.createStatement();
			ResultSet res = stmt.executeQuery("select * from tb_stu where name like '张%'");
			while (res.next()) { // 如果当前语句不是最后一条,则进入循环
				String id = res.getString("id"); // 获取列名是id的字段值
				String name = res.getString("name"); // 获取列名是name的字段值
				String sex = res.getString("sex"); // 获取列名是sex的字段值
				String birthday = res.getString("birthday"); // 获取列名是birthday的字段值
				System.out.print("编号:" + id); // 将列值输出
				System.out.print(" 姓名:" + name);
				System.out.print(" 性别:" + sex);
				System.out.println(" 生日:" + birthday);
			}
			con.close();							// 关闭数据库连接
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
}

运行结果如下:

     编号:1 姓名:张三 性别:男 生日:1998-02-06

预处理语句

向数据库发送一个 SQL 语句,数据库中的 SQL 解释器负责把 SQL 语句生成为底层的内部命令,然后执行该命令,完成相关的数据操作。如果不断地向数据库中提交 SQL 语句,则肯定会增加数据库中 SQL 解释器的负担,影响执行的速度。

JDBC 可以通过 Connection 对象的 preparedStatement(String sql) 方法来对 SQL 语句进行预处理,生成数据库底层的内部命令,并将该命令封装在 PreparedStatement 对象中。通过调用该对象的相应方法,SQL 语句可执行底层数据库命令。也就是说,应用程序能针对连接的数据库,将 SQL 语句解释为数据库底层的内部命令,然后让数据库执行这个命令。这样,可以减轻数据库的负担,提高访问数据库的速度。

对 SQL 进行预处理时可以使用通配符 “?” 来代替任何的字段值。例如:

sql = con.prepareStatement("select * from tb_stu where id = ?");

在执行预处理语句前,必须用相应方法来设置通配符所表示的值。例如:

sql.setInt(1,16);

上述语句中的1表示从左向右的第1个通配符,16表示设置的通配符的值。将通配符的值设置为16后,功能等同于:

sql = con.prepareStatement("select * from tb_stu where id = 16");

书写两条语句看似麻烦了一些,但使用预处理语句可使应用程序动态地改变 SQL 语句中关于字段值条件的设定。

通过 setXXX() 方法为 SQL 语句中的参数赋值时,建议使用与参数匹配的方法,也可以使用 setObject() 方法为各种类型的参数赋值。例如: sql.setObject(2,'李丽');

【例17.4】找出编号为3的同学(实例位置:资源包\TM\sl\17\4)

本例在例17.2的基础上进行修改,使用预处理语句动态地查询编号为3的同学信息,并将该同学的全部信息输出到控制台中。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCDemo3 {
	public static void main(String[] args) {
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");// 加载数据库驱动类
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		try {
			// 通过访问数据库的URL,获取数据库连接对象
			Connection con = DriverManager.getConnection(
					"jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&allowPublicKeyRetrieval=true",
					"root", "123456");
			PreparedStatement ps = con.prepareStatement("select * from tb_stu where id = ?");
			ps.setInt(1, 3); // 设置参数
			ResultSet rs = ps.executeQuery(); // 执行预处理语句
			// 如果当前记录不是结果集中的最后一行,则进入循环体
			while (rs.next()) {
				String id = rs.getString(1); // 获取结果集中第一列的值
				String name = rs.getString("name"); // 获取name列的列值
				String sex = rs.getString("sex"); // 获取sex列的列值
				String birthday = rs.getString("birthday"); // 获取birthday列的列值
				System.out.print("编号:" + id); // 输出信息
				System.out.print(" 姓名:" + name);
				System.out.print(" 性别:" + sex);
				System.out.println(" 生日:" + birthday);
			}
			con.close();							// 关闭数据库连接
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}
}

运行结果如下:

     编号:3 姓名:王五 性别:女 生日:1999-11-23

添加修改删除记录

SQL 语句可以对数据执行添加、修改和删除操作。可通过 PreparedStatement 类的指定参数来动态地对数据表中原有数据进行修改操作,并通过 executeUpdate() 方法执行更新语句操作。

【例17.5】对学生表进行添加、修改和删除操作(实例位置:资源包\TM\sl\17\5)

创建 JDBCDemo4 类,在类中编写相应的方法,分别用来初始化数据库连接、关闭数据库连接、查询所有学生数据、添加新学生数据、修改指定编号的学生姓名和删除指定学生的全部数据。最后模拟以下场景。

  • 添加新学生:姓名王富贵,男,生日1990-12-30,编号为 5。

  • 将编号为 2 的学生姓名修改为 “李美丽”。

  • 删除编号为 3 的学生。

具体代码如下:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JDBCDemo4 {
	Connection con;// 声明数据库连接对象

	public void initConnection() {// 初始化数据库连接
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");// 加载数据库驱动类
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		try {
			// 通过访问数据库的URL,获取数据库连接对象
			con = DriverManager.getConnection(
					"jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai&zeroDateTimeBehavior=CONVERT_TO_NULL&allowPublicKeyRetrieval=true",
					"root", "123456");
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void closeConnection() {// 关闭数据库连接
		if (con != null) {
			try {
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	public void showAllData() {// 显示所有学生数据
		try {
			Statement stmt = con.createStatement();
			ResultSet rs = stmt.executeQuery("select * from tb_stu");
			while (rs.next()) { // 如果当前语句不是最后一条,则进入循环
				System.out.print("编号:" + rs.getString("id")); // 将列值输出
				System.out.print(" 姓名:" + rs.getString("name"));
				System.out.print(" 性别:" + rs.getString("sex"));
				System.out.println(" 生日:" + rs.getString("birthday"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void add(int id, String name, String sex, String birthday) {// 添加新学生
		try {
			String sql = "insert into tb_stu values(?,?,?,?) ";
			PreparedStatement ps = con.prepareStatement(sql);
			ps.setInt(1, id); // 设置编号
			ps.setString(2, name); // 设置名字
			ps.setString(3, sex); // 设置性别
			ps.setString(4, birthday); // 设置出生日期
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void delete(int id) {// 删除指定ID的学生
		try {
			Statement stmt = con.createStatement();
			stmt.executeUpdate("delete from tb_stu where id =" + id);
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public void update(int id, String newName) {// 修改指定ID的学会姓名
		try {
			String sql = "update tb_stu set name = ? where id = ? ";
			PreparedStatement ps = con.prepareStatement(sql);
			ps.setString(1, newName); // 设置名字
			ps.setInt(2, id); // 设置编号
			ps.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		JDBCDemo4 demo = new JDBCDemo4();
		demo.initConnection();
		demo.showAllData();
		System.out.println("---添加新同学---");
		demo.add(5, "王富贵","男","1990-12-30");
		demo.showAllData();
		System.out.println("---修改编号为2的学生姓名---");
		demo.update(2, "李美丽");
		demo.showAllData();
		System.out.println("---删除编号为3的学生---");
		demo.delete(3);
		demo.showAllData();
		demo.closeConnection();
	}
}

运行结果如下:

     编号:1 姓名:张三 性别:男 生日:1998-02-06
     编号:2 姓名:李四 性别:女 生日:1995-06-28
     编号:3 姓名:王五 性别:女 生日:1999-11-23
     编号:4 姓名:赵六 性别:男 生日:2000-05-30
     ---添加新同学---
     编号:1 姓名:张三 性别:男 生日:1998-02-06
     编号:2 姓名:李四 性别:女 生日:1995-06-28
     编号:3 姓名:王五 性别:女 生日:1999-11-23
     编号:4 姓名:赵六 性别:男 生日:2000-05-30
     编号:5 姓名:王富贵 性别:男 生日:1990-12-30
     ---修改编号为2的学生姓名---
     编号:1 姓名:张三 性别:男 生日:1998-02-06
     编号:2 姓名:李美丽 性别:女 生日:1995-06-28
     编号:3 姓名:王五 性别:女 生日:1999-11-23
     编号:4 姓名:赵六 性别:男 生日:2000-05-30
     编号:5 姓名:王富贵 性别:男 生日:1990-12-30
     ---删除编号为3的学生---
     编号:1 姓名:张三 性别:男 生日:1998-02-06
     编号:2 姓名:李美丽 性别:女 生日:1995-06-28
     编号:4 姓名:赵六 性别:男 生日:2000-05-30
     编号:5 姓名:王富贵 性别:男 生日:1990-12-30