You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)

问题根源

  1. 共享变量冲突:ConnectionMysql类中的rs、st、conexion是类级共享变量。在loadOrderList()的循环中调用findItem()/findClient()时,这两个方法会重新创建连接、Statement并执行查询,直接覆盖了类中的rs变量。回到循环继续读取原订单ResultSet时,该ResultSet已被替换且可能已遍历到末尾,触发异常。
  2. 资源未正确关闭:所有数据库操作未关闭Statement和ResultSet,导致资源泄漏,同时加剧变量冲突问题。
  3. 性能冗余: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 01:31:01