Java读取MySQL数据触发After end of result set异常排查
问题描述
尝试通过Java连接MySQL数据库,获取未处理的订单列表,创建包含Cliente(客户)对象、Producto(产品)对象和整数unidad(数量)的Pedido(订单)对象。但调用ConnectionMysql类的loadOrderList()方法时,抛出了“After end of result set”的SQL异常。
相关代码
连接类相关方法
public Map<String, Cliente> loadCustomerList() throws SQLException { conexion = DriverManager.getConnection(this.url, this.user, this.password); Map<String, Cliente> listaClientes = new HashMap<>(); query = "SELECT * FROM cliente"; st = conexion.prepareStatement(query); 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)); } //conexion.close(); return listaClientes; } public List<Item> loadItemList() throws SQLException { conexion = DriverManager.getConnection(this.url, this.user, this.password); List<Item> listaItems = new ArrayList<>(); query = "SELECT * FROM item;"; st = conexion.prepareStatement(query); 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)); } //conexion.close(); return listaItems; } public Cliente findClient(String dni) throws SQLException { Cliente cliente = null; for (Map.Entry<String, Cliente> entry : loadCustomerList().entrySet()) { if(entry.getKey().equals(dni)) { cliente = entry.getValue(); } } return cliente; } public Producto findItem(String nombreProduco) throws SQLException { Producto producto = null; for(Producto item : loadListItems()) { if(item.getNombre().equals(nombreProduco)) { producto = item; } } return producto; } public List<Pedido> loadOrderList() throws SQLException { conexion = DriverManager.getConnection(this.url, this.user, this.password); List<Pedido> listaPedidos = new ArrayList<>(); Cliente cliente =null; Producto producto =null; query = "SELECT * FROM pedido WHERE `Numero Factura` IS NULL"; st = conexion.prepareStatement(query); rs = st.executeQuery(); while (rs.next()) { int unidad = rs.getInt(3); producto = findItem(rs.getString(2)); cliente = findClient(rs.getString(6)); listaPedidos.add(new Pedido(producto, unidad, cliente)); } conexion.close(); return listaPedidos; }
调用代码
public class PruebaComercio { private Map<String, Cliente> listaClientes; private List<Pedido> listaPedidos; private List<Factura> listaFacturas; private Almacen almacen; private ConnectionMysql connection = new ConnectionMysql(); public PruebaComercio() throws SQLException { this.listaClientes = connection.cargarListaClientes(); this.listaPedidos = connection.loadOrderList(); this.listaFacturas = new ArrayList<>(); this.almacen = new Almacen(); } }
异常信息
Exception in thread "main" java.sql.SQLException: After end of result set at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:89) at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:63) at com.mysql.cj.jdbc.result.ResultSetImpl.checkRowPos(ResultSetImpl.java:532) at com.mysql.cj.jdbc.result.ResultSetImpl.getString(ResultSetImpl.java:878) at Facturacion.ConnectionMysql.cargarListaPedido(ConnectionMysql.java:166) at Facturacion.PruebaComercio.<init>(PruebaComercio.java:23) at Facturacion.Main.main(Main.java:25)
问题根源
- 共享变量冲突:
ConnectionMysql类中的rs、st、conexion是类级共享变量。在loadOrderList()的循环中调用findItem()/findClient()时,这两个方法会重新创建连接、Statement并执行查询,直接覆盖了类中的rs变量。回到循环继续读取原订单ResultSet时,该ResultSet已被替换且可能已遍历到末尾,触发异常。 - 资源未正确关闭:所有数据库操作未关闭Statement和ResultSet,导致资源泄漏,同时加剧变量冲突问题。
- 性能冗余:
findClient()和findItem()每次调用都全表查询并遍历匹配,效率低下且频繁创建数据库连接,消耗资源。
最优实现方案
1. 重构核心逻辑
将数据库资源改为方法局部变量,用try-with-resources自动关闭;预加载客户/产品数据到内存缓存,避免重复查询;修复变量冲突问题。
2. 重构后的代码示例
import java.sql.*; import java.util.*; public class ConnectionMysql { private final String url; private final String user; private final String password; // 内存缓存客户和产品数据 private Map<String, Cliente> customerCache; private Map<String, Producto> productCache; public ConnectionMysql(String url, String user, String password) { this.url = url; this.user = user; this.password = password; // 初始化缓存 try { loadCustomerCache(); loadProductCache(); } catch (SQLException e) { throw new RuntimeException("初始化数据缓存失败", e); } } private void loadCustomerCache() throws SQLException { customerCache = new HashMap<>(); String query = "SELECT * FROM cliente"; // try-with-resources自动关闭资源 try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement stmt = conn.prepareStatement(query); ResultSet rs = stmt.executeQuery()) { while (rs.next()) { String dni = rs.getString(1); String nombre = rs.getString(2); String apellidos = rs.getString(3); int descuento = rs.getInt(4); customerCache.put(dni, new Cliente(dni, nombre, apellidos, descuento)); } } } private void loadProductCache() throws SQLException { productCache = new HashMap<>(); String query = "SELECT * FROM item"; try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement stmt = conn.prepareStatement(query); ResultSet rs = stmt.executeQuery()) { while (rs.next()) { String nombre = rs.getString(1); double precio = rs.getDouble(2); int existencias = rs.getInt(3); productCache.put(nombre, new Producto(nombre, precio, existencias)); } } } public Cliente findClient(String dni) { // 直接从缓存获取,无需查询数据库 return customerCache.get(dni); } public Producto findItem(String nombreProducto) { return productCache.get(nombreProducto); } public List<Pedido> loadOrderList() throws SQLException { List<Pedido> listaPedidos = new ArrayList<>(); String query = "SELECT * FROM pedido WHERE `Numero Factura` IS NULL"; try (Connection conn = DriverManager.getConnection(url, user, password); PreparedStatement stmt = conn.prepareStatement(query); ResultSet rs = stmt.executeQuery()) { while (rs.next()) { int unidad = rs.getInt(3); String productName = rs.getString(2); String clientDni = rs.getString(6); Producto producto = productCache.get(productName); Cliente cliente = customerCache.get(clientDni); // 校验数据有效性,避免空指针 if (producto != null && cliente != null) { listaPedidos.add(new Pedido(producto, unidad, cliente)); } else { System.err.printf("订单存在无效数据:客户[%s]或产品[%s]不存在%n", clientDni, productName); } } } return listaPedidos; } // 可选:提供刷新缓存的方法,用于数据更新后同步 public void refreshCaches() throws SQLException { loadCustomerCache(); loadProductCache(); } }
3. 调用代码调整
public class PruebaComercio { private Map<String, Cliente> listaClientes; private List<Pedido> listaPedidos; private List<Factura> listaFacturas; private Almacen almacen; private ConnectionMysql connection; public PruebaComercio() throws SQLException { // 替换为实际数据库参数 connection = new ConnectionMysql("jdbc:mysql://localhost:3306/your_db", "db_user", "db_password"); this.listaClientes = connection.customerCache; // 直接使用缓存,或添加getter方法 this.listaPedidos = connection.loadOrderList(); this.listaFacturas = new ArrayList<>(); this.almacen = new Almacen(); } }
关键优化说明
- 局部资源隔离:每个数据库操作的Connection、Statement、ResultSet都是局部变量,用try-with-resources自动关闭,彻底避免变量冲突和资源泄漏。
- 内存缓存优化:一次性加载客户/产品数据到Map,后续查找直接通过键获取,时间复杂度O(1),大幅提升性能。
- 数据有效性校验:添加客户/产品存在性判断,避免空指针异常,同时输出错误日志便于排查。
内容的提问来源于stack exchange,提问作者Badr Serhiri
相关产品推荐
相关产品推荐

