之前的文章中,介绍了JDBC的概述,包括JDBC操作数据库的步骤、JDBC的作用等;还介绍了JDBC常用的类和接口,本文将以操作MySQL数据库为例,介绍几种常见的JDBC的数据库操作。
一、数据库基础
数据库是一种存储结构,它允许使用葛总格式输入、处理和检索数据,且不必在每次都需要数据是重新输入数据。例如,当需要某人的电话号码时,需要查看电话薄,按照姓名来查阅,这个电话薄就是一个数据库。
当前比较流行的数据库主要有MySQL、Oracle、SQL Server等,它们各有各自的特点,本文主要讲解如何操作MySQl数据库。
1、MySQL数据库安装
本部分内容,请跳转链接查看前文《mac环境下MySQL安装与卸载【吐血排坑】》,文中详细介绍了Vavicat Prenium安装问题、mac系统的MySQL卸载问题,和MySQL安装问题以及安装成功后可能遇到的各类启动和权限问题及解决方案。因此,安装部分的内容就不在这里赘述了。
2、数据库常用操作
数据库的基本操作主要包括,数据的增删改查。
(1)create
实例1:创建db_employeeinfo数据库,和tb_employer数据表
CREATE DATABASE db_employeeinfo;
USE db_employeeinfo;
SET FOREIGN_KEY_CHECKS=0;
DROP TABLE IF EXISTS `tb_employer`;
CREATE TABLE `tb_employer` (
`name` varchar(30) NOT NULL,
`sex` varchar(30) NOT NULL,
`age` int(30) NOT NULL,
`dept` varchar(30) NOT NULL,
`phone` varchar(30) NOT NULL,
`remark` varchar(300) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
(2)insert
MySQL 表中使用 INSERT INTO SQL语句来插入数据。
你可以通过 mysql> 命令提示窗口中向数据表中插入数据,或者通过PHP脚本来插入数据。
语法:
INSERT INTO table_name [( column_name1, column_name2,...column_nameN )]
VALUES( value1, value2,...valueN );
如果数据是字符型,必须使用单引号或者双引号,如:“value”。
实例2:向db_employeeinfo数据库的tb_employer数据表中插入数据
INSERT INTO `tb_employer` VALUES ('张三', '男', '20', '后勤保障部', '13078931129', '员工');
INSERT INTO `tb_employer` VALUES ('王丽', '女', '28', '网站运营部', '13894658872', '主管');
INSERT INTO `tb_employer` VALUES ('喵喵', '女', '22', '网站运营部', '13894658872', '员工');
INSERT INTO `tb_employer` VALUES ('妞妞', '女', '25', '网站运营部', '13894658872', '员工');
INSERT INTO `tb_employer` VALUES ('大熊', '男', '28', '网站运营部', '13894658872', '员工');
(3)select
select语句主要用于查询数据表中的数据。
语法:
SELECT column_name,column_name
FROM table_name
WHERE Clause GROUP BY column_name HAVING Clause(指定分组条件)
GROUP BY column_name[ASC|DESC]
[LIMIT N][ OFFSET M]
- 查询语句中你可以使用一个或者多个表,表之间使用逗号(,)分割,并使用WHERE语句来设定查询条件。
- SELECT 命令可以读取一条或者多条记录。
- 你可以使用星号(*)来代替其他字段,SELECT语句会返回表的所有字段数据
- 你可以使用 WHERE 语句来包含任何条件。
- 你可以使用GROUP BY来进行分组查询
- HAVING 子句允许指定条件来过滤将出现在最终结果中的分组结果。
- WHERE 子句在所选列上设置条件,而 HAVING 子句则在由 GROUP BY 子句创建的分组上设置条件。
- 你可以使用 LIMIT 属性来设定返回的记录数。
- 你可以通过OFFSET指定SELECT语句开始查询的数据偏移量。默认情况下偏移量为0。
实例3:查询tb_employee表中的所有女性员工的姓名和工资,并按照工资升序排序
- 查看表结构
select * from tb_employer;
结果:
张三 男 20 后勤保障部 13078931129 员工
王丽 女 28 网站运营部 13894658872 主管
喵喵 女 22 网站运营部 13894658872 员工
妞妞 女 25 网站运营部 13894658872 员工
大熊 男 28 网站运营部 13894658872 员工
- 按照要求查询
SELECT name, age FROM tb_employer WHERE sex = "女" ORDER BY age;
结果:
喵喵 22
妞妞 25
王丽 28
(4)update
如果我们需要修改或更新 MySQL 中的数据,我们可以使用 SQL UPDATE 命令来操作。
语法:
UPDATE table_name SET field1=new-value1, field2=new-value2
[WHERE Clause]
- 你可以同时更新一个或多个字段。
- 你可以在 WHERE 子句中指定任何条件。
- 你可以在一个单独表中同时更新数据。
当你需要更新数据表中指定行的数据时 WHERE 子句是非常有用的。
实例4:使用UPDATE 命令来更新tb_employer表中指定的人员的年龄
- 查看修改数据前的张丽的信息
SELECT * FROM tb_employer WHERE name = '王丽';
结果:
王丽 女 28 网站运营部 13894658872 主管
- 修改数据并查看修改后的数据
UPDATE tb_employer SET age='32' WHERE name='王丽';
SELECT * FROM tb_employer WHERE name = '王丽';
结果:
王丽 女 32 网站运营部 13894658872 主管
(5)delete
你可以使用 SQL 的 DELETE FROM 命令来删除 MySQL 数据表中的记录。
你可以在 mysql> 命令提示符或 PHP 脚本中执行该命令。
语法:
DELETE FROM table_name [WHERE Clause]
- 如果没有指定 WHERE 子句,MySQL 表中的所有记录将被删除。
- 你可以在 WHERE 子句中指定任何条件
- 您可以在单个表中一次性删除记录。
当你想删除数据表中指定的记录时 WHERE 子句是非常有用的。
实例5:删除表指定人员的信息
- 查看表结构
select * from tb_employer;
结果:
张三 男 20 后勤保障部 13078931129 员工
王丽 女 32 网站运营部 13894658872 主管
喵喵 女 22 网站运营部 13894658872 员工
妞妞 女 25 网站运营部 13894658872 员工
大熊 男 28 网站运营部 13894658872 员工
- 删除姓名为’妞妞’的数据
use db_employeeinfo;
DELETE FROM tb_employer WHERE name='妞妞';
结果:
张三 男 20 后勤保障部 13078931129 员工
王丽 女 32 网站运营部 13894658872 主管
喵喵 女 22 网站运营部 13894658872 员工
大熊 男 28 网站运营部 13894658872 员工
二、JDBC连接数据库
要访问MySQL数据库,首先要加载数据库的驱动程序(只需要在第一次访问数据库时加载一次),然后每次访问数据时创建一个Connection对象,接着执行操作数据库的SQL语句,最后在完成数据库操作后销毁前面创建的Connection对象。
1、下载jar包并添加到指定项目路径中
我们使用JDBC连接数据库之前,需要先下载jar包,我们使用最新版本的MySQL,因此使用最新版本的jar包。
(1)在官网下载jar包
一般最新版的MySQL需要下载最新版的jar包,老版本的jar包可能因为不兼容的问题导致驱动安装失败,最好去官网直接下载最新版的:https://dev.mysql.com/downloads/connector/j/
(2)解压,并把jar包放到指定的项目目录下
(3)添加路径
(4)查看驱动
这样驱动安装好了。
(5)查看数据库
在终端登录mysql,输入:
mysql -u root -p
# 按照提示输入mysql安装密码
12
在查看现有的数据库:
show databases;
1
确认你要连接的数据库是否存在,不存在的话可以换一个数据库,也可以新建数据库:
create database my_exercise;
1
创建完毕后,在连接这个数据库就OK了。
2、加载驱动
(1)驱动的版本问题
MySQL 8.0 以上版本的数据库连接有所不同:
- 1、MySQL 8.0 以上版本驱动包版本 mysql-connector-java-8.0.16.jar。
- 2、
com.mysql.jdbc.Driver
更换为com.mysql.cj.jdbc.Driver
。 - MySQL 8.0 以上版本不需要建立 SSL 连接的,需要显示关闭。
- allowPublicKeyRetrieval=true 允许客户端从服务器获取公钥。
- 最后还需要设置 CST。
加载驱动与连接数据库方式如下:
(2)加载MySQL数据库驱动程序
加载MySQL数据库驱动程序(包名为mysql-connector-java-8.0.22.jar)的代码如下:
try{
Class.forName("com.mysql.cj.jdbc.Driver");
}catch(ClassNotFoundException e){
e.printStackTrace();
}
3、创建连接
加载要连接的数据库驱动程序后,Java会自动将驱动程序的实例注册到Drivermanager类中,这时可以通过DriverManager类的getConnection()方法与指定的数据库建立连接,getConnection()方法的语法如下:
getConnection(String url,String user,String password)
1
- url:要连接的数据库的URL;
- user:连接数据库的用户名;
- password:连接数据库的密码;
例如,使用DriverManager类的getConnection()方法,与本地的MySQL数据库建立连接:
Drivermanager.getConnection("jdbc:mysql://127.0.0.1:3306/test","root","password");
1
- 本地的Ip地址:127.0.0.1
- MySQL默认端口:3306
- 数据库名称:test
- 用户名:root
- 密码:password
4、JDBC连接数据库的实现
实例5:JDBC连接数据库db_employeeinfo数据库
package JDBC;
import java.sql.*; //导入java.sql包
public class Conn { // 创建类Conn
Connection con; // 声明Connection对象
public Connection getConnection() { // 建立返回值为Connection的方法
try { // 加载数据库驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try { // 通过访问数据库的URL获取数据库连接对象
con = DriverManager.getConnection("jdbc:mysql:"
+ "//localhost:3306/db_employeeinfo", "root", "19930122");
System.out.println("数据库连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
return con; // 按方法要求返回一个Connection对象
}
public static void main(String[] args) { // 主方法
Conn c = new Conn(); // 创建本类对象
c.getConnection(); // 调用连接数据库的方法
}
}
Console;
数据库驱动加载成功
数据库连接成功
⚠️说明:
- 本实例中将连接数据库作为单独的一个方法,并以Connection对象作为返回值,这样写的好
处是在遇到对数据库执行操作的程序时,可直接调用Conn类的getConnectionO方法获取连接,增加了代码的重用性。 - 加载数据库驱动程序之前,首先需要确定数据库驱动类是否成功加载到程序中,如果没有加
载,可以按以下步骤加载,此处以加载MySQL数据库的驱动包为例:
选中当前项目,单击右键,选择 Build Path→Build Path菜单项即可。
三、JDBC操作数据库
1、数据查询
数据查询主要通过Statement接口和ResultSet接口实现。其中,Statement接口用来执行SQL语句,ResultSet接口用来存储查询结果。下面我们通过一个例子来演示如何查询数据表中的数据,创建本查询时,需要先查自己的数据库中是否有db_employeeinfo,查询db_employeeinfo数据库中是否存在tb_employer表。如果不存在请查看本文第一章的第2小节(数据库常用操作)来建表。
实例6:查询表中数据并遍历查询结果
使用getConnection()方法获取与数据库的连接,在主方法中查询数据表tb_employer中的数据,并把查询结果存储癌ResultSet对象中,使用ResultSet接口中的方法遍历查询的结果:
使用sql语句查询tb_employer表信息如下:
use db_employeeinfo;
select * from tb_employer;
结果:
张三 男 20 后勤保障部 13078931129 员工
王丽 女 28 网站运营部 13894658872 主管
喵喵 女 22 网站运营部 13894658872 员工
妞妞 女 25 网站运营部 13894658872 员工
大熊 男 28 网站运营部 13894658872 员工
我们来使用JDBC查询数据库中的数据:
package JDBC;
import java.sql.*; //导入java.sql包
//创建类Conn
public class Conn {
// 声明Connection对象
Connection con;
// 建立返回值为Connection的方法
public Connection getConnection() {
// 加载数据库驱动类
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过访问数据库的URL获取数据库连接对象
try {
con = DriverManager.getConnection("jdbc:mysql:"
+ "//localhost:3306/db_employeeinfo", "root", "19930122");
System.out.println("数据库连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
// 按方法要求返回一个Connection对象
return con;
}
public static void main(String[] args) throws ClassNotFoundException, SQLException { // 主方法
Conn c = new Conn(); // 创建本类对象
Connection con = null;
Statement stmt = null;
ResultSet res = null;
try {
//与数据库连接
con = c.getConnection();
//实例化Ststement对象
stmt = con.createStatement();
//执行SQL的查询语句并返回结果
res = stmt.executeQuery("select * from tb_employer");
//获取查询数据
while(res.next()) {
String name = res.getString("name");
String sex= res.getString("sex");
String age = res.getString("age");
String dept = res.getString("dept");
String phone = res.getString("phone");
String remark = res.getString("remark");
//打印查询结果
System.out.print("\n姓名:"+ name);
System.out.print("性别:"+ sex);
System.out.print("年龄:"+ age);
System.out.print("部门:"+ dept);
System.out.print("电话:"+ phone);
System.out.print("职位:"+ remark);
}
}
catch(Exception e) {
e.printStackTrace();
}
//查询完毕后依次关闭数据库连接
finally {
if(res != null) {
try {
res.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
}
}
}
Console:
数据库驱动加载成功
数据库连接成功
姓名:张三性别:男年龄:20部门:后勤保障部电话:13078931129职位:员工
姓名:王丽性别:女年龄:28部门:网站运营部电话:13894658872职位:主管
姓名:喵喵性别:女年龄:22部门:网站运营部电话:13894658872职位:员工
姓名:妞妞性别:女年龄:25部门:网站运营部电话:13894658872职位:员工
姓名:大熊性别:男年龄:28部门:网站运营部电话:13894658872职位:员工
2、动态查询
向数据库发送一个SQL语句,数据库中的SQL解释器负责把SQL语句生成底层的内部命令,然后执行这个命令,进而完成相关的数据操作。
如果不断地向数据库发送SQL语句,那么就会增加数据库中SQL解释器的负担,从而降低SQL语句的执行速度。为了避免这类情况,可以通过Connection 对象的 prepareStatement(String sql)
方法对SQL 语句进行预处理,生成数据库底层的内部命令,并将这个命令封装在PreparedStatement 对象中,通过调用 PreparedStatement 对象的相应方法执行底层的内部命令,这样就可以减轻数据库中SQL解释器的负担,提高执行SQL语句的速度。
对SQL进行预处理时,可以使用通配符"?"来代替任何的字段值。例如:
PreparedStatement ps = con.prepareStatement("select *from tb_stu where name =?");
在执行预处理语句前,必须用相应方法来设置通配符所表示的值。例如:
ps.setString((1,"小王");
上述语句中的“1”表示从左向右的第几个通配符,“小王”表示通配符的值。将通配符的值设置为小王后,功能等同于:
PreparedStatement ps = con.prepareStatement("select* from tb_stu where name="小王");
尽管书写两条语句看似麻烦了些,但使用预处理语句可以使应用程序更容易动态地设定SQL语句的字段值,从而实现动态查询的功能。
实例7:使用动态查询获取指定姓名的人员信息
实现动态的获取指定姓名的人员信息,这里以查询姓名为“王丽”的员工为例:
package JDBC;
import java.sql.*; //导入java.sql包
//创建类Conn
public class Conn {
// 声明Connection对象
Connection con;
// 建立返回值为Connection的方法
public Connection getConnection() {
// 加载数据库驱动类
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过访问数据库的URL获取数据库连接对象
try {
con = DriverManager.getConnection("jdbc:mysql:"
+ "//localhost:3306/db_employeeinfo", "root", "19930122");
System.out.println("数据库连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
// 按方法要求返回一个Connection对象
return con;
}
public static void main(String[] args){ // 主方法
Conn c = new Conn(); // 创建本类对象
Connection con = null;
ResultSet res = null;
PreparedStatement ps = null;
try {
con = c.getConnection();
ps = con.prepareStatement("select * from tb_employer where Name=?"); //查询语句
ps.setString(1,"妞妞"); //设置参数
res = ps.executeQuery();
//建立查询并打印结果
while(res.next()) {
String name = res.getString("name");
String sex= res.getString("sex");
String age = res.getString("age");
String dept = res.getString("dept");
String phone = res.getString("phone");
String remark = res.getString("remark");
System.out.print("姓名:"+ name);
System.out.print("性别:"+ sex);
System.out.print("年龄:"+ age);
System.out.print("部门:"+ dept);
System.out.print("电话:"+ phone);
System.out.print("职位:"+ remark);
}
}
catch(Exception e) {
e.printStackTrace();
}
//查询完毕后依次关闭数据库连接
finally {
if(ps != null) {
try {
ps.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(res != null) {
try {
res.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
}
}
}
Console:
数据库驱动加载成功
数据库连接成功
姓名:妞妞性别:女年龄:25部门:网站运营部电话:13894658872职位:员工
3、添加、修改、删除
通过SQL语句,除了可以查询数据外,还可以对数据进行添加、修改和删除等操作。Java中可以通过PreparedStatement对象动态的对数据表中原数据进行修改,并通过executeUpdate()方法进行更新语句的操作。
实例8:动态添加、修改和删除数据表中的数据
通过预处理语句动态的对数据表tb_employer中的数据进行添加、修改和删除的操作,然后通过遍历结果集,对比操作前后的tb_employer表的数据变化:
package JDBC;
import java.sql.*; //导入java.sql包
//创建类Conn
public class Conn {
// 声明Connection对象
Connection con;
// 建立返回值为Connection的方法
public Connection getConnection() {
// 加载数据库驱动类
try {
Class.forName("com.mysql.cj.jdbc.Driver");
System.out.println("数据库驱动加载成功");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 通过访问数据库的URL获取数据库连接对象
try {
con = DriverManager.getConnection("jdbc:mysql:"
+ "//localhost:3306/db_employeeinfo", "root", "19930122");
System.out.println("数据库连接成功");
} catch (SQLException e) {
e.printStackTrace();
}
// 按方法要求返回一个Connection对象
return con;
}
public static void main(String[] args) throws ClassNotFoundException, SQLException{ // 主方法
Conn c = new Conn(); // 创建本类对象
Connection con = null;
Statement stmt = null;
ResultSet res = null;
PreparedStatement ps = null;
con = c.getConnection();
stmt = con.createStatement();
ps = con.prepareStatement("select * from tb_employer"); //查询语句
res = ps.executeQuery();
try {
System.out.println("\n执行增加、修改、删除前的数据:");
//遍历执行增加、修改、删除前的结果集
while(res.next()) {
String name = res.getString("name");
String sex= res.getString("sex");
String age = res.getString("age");
String dept = res.getString("dept");
String phone = res.getString("phone");
String remark = res.getString("remark");
System.out.print("\n姓名:"+ name);
System.out.print(" 性别:"+ sex);
System.out.print(" 年龄:"+ age);
System.out.print(" 部门:"+ dept);
System.out.print(" 电话:"+ phone);
System.out.print(" 职位:"+ remark);
}
//向数据表tb_employer中添加5列值:id,birthday,salary
ps = con.prepareStatement("insert into tb_employer(name,sex,age,dept,phone,remark) values(?,?,?,?,?,?)");
//防止要插入的数据反复插入,需要在插入之前确定表中没有这个信息
stmt.executeUpdate("delete from tb_employer where name = '老钱'");
//添加值
ps.setString(1, "老钱");
ps.setString(2, "男");
ps.setString(3,"33");
ps.setString(4, "销售部");
ps.setString(5, "1899999834");
ps.setString(6, "主管");
ps.executeUpdate();
//根据指定的姓名动态的更改数据表
ps = con.prepareStatement("update tb_employer set remark = ? where name = ?");
//更新数据
ps.setString(1, "副主管");
ps.setString(2,"大熊");
ps.executeUpdate();
//删除数据
stmt.executeUpdate("delete from tb_employer where name = '妞妞'");
//查询修改后的数据表tb_employer
ps = con.prepareStatement("select * from tb_employer");
res = ps.executeQuery();
System.out.println("\n\n执行增加、修改、删除后的数据:");
//遍历执行增加、修改、删除前的结果集
while(res.next()) {
String name = res.getString("name");
String sex= res.getString("sex");
String age = res.getString("age");
String dept = res.getString("dept");
String phone = res.getString("phone");
String remark = res.getString("remark");
System.out.print("\n姓名:"+ name);
System.out.print(" 性别:"+ sex);
System.out.print(" 年龄:"+ age);
System.out.print(" 部门:"+ dept);
System.out.print(" 电话:"+ phone);
System.out.print(" 职位:"+ remark);
}
}
catch(Exception e) {
e.printStackTrace();
}
//查询完毕后依次关闭数据库连接
finally {
if(ps != null) {
try {
ps.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(stmt != null) {
try {
stmt.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(res != null) {
try {
res.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
if(con != null) {
try {
con.close();
}
catch(SQLException e) {
e.printStackTrace();
}
}
}
}
}
Console:
数据库驱动加载成功
数据库连接成功
执行增加、修改、删除前的数据:
姓名:张三 性别:男 年龄:20 部门:后勤保障部 电话:13078931129 职位:员工
姓名:王丽 性别:女 年龄:28 部门:网站运营部 电话:13894658872 职位:主管
姓名:喵喵 性别:女 年龄:22 部门:网站运营部 电话:13894658872 职位:员工
姓名:大熊 性别:男 年龄:28 部门:网站运营部 电话:13894658872 职位:副主管
执行增加、修改、删除后的数据:
姓名:张三 性别:男 年龄:20 部门:后勤保障部 电话:13078931129 职位:员工
姓名:王丽 性别:女 年龄:28 部门:网站运营部 电话:13894658872 职位:主管
姓名:喵喵 性别:女 年龄:22 部门:网站运营部 电话:13894658872 职位:员工
姓名:大熊 性别:男 年龄:28 部门:网站运营部 电话:13894658872 职位:副主管
姓名:老钱 性别:男 年龄:33 部门:销售部 电话:1899999834 职位:主管
四、小结
1、如果遇到无法连接数据库的问题
- 查看自己的mySQL服务是否时启动状态
- 检查连接的url是否正确
- 排除jar包的问题:
- 不在当前项目文件夹中
- 版本不兼容
- 查看要连接的数据库是否存在:
- 在终端登录MySQL查看当前的数据库:
- mysql -u root -p
- show databases;
- 在navicat新建查询:
- 语法同上
- 不存在要连接的数据库,可以新建一个:
- Create database db_employeeinfo;
- 在终端登录MySQL查看当前的数据库:
2、SQL语句的使用
SQL语句是关系型数据库的专用查询语言,不同数据库的SQL语句会有细微的差距,但是大体上的语法是相同的。因此JDBC技术对所有数据库使用同一套接口,不区分SQL语句。本部分的内容并没有专门再去讲SQL语句的内容,有需要请自行补充相关基础知识。