JDBC的入门及增删改查操作(全面细节讲解)

这里声明:本文是我阅读了动力节点文档所做的学习笔记,很多内容摘自动力节点。

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编程的步骤是很固定的,通常包含以下六步:

  1. 第一步:注册驱动

作用一:将 JDBC 驱动程序从硬盘上的文件系统中加载到内存中。

作用二:使得 DriverManager 可以通过一个统一的接口来管理该驱动程序的所有连接操作。

  1. 第二步:获取数据库连接

获取java.sql.Connection对象,该对象的创建标志着mysql进程和jvm进程之间的通道打开了。

  1. 第三步:获取数据库操作对象

获取java.sql.Statement对象,该对象负责将SQL语句发送给数据库,数据库负责执行该SQL语句。

  1. 第四步:执行SQL语句

执行具体的SQL语句,例如:insert delete update select等。

  1. 第五步:处理查询结果集

如果之前的操作是DQL查询语句,才会有处理查询结果集这一步。

执行DQL语句通常会返回查询结果集对象:java.sql.ResultSet。

对于ResultSet查询结果集来说,通常的操作是针对查询结果集进行结果集的遍历。

  1. 第六步:释放资源

释放资源可以避免资源的浪费。在 JDBC 编程中,每次使用完 Connection、Statement、ResultSet 等资源后,都需要显式地调用对应的 close() 方法来释放资源,避免资源的浪费。

释放资源可以避免出现内存泄露问题。在 Java 中,当一个对象不再被引用时,会被 JVM 的垃圾回收机制进行回收。但是在 JDBC 编程中,如果不显式地释放资源,那么这些资源就不会被 JVM 的垃圾回收机制自动回收,从而导致内存泄露问题。

4. JDBC完成新增操作

新增操作就是让数据库执行insert语句。通过这个操作来学习一下JDBC编程的每一步。刚开始编写JDBC代码的时候,建议使用文本编辑器,先不借助任何IDE。
 

在进行操作之前 我们们现在数据库建表:

建表语句我就不多写了

插入数据:

4.1. JDBC编程第一步:注册驱动

注册驱动有两个作用:

  1. 将 JDBC 驱动程序从硬盘上的文件系统中加载到内存。
  2. 让 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 遍历查询结果时,一般会采用以下步骤:

  1. 执行 SQL 查询,获取 ResultSet 对象。
  2. 使用 ResultSet 的 next() 方法移动游标指向结果集的下一行,判断是否有更多的数据行。
  3. 如果有更多的数据行,则使用 ResultSet 对象提供的 getXXX() 方法获取当前行的各个字段(XXX 表示不同的数据类型)。例如,getLong("id") 方法用于获取当前行的 id 列对应的 Long 类型的值。
  4. 处理当前行的数据,例如将其存入 Java 对象中。
  5. 重复执行步骤 2~4,直到结果集中的所有行都被遍历完毕。
  6. 调用 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() 方法的重载版本,该方法接受一个额外的参数,用于指定是否需要获取自动生成的主键值。然后,通过以下两个步骤获取插入数据后的主键值:

  1. 在执行 executeUpdate() 方法时指定一个标志位,表示需要返回插入的主键值。
  2. 调用 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() 方法获取自动生成的主键值时,主键值的获取方式具有一定的差异,需要根据不同的数据库种类和版本来进行调整。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值