
本文深入探讨了java jdbc操作mysql数据库时常见的“after end of result set”错误,该错误通常源于不当的数据库资源(如`connection`、`preparedstatement`、`resultset`)管理,特别是当这些资源被共享或未及时关闭时。文章分析了导致此问题的代码模式,并提供了三种优化策略:局部化资源、利用内存缓存进行查找以及使用sql join语句,旨在帮助开发者构建更健壮、高效的jdbc应用。
在使用Java JDBC与MySQL数据库交互时,开发者可能会遇到java.sql.SQLException: After end of result set这样的错误。这个错误通常发生在尝试访问一个已经遍历到末尾或者已经被关闭/覆盖的ResultSet对象时。本教程将深入分析导致此问题的原因,并提供几种健壮且高效的解决方案。
在提供的代码示例中,ConnectionMysql类将conexion (Connection), st (PreparedStatement), 和 rs (ResultSet) 作为成员变量。这是导致“After end of result set”错误的核心原因。
让我们详细分析loadOrderList方法:
问题在于: 当loadItemList或loadCustomerList执行时,它们会覆盖ConnectionMysql实例中正在被loadOrderList使用的st和rs成员变量。一旦loadOrderList的循环尝试在下一次迭代中访问其原始的rs(现在已经被覆盖),就会导致“After end of result set”错误,因为它实际上在尝试访问一个不再指向有效结果集或者已经被关闭的结果集。
立即学习“Java免费学习笔记(深入)”;
此外,每次调用loadCustomerList、loadItemList和loadOrderList都会创建一个新的数据库连接,这不仅效率低下,而且如果不及时关闭,还会导致资源泄露。
最直接且根本的解决方案是避免共享Connection、PreparedStatement和ResultSet对象。每个方法都应该在自己的作用域内创建、使用并关闭这些资源。
改进后的ConnectionMysql示例:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
// 假设Cliente, Item, Producto, Pedido是已定义的POJO类
// 为简化示例,这里省略了它们的定义
public class ConnectionMysql {
private String url;
private String user;
private String password;
public ConnectionMysql(String url, String user, String password) {
this.url = url;
this.user = user;
this.password = password;
// 确保MySQL驱动已加载
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
System.err.println("MySQL JDBC Driver not found.");
e.printStackTrace();
}
}
// 辅助方法,用于获取连接
private Connection getConnection() throws SQLException {
return DriverManager.getConnection(this.url, this.user, this.password);
}
public Map<String, Cliente> loadCustomerList() throws SQLException {
Map<String, Cliente> listaClientes = new HashMap<>();
String query = "SELECT * FROM cliente";
// 使用try-with-resources确保资源自动关闭
try (Connection conn = getConnection();
PreparedStatement st = conn.prepareStatement(query);
ResultSet rs = st.executeQuery()) {
while (rs.next()) {
String dni = rs.getString(1);
String nombre = rs.getString(2);
String apellidos = rs.getString(3);
int descuento = rs.getInt(4);
listaClientes.put(dni, new Cliente(dni, nombre, apellidos, descuento));
}
} // conn, st, rs 会在此处自动关闭
return listaClientes;
}
public List<Item> loadItemList() throws SQLException {
List<Item> listaItems = new ArrayList<>();
String query = "SELECT * FROM item;";
try (Connection conn = getConnection();
PreparedStatement st = conn.prepareStatement(query);
ResultSet rs = st.executeQuery()) {
while (rs.next()) {
String nombre = rs.getString(1);
double precio = rs.getDouble(2);
int existencias = rs.getInt(3);
listaItems.add(new Item(nombre, precio, existencias));
}
}
return listaItems;
}
// findClient 和 findItem 不再直接访问数据库,而是依赖于外部提供的列表
// 这需要在调用时先加载所有客户和产品,然后进行内存查找
// 见优化策略二
// loadOrderList 方法也需要修改,以避免在循环中重复查询数据库
// 见优化策略二和三
}
// 示例 POJO 类 (请根据实际情况补充完整)
class Cliente {
String dni;
String nombre;
String apellidos;
int descuento;
public Cliente(String dni, String nombre, String apellidos, int descuento) {
this.dni = dni; this.nombre = nombre; this.apellidos = apellidos; this.descuento = descuento;
}
public String getDni() { return dni; }
}
class Item {
String nombre;
double precio;
int existencias;
public Item(String nombre, double precio, int existencias) {
this.nombre = nombre; this.precio = precio; this.existencias = existencias;
}
public String getNombre() { return nombre; }
}
class Producto extends Item { // 假设Producto继承自Item或有相似结构
public Producto(String nombre, double precio, int existencias) { super(nombre, precio, existencias); }
}
class Pedido {
Producto producto;
int unidad;
Cliente cliente;
public Pedido(Producto producto, int unidad, Cliente cliente) {
this.producto = producto; this.unidad = unidad; this.cliente = cliente;
}
}注意事项:
原代码在loadOrderList的循环中,为每个订单调用findClient和findItem,而这两个方法又会重复地加载整个客户列表和商品列表。这种模式效率极低。更好的方法是先一次性加载所有客户和产品数据到内存中(例如Map),然后在处理订单时直接从内存中查找。
改进后的ConnectionMysql和PruebaComercio示例:
// ... ConnectionMysql 类中 loadCustomerList 和 loadItemList 保持不变 (如上) ...
public class ConnectionMysql {
// ... (构造函数和getConnection方法) ...
public Map<String, Cliente> loadCustomerList() throws SQLException { /* ... 保持不变 ... */ return new HashMap<>(); }
public List<Item> loadItemList() throws SQLException { /* ... 保持不变 ... */ return new ArrayList<>(); }
// loadOrderList 现在依赖于外部提供的客户和产品映射
public List<Pedido> loadOrderList(Map<String, Cliente> allClients, Map<String, Producto> allProducts) throws SQLException {
List<Pedido> listaPedidos = new ArrayList<>();
String query = "SELECT `unidad`, `nombre_producto`, `dni_cliente` FROM pedido WHERE `Numero Factura` IS NULL"; // 假设字段名
try (Connection conn = getConnection();
PreparedStatement st = conn.prepareStatement(query);
ResultSet rs = st.executeQuery()) {
while (rs.next()) {
int unidad = rs.getInt("unidad"); // 根据实际列名获取
String nombreProducto = rs.getString("nombre_producto");
String dniCliente = rs.getString("dni_cliente");
Producto producto = allProducts.get(nombreProducto);
Cliente cliente = allClients.get(dniCliente);
if (producto != null && cliente != null) {
listaPedidos.add(new Pedido(producto, unidad, cliente));
} else {
// 处理找不到客户或产品的情况,例如记录日志
System.err.println("警告: 订单关联的客户或产品未找到. 产品名称: " + nombreProducto + ", 客户DNI: " + dniCliente);
}
}
}
return listaPedidos;
}
}
// 主操作类 PruebaComercio
public class PruebaComercio {
private Map<String, Cliente> listaClientes;
private Map<String, Producto> listaProductos; // 用于存储产品,方便通过名称查找
private List<Pedido> listaPedidos;
// ... 其他成员变量 ...
private ConnectionMysql connection;
public PruebaComercio() throws SQLException {
this.connection = new ConnectionMysql("jdbc:mysql://localhost:3306/db_name", "user", "password"); // 替换为你的数据库信息
// 1. 一次性加载所有客户和产品数据
this.listaClientes = connection.loadCustomerList();
this.listaProductos = new HashMap<>();
for (Item item : connection.loadItemList()) {
this.listaProductos.put(item.getNombre(), (Producto) item); // 假设Item和Producto兼容
}
// 2. 加载订单时,传入已加载的客户和产品列表进行查找
this.listaPedidos = connection.loadOrderList(this.listaClientes, this.listaProductos);
// ... 其他初始化 ...
}
}优点:
最推荐且最高效的方式是使用SQL JOIN语句在数据库层面将相关数据一次性查询出来,而不是在Java代码中进行多次查询和查找。这利用了数据库的优化能力,减少了网络往返次数。
SQL JOIN查询示例:
假设pedido表包含nombre_producto和dni_cliente字段,分别关联item表和cliente表。
SELECT
p.unidad,
i.nombre AS producto_nombre,
i.precio AS producto_precio,
i.existencias AS producto_existencias,
c.dni AS cliente_dni,
c.nombre AS cliente_nombre,
c.apellidos AS cliente_apellidos,
c.descuento AS cliente_descuento
FROM
pedido p
JOIN
item i ON p.`nombre_producto` = i.nombre
JOIN
cliente c ON p.`dni_cliente` = c.dni
WHERE
p.`Numero Factura` IS NULL;改进后的loadOrderList方法(使用JOIN):
public class ConnectionMysql {
// ... (构造函数和getConnection方法) ...
public List<Pedido> loadOrderListWithJoin() throws SQLException {
List<Pedido> listaPedidos = new ArrayList<>();
String query = "SELECT " +
" p.unidad, " +
" i.nombre AS producto_nombre, " +
" i.precio AS producto_precio, " +
" i.existencias AS producto_existencias, " +
" c.dni AS cliente_dni, " +
" c.nombre AS cliente_nombre, " +
" c.apellidos AS cliente_apellidos, " +
" c.descuento AS cliente_descuento " +
"FROM " +
" pedido p " +
"JOIN " +
" item i ON p.`nombre_producto` = i.nombre " +
"JOIN " +
" cliente c ON p.`dni_cliente` = c.dni " +
"WHERE " +
" p.`Numero Factura` IS NULL";
try (Connection conn = getConnection();
PreparedStatement st = conn.prepareStatement(query);
ResultSet rs = st.executeQuery()) {
while (rs.next()) {
// 构建Producto对象
String prodNombre = rs.getString("producto_nombre");
double prodPrecio = rs.getDouble("producto_precio");
int prodExistencias = rs.getInt("producto_existencias");
Producto producto = new Producto(prodNombre, prodPrecio, prodExistencias);
// 构建Cliente对象
String cliDni = rs.getString("cliente_dni");
String cliNombre = rs.getString("cliente_nombre");
String cliApellidos = rs.getString("cliente_apellidos");
int cliDescuento = rs.getInt("cliente_descuento");
Cliente cliente = new Cliente(cliDni, cliNombre, cliApellidos, cliDescuento);
// 获取订单单位
int unidad = rs.getInt("unidad");
listaPedidos.add(new Pedido(producto, unidad, cliente));
}
}
return listaPedidos;
}
}
// 主操作类 PruebaComercio
public class PruebaComercio {
// ... (其他成员变量) ...
private List<Pedido> listaPedidos;
private ConnectionMysql connection;
public PruebaComercio() throws SQLException {
this.connection = new ConnectionMysql("jdbc:mysql://localhost:3306/db_name", "user", "password");
// 直接调用使用JOIN的方法加载订单
this.listaPedidos = connection.loadOrderListWithJoin();
// ... 其他初始化 ...
}
}优点:
“After end of result set”错误通常是由于JDBC资源管理不当引起的。为了避免此类问题并构建健壮的Java数据库应用程序,请遵循以下最佳实践:
通过采纳这些策略,开发者可以有效避免“After end of result set”这类常见错误,并显著提升Java数据库应用的性能和稳定性。
以上就是Java JDBC“结果集结束之后”错误详解与优化策略的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号