这里声明:本文是我阅读了动力节点文档所做的学习笔记,很多内容摘自动力节点。
1. 什么是JDBC
JDBC(Java DataBase Connectivity)就是Java数据库连接,说白了就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。
2. JDBC原理
早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司数据库服务器的API。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动!
JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。
当然还有第三方公司专门为某一数据库提供驱动,这样的驱动往往不是开源免费的!
3. JDBC编程六步
JDBC编程的步骤是很固定的,通常包含以下六步:
- 第一步:注册驱动
作用一:将 JDBC 驱动程序从硬盘上的文件系统中加载到内存中。
作用二:使得 DriverManager 可以通过一个统一的接口来管理该驱动程序的所有连接操作。
- 第二步:获取数据库连接
获取java.sql.Connection对象,该对象的创建标志着mysql进程和jvm进程之间的通道打开了。
- 第三步:获取数据库操作对象
获取java.sql.Statement对象,该对象负责将SQL语句发送给数据库,数据库负责执行该SQL语句。
- 第四步:执行SQL语句
执行具体的SQL语句,例如:insert delete update select等。
- 第五步:处理查询结果集
如果之前的操作是DQL查询语句,才会有处理查询结果集这一步。
执行DQL语句通常会返回查询结果集对象:java.sql.ResultSet。
对于ResultSet查询结果集来说,通常的操作是针对查询结果集进行结果集的遍历。
- 第六步:释放资源
释放资源可以避免资源的浪费。在 JDBC 编程中,每次使用完 Connection、Statement、ResultSet 等资源后,都需要显式地调用对应的 close() 方法来释放资源,避免资源的浪费。
释放资源可以避免出现内存泄露问题。在 Java 中,当一个对象不再被引用时,会被 JVM 的垃圾回收机制进行回收。但是在 JDBC 编程中,如果不显式地释放资源,那么这些资源就不会被 JVM 的垃圾回收机制自动回收,从而导致内存泄露问题。
4. JDBC完成新增操作
新增操作就是让数据库执行insert语句。通过这个操作来学习一下JDBC编程的每一步。刚开始编写JDBC代码的时候,建议使用文本编辑器,先不借助任何IDE。
在进行操作之前 我们们现在数据库建表:
建表语句我就不多写了
插入数据:
4.1. JDBC编程第一步:注册驱动
注册驱动有两个作用:
- 将 JDBC 驱动程序从硬盘上的文件系统中加载到内存。
- 让 DriverManager 可以通过一个统一的接口来管理该驱动程序的所有连接操作。
API帮助文档:
代码如下:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
public class JDBCTest01 {
public static void main(String[] args){
try {
// 1. 注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver(); // 创建MySQL驱动对象
DriverManager.registerDriver(driver); // 完成驱动注册
} catch(SQLException e){
e.printStackTrace();
}
}
}
4.2. JDBC编程第二步:获取连接
获取java.sql.Connection对象,该对象的创建标志着mysql进程和jvm进程之间的通道打开了。
4.2.1. 代码实现
API帮助文档:
代码如下:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
public class JDBCTest01 {
public static void main(String[] args){
try {
// 1. 注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver(); // 创建MySQL驱动对象
DriverManager.registerDriver(driver); // 完成驱动注册
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/jdbc";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
System.out.println("连接对象:" + conn);
} catch(SQLException e){
e.printStackTrace();
}
}
}
执行结果如下:
看到以上的输出结果,表示数据库已经连接成功了。
通过以上程序的输出结果得知:com.mysql.cj.jdbc.ConnectionImpl是java.sql.Connection接口的实现类,大家可以想象一下,如果换成Oracle数据库的话,这个实现类的类名是不是就会换一个呢?答案是肯定的。不过对于我们来说是不需要关心具体实现类的,因为后续的代码都是直接面向java.sql.Connection接口来调用方法的。面向接口编程在这里体现的淋漓尽致。确实降低了耦合度。
4.2.2. 什么是URL
URL 是统一资源定位符 (Uniform Resource Locator) 的缩写,是互联网上标识、定位、访问资源的字符串。它可以用来指定互联网上各种类型的资源的位置,如网页、图片、视频等。
URL 通常由协议、服务器名、服务器端口、路径和查询字符串组成。其中:
- 协议是规定了访问资源所采用的通信协议,例如 HTTP、HTTPS、FTP 等;
- 服务器名是资源所在的服务器主机名或 IP 地址,可以是域名或 IP 地址;
- 服务器端口是资源所在的服务器的端口号;
- 路径是资源所在的服务器上的路径、文件名等信息;
- 查询字符串是向服务器提交的参数信息,用来定位更具体的资源。
URL 在互联网中广泛应用,比如在浏览器中输入 URL 来访问网页或下载文件,在网站开发中使用 URL 来访问 API 接口或文件,在移动应用和桌面应用中使用 URL 来访问应用内部的页面或功能,在搜索引擎中使用 URL 来爬取网页内容等等。
总之,URL 是互联网上所有资源的唯一识别标识,是互联网通信的基础和核心技术之一。
4.2.3. JDBC连接MySQL时的URL格式
JDBC URL 是在使用 JDBC 连接数据库时的一个 URL 字符串,它用来标识要连接的数据库的位置、认证信息和其他配置参数等。JDBC URL 的格式可以因数据库类型而异,但通常包括以下几个部分:
- 协议:表示要使用的数据库管理系统(DBMS)的类型,如
jdbc:mysql
表示要使用 MySQL 数据库,jdbc:postgresql
表示要使用 PostgreSQL 数据库。 - 主机地址和端口号:表示要连接的数据库所在的服务器的 IP 地址或域名,以及数据库所在服务器监听的端口号。
- 数据库名称:表示要连接的数据库的名称。
- 其他可选参数:这些参数包括连接的超时时间、使用的字符集、连接池相关配置等。
例如,连接 MySQL 数据库的 JDBC URL 的格式一般如下:
jdbc:mysql://<host>:<port>/<database_name>?<connection_parameters>
其中:
<host>
是 MySQL 数据库服务器的主机名或 IP 地址;<port>
是 MySQL 服务器的端口号(默认为 3306);<database_name>
是要连接的数据库名称;<connection_parameters>
包括连接的额外参数,例如用户名、密码、字符集等。
JDBC URL 是连接数据库的关键,通过 JDBC URL,应用程序可以通过特定的 JDBC 驱动程序与数据库服务器进行通信,从而实现与数据库的交互。在开发 Web 应用和桌面应用时,使用 JDBC URL 可以轻松地连接和操作各种类型的数据库,例如 MySQL、PostgreSQL、Oracle 等。
以下是一个常见的JDBC MySQL URL:
jdbc:mysql://localhost:3306/jdbc
jdbc:mysql://
是协议
localhost
表示连接本地主机的MySQL数据库,也可以写作127.0.0.1
3306
是MySQL数据库的端口号
jdbc
是数据库实例名
4.3. JDBC编程第三步:获取数据库操作对象
数据库操作对象是这个接口:java.sql.Statement。这个对象负责将SQL语句发送给数据库服务器,服务器接收到SQL后进行编译,然后执行SQL。
API帮助文档如下:
获取数据库操作对象代码如下:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest01 {
public static void main(String[] args){
try {
// 1. 注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver(); // 创建MySQL驱动对象
DriverManager.registerDriver(driver); // 完成驱动注册
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
Statement stmt = conn.createStatement();
System.out.println("数据库操作对象stmt = " + stmt);
} catch(SQLException e){
e.printStackTrace();
}
}
}
执行结果如下:
同样可以看到:java.sql.Statement接口在MySQL驱动中的实现类是:com.mysql.cj.jdbc.StatementImpl。不过我们同样是不需要关心这个具体的实现类。因为后续的代码仍然是面向Statement接口写代码的。
另外,要知道的是通过一个Connection对象是可以创建多个Statement对象的:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest01 {
public static void main(String[] args){
try {
// 1. 注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver(); // 创建MySQL驱动对象
DriverManager.registerDriver(driver); // 完成驱动注册
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
Statement stmt = conn.createStatement();
System.out.println("数据库操作对象stmt = " + stmt);
Statement stmt2 = conn.createStatement();
System.out.println("数据库操作对象stmt2 = " + stmt2);
} catch(SQLException e){
e.printStackTrace();
}
}
}
执行结果:
4.4. JDBC编程第四步:执行SQL
当获取到Statement对象后,调用这个接口中的相关方法即可执行SQL语句。
API帮助文档如下:
该方法的参数是一个SQL语句,只要将insert语句传递过来即可。当执行executeUpdate(sql)方法时,JDBC会将sql语句发送给数据库服务器,数据库服务器对SQL语句进行编译,然后执行SQL。
该方法的返回值是int类型,返回值的含义是:影响了数据库表当中几条记录。例如:返回1表示1条数据插入成功,返回2表示2条数据插入成功,以此类推。如果一条也没有插入,则返回0。
该方法适合执行的SQL语句是DML,包括:insert delete update。
这里只需要记住:增删改的返回值为int整形 代表影响的行数
代码实现如下:
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest01 {
public static void main(String[] args){
try {
// 1. 注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver(); // 创建MySQL驱动对象
DriverManager.registerDriver(driver); // 完成驱动注册
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
Statement stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "insert into t_user(name,password,realname,gender,tel) values('tangsanzang','123','唐三藏','男','12566568956')"; // sql语句最后的分号';'可以不写。
int count = stmt.executeUpdate(sql);
System.out.println("插入了" + count + "条记录");
} catch(SQLException e){
e.printStackTrace();
}
}
}
执行结果如下:
数据库表变化了:
4.5. JDBC编程第六步:释放资源
第五步去哪里了?第五步是处理查询结果集,以上操作不是select语句,所以第五步直接跳过,直接先看一下第六步释放资源。【后面学习查询语句的时候,再详细看第五步】
4.5.1. 为什么要释放资源
在 JDBC 编程中,建立数据库连接、创建 Statement 对象等操作都需要申请系统资源,例如打开网络端口、申请内存等。为了避免占用过多的系统资源和避免出现内存泄漏等问题,我们需要在使用完资源后及时释放它们。
4.5.2. 释放资源的原则
原则1:在finally语句块中释放
- 建议在finally语句块中释放,因为程序执行过程中如果出现了异常,finally语句块中的代码是一定会执行的。也就是说:我们需要保证程序在执行过程中,不管是否出现了异常,最后的关闭是一定要执行的。当然了,也可以使用Java7的新特性:Try-with-resources。Try-with-resources 是 Java 7 引入的新特性。它简化了资源管理的代码实现,可以自动释放资源,减少了代码出错的可能性,同时也可以提供更好的代码可读性和可维护性。
原则2:释放有顺序
- 从小到大依次释放,创建的时候,先创建Connection,再创建Statement。那么关闭的时候,先关闭Statement,再关闭Connection。
原则3:分别进行try...catch...
- 关闭的时候调用close()方法,该方法有异常需要处理,建议分别对齐try...catch...进行异常捕获。如果只编写一个try...catch...进行一块捕获,在关闭过程中,如果某个关闭失败,会影响下一个资源的关闭。
4.5.3. 代码如何实现
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest01 {
public static void main(String[] args){
Connection conn = null;
Statement stmt = null;
try {
// 1. 注册驱动
Driver driver = new com.mysql.cj.jdbc.Driver(); // 创建MySQL驱动对象
DriverManager.registerDriver(driver); // 完成驱动注册
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "insert into t_user(name,password,realname,gender,tel) values('tangsanzang','123','唐三藏','男','12566568956')"; // sql语句最后的分号';'可以不写。
int count = stmt.executeUpdate(sql);
System.out.println("插入了" + count + "条记录");
} catch(SQLException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
5. 注册驱动的常用方式
上面在注册驱动的时候,执行了这样的代码:
java.sql.Driver driver = new com.mysql.cj.jdbc.Driver();
java.sql.DriverManager.registerDriver(driver);
这种方式是自己new驱动对象,然后调用DriverManager的registerDriver()方法来完成驱动注册,还有另一种方式,并且这种方式是常用的:
Class.forName("com.mysql.cj.jdbc.Driver");
为什么这种方式常用?
- 第一:代码少了很多。
- 第二:这种方式可以很方便的将
com.mysql.cj.jdbc.Driver
类名配置到属性文件当中。
实现原理是什么?找一下com.mysql.cj.jdbc.Driver
的源码:
通过源码不难发现,在com.mysql.cj.jdbc.Driver
类中有一个静态代码块,在这个静态代码块中调用了java.sql.DriverManager.registerDriver(new Driver());
完成了驱动的注册。而Class.forName("com.mysql.cj.jdbc.Driver");
代码的作用就是让com.mysql.cj.jdbc.Driver
类完成加载,执行它的静态代码块。 类加载静态代码块执行驱动注册成功。
上面的增添操作的代码就可以修改为:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
public class JDBCTest02 {
public static void main(String[] args){
Connection conn = null;
Statement stmt = null;
try {
// 1. 注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 获取连接
String url = "jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8";
String user = "root";
String password = "123456";
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "insert into t_user(name,password,realname,gender,tel) values('tangsanzang','123','唐三藏','男','12566568956')"; // sql语句最后的分号';'可以不写。
int count = stmt.executeUpdate(sql);
System.out.println("插入了" + count + "条记录");
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
执行结果:
数据库表中数据也新增了:
6. 动态配置连接数据库的信息
为了程序的通用性,为了切换数据库的时候不需要修改Java程序,为了符合OCP开闭原则,建议将连接数据库的信息配置到属性文件中,例如:
jdbc.properties:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbc?useUnicode=true&serverTimezone=Asia/Shanghai&useSSL=true&characterEncoding=utf-8
user=root
password=123456
然后使用IO流读取属性文件,动态获取连接数据库的信息:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
public class JDBCTest04 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "insert into t_user(name,password,realname,gender,tel) values('tangsanzang','123','唐三藏','男','12566568956')"; // sql语句最后的分号';'可以不写。
int count = stmt.executeUpdate(sql);
System.out.println("插入了" + count + "条记录");
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
7. JDBC完成删除操作
删除操作就是执行delete语句。仍然调用Statement接口的executeUpdate(sql)方法即可。
业务要求:将id是15,16,17的数据删除。
删除前的数据:
代码如下:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
public class JDBCTest08 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "delete from t_user where id in(15, 16, 17)";
int count = stmt.executeUpdate(sql);
System.out.println("删除了" + count + "条记录");
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
8. JDBC完成修改操作
修改操作就是执行update语句。仍然调用Statement接口的executeUpdate(sql)方法即可。
业务要求:将name是tangsanzang的真实姓名修改为唐僧。
修改前的数据:
代码如下:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
public class JDBCTest07 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "update t_user set realname='唐僧' where name='tangsanzang'";
int count = stmt.executeUpdate(sql);
System.out.println("更新了" + count + "条记录");
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
9. JDBC的查询操作(重中之重)
ResultSet 是 JDBC (Java 数据库连接) API 提供的接口,它用于表示 SQL 查询的结果集。ResultSet 对象中包含了查询结果的所有行,可以通过 next() 方法逐行地获取并处理每一行的数据。它最常用于执行 SELECT 语句查询出来的结果集。
ResultSet 的遍历是基于 JDBC 的流式处理机制的,即一行一行地获取结果,避免将所有结果全部取出后再进行处理导致内存溢出问题。
在使用 ResultSet 遍历查询结果时,一般会采用以下步骤:
- 执行 SQL 查询,获取 ResultSet 对象。
- 使用 ResultSet 的 next() 方法移动游标指向结果集的下一行,判断是否有更多的数据行。
- 如果有更多的数据行,则使用 ResultSet 对象提供的 getXXX() 方法获取当前行的各个字段(XXX 表示不同的数据类型)。例如,getLong("id") 方法用于获取当前行的 id 列对应的 Long 类型的值。
- 处理当前行的数据,例如将其存入 Java 对象中。
- 重复执行步骤 2~4,直到结果集中的所有行都被遍历完毕。
- 调用 ResultSet 的 close() 方法释放资源。
需要注意的是,在使用完 ResultSet 对象之后,需要及时关闭它,以释放数据库资源并避免潜在的内存泄漏问题。否则,如果在多个线程中打开了多个 ResultSet 对象,并且没有正确关闭它们的话,可能会导致数据库连接过多,从而影响系统的稳定性和性能。
9.1. 通过列索引获取数据(以String类型获取)
需求:获取t_user表中所有数据,在控制台打印输出每一行的数据。
select id,name,password,realname,gender,tel from t_user;
要查询的数据如下图:
代码如下(重点关注第4步 第5步 第6步):
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
import java.sql.ResultSet;
public class JDBCTest09 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "select id,name,password,realname,gender,tel from t_user";
rs = stmt.executeQuery(sql);
// 5. 处理查询结果集(这里的处理方式就是:遍历所有数据并输出)
while(rs.next()){
String id = rs.getString(1);
String name = rs.getString(2);
String pwd = rs.getString(3);
String realname = rs.getString(4);
String gender = rs.getString(5);
String tel = rs.getString(6);
System.out.println(id + "\t" + name + "\t" + pwd + "\t" + realname + "\t" + gender + "\t" + tel);
}
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
执行结果如下:
代码解读:
// 4. 执行SQL语句
String sql = "select id,name,password,realname,gender,tel from t_user";
rs = stmt.executeQuery(sql);
执行insert delete update语句的时候,调用Statement接口的executeUpdate()方法。
执行select语句的时候,调用Statement接口的executeQuery()方法。执行select语句后返回结果集对象:ResultSet。
代码解读:
// 5. 处理查询结果集(这里的处理方式就是:遍历所有数据并输出)
while(rs.next()){
String id = rs.getString(1);
String name = rs.getString(2);
String pwd = rs.getString(3);
String realname = rs.getString(4);
String gender = rs.getString(5);
String tel = rs.getString(6);
System.out.println(id + "\t" + name + "\t" + pwd + "\t" + realname + "\t" + gender + "\t" + tel);
}
- rs.next() 将游标移动到下一行,如果移动后指向的这一行有数据则返回true,没有数据则返回false。
- while循环体当中的代码是处理当前游标指向的这一行的数据。(注意:是处理的一行数据)
- rs.getString(int columnIndex) 其中 int columnIndex 是查询结果的列下标,列下标从1开始,以1递增。
- rs.getString(...) 方法在执行时,不管底层数据库中的数据类型是什么,统一以字符串String类型来获取。
代码解读:
// 6. 释放资源
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
ResultSet最终也是需要关闭的。先关闭ResultSet,再关闭Statement,最后关闭Connection。
9.2. 通过列名获取数据(以String类型获取)
获取当前行的数据,不仅可以通过列下标获取,还可以通过查询结果的列名来获取,通常这种方式是被推荐的,因为可读性好。
例如这样的SQL:
select id, name as username, realname from t_user;
执行结果是:
我们可以按照查询结果的列名来获取数据:
注意:是根据查询结果的列名,而不是表中的列名。以上查询的时候将字段name起别名username了,所以要根据username来获取,而不能再根据name来获取了。
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
import java.sql.ResultSet;
public class JDBCTest10 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "select id,name as username,realname from t_user";
rs = stmt.executeQuery(sql);
// 5. 处理查询结果集(这里的处理方式就是:遍历所有数据并输出)
while(rs.next()){
String id = rs.getString("id");
String name = rs.getString("username");
String realname = rs.getString("realname");
System.out.println(id + "\t" + name + "\t" + realname);
}
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
执行结果如下:
如果将上面代码中rs.getString("username")
修改为rs.getString("name")
,执行就会出现以下错误:
提示name列是不存在的。所以一定是根据查询结果中的列名来获取,而不是表中原始的列名。
9.3. 以指定的类型获取数据
前面的程序可以看到,不管数据库表中是什么数据类型,都以String类型返回。当然,也能以指定类型返回。
使用PowerDesigner再设计一张商品表:t_product,使用Navicat for MySQL工具准备数据如下:
id以long类型获取,name以String类型获取,price以double类型获取,create_time以java.sql.Date类型获取,代码如下:
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
import java.sql.ResultSet;
public class JDBCTest11 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "select id,name,price,create_time as createTime from t_product";
rs = stmt.executeQuery(sql);
// 5. 处理查询结果集(这里的处理方式就是:遍历所有数据并输出)
while(rs.next()){
long id = rs.getLong("id");
String name = rs.getString("name");
double price = rs.getDouble("price");
java.sql.Date createTime = rs.getDate("createTime");
// 以指定类型获取后是可以直接用的,例如获取到价格后,统一让价格乘以2
System.out.println(id + "\t" + name + "\t" + price * 2 + "\t" + createTime);
}
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
执行结果如下:
10. 获取新增行的主键值
有很多表的主键字段值都是自增的,在某些特殊的业务环境下,当我们插入了新数据后,希望能够获取到这条新数据的主键值,应该如何获取呢?
在 JDBC 中,如果要获取插入数据后的主键值,可以使用 Statement 接口的 executeUpdate() 方法的重载版本,该方法接受一个额外的参数,用于指定是否需要获取自动生成的主键值。然后,通过以下两个步骤获取插入数据后的主键值:
- 在执行 executeUpdate() 方法时指定一个标志位,表示需要返回插入的主键值。
- 调用 Statement 对象的 getGeneratedKeys() 方法,返回一个包含插入的主键值的 ResultSet 对象。
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Connection;
import java.sql.Statement;
import java.util.ResourceBundle;
import java.sql.ResultSet;
public class JDBCTest13 {
public static void main(String[] args){
// 通过以下代码获取属性文件中的配置信息
ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
String driver = bundle.getString("driver");
String url = bundle.getString("url");
String user = bundle.getString("user");
String password = bundle.getString("password");
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
// 1. 注册驱动
Class.forName(driver);
// 2. 获取连接
conn = DriverManager.getConnection(url, user, password);
// 3. 获取数据库操作对象
stmt = conn.createStatement();
// 4. 执行SQL语句
String sql = "insert into t_user(name,password,realname,gender,tel) values('zhangsan','111','张三','男','19856525352')";
// 第一步
int count = stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
// 第二步
rs = stmt.getGeneratedKeys();
if(rs.next()){
int id = rs.getInt(1);
System.out.println("新增数据行的主键值:" + id);
}
} catch(SQLException | ClassNotFoundException e){
e.printStackTrace();
} finally {
// 6. 释放资源
if(rs != null){
try{
rs.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(stmt != null){
try{
stmt.close();
}catch(SQLException e){
e.printStackTrace();
}
}
if(conn != null){
try{
conn.close();
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
}
执行结果如下:
以上代码中,我们将 Statement.RETURN_GENERATED_KEYS 传递给 executeUpdate() 方法,以指定需要获取插入的主键值。然后,通过调用 Statement 对象的 getGeneratedKeys() 方法获取包含插入的主键值的 ResultSet 对象,通过 ResultSet 对象获取主键值。需要注意的是,在使用 Statement 对象的 getGeneratedKeys() 方法获取自动生成的主键值时,主键值的获取方式具有一定的差异,需要根据不同的数据库种类和版本来进行调整。