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

基于MVC架构的Java Swing项目SQL记录导航实现求助

解决Java Swing MVC项目的数据库记录导航问题

先梳理你现有代码的核心问题:

  • 成员变量rs未被正确维护,getStocks方法每次查询都会覆盖它,且默认ResultSet是只能向前滚动的类型,无法实现上一条、第一条、最后一条等导航操作
  • nextRecord方法逻辑错误,调用getStocks(barcode)是获取记录列表,而非将当前记录数据填充到模型属性中
  • 数据库资源(Connection、Statement、ResultSet)未正确关闭,存在泄漏风险
  • 内部类Service的内存列表操作和数据库操作分离,导航逻辑未和数据层对齐

下面提供两种可行的导航实现方案,根据你的数据量选择:


方案1:基于内存列表的导航(推荐,适合数据量不大的场景)

这种方案先把所有数据库记录加载到内存列表,通过索引移动实现导航,无需一直保持数据库连接,稳定性更高。

修改MainModel代码步骤:

  1. 添加存储记录列表和当前索引的成员变量
private List<MainModel> stockList;
private int currentIndex = -1;
  1. 添加加载所有记录并初始化导航的方法
// 加载所有库存记录到内存列表,并定位到第一条
public void loadAllStocks() {
    stockList = getStocks(null);
    currentIndex = stockList.isEmpty() ? -1 : 0;
    if (currentIndex != -1) {
        syncModelWithCurrentRecord();
    }
}

// 将当前索引的记录数据同步到模型自身属性
private void syncModelWithCurrentRecord() {
    MainModel current = stockList.get(currentIndex);
    this.setStockCode(current.getStockCode());
    this.setStockName(current.getStockName());
    this.setStockType(current.getStockType());
    this.setUnit(current.getUnit());
    this.setBarcode(current.getBarcode());
    this.setKdv(current.getKdv());
    this.setExplanation(current.getExplanation());
    this.setCreateDate(current.getCreateDate());
}
  1. 实现导航方法(下一条、上一条、第一条、最后一条)
// 下一条记录,返回是否成功
public boolean nextRecord() {
    if (stockList == null || currentIndex >= stockList.size() - 1) {
        return false;
    }
    currentIndex++;
    syncModelWithCurrentRecord();
    return true;
}

// 上一条记录
public boolean previousRecord() {
    if (stockList == null || currentIndex <= 0) {
        return false;
    }
    currentIndex--;
    syncModelWithCurrentRecord();
    return true;
}

// 第一条记录
public boolean firstRecord() {
    if (stockList == null || stockList.isEmpty()) {
        return false;
    }
    currentIndex = 0;
    syncModelWithCurrentRecord();
    return true;
}

// 最后一条记录
public boolean lastRecord() {
    if (stockList == null || stockList.isEmpty()) {
        return false;
    }
    currentIndex = stockList.size() - 1;
    syncModelWithCurrentRecord();
    return true;
}
  1. 修复getStocks方法的错误(重复设置stockCode)
public List<MainModel> getStocks(String Stock_Code) {
    try {
        PreparedStatement stmt = null; // 改用局部变量,避免成员变量被覆盖
        ResultSet localRs = null;
        if (Stock_Code != null) {
            stmt = getConnection().prepareStatement("select stokKodu,stokAdi,stokTipi,birimi,barkodu,kdvTipi,aciklama,olusturmaTarihi from stockcard1 where stokKodu=? or stokAdi=?");
            stmt.setString(1, Stock_Code);
            stmt.setString(2, Stock_Code);
        } else {
            stmt = getConnection().prepareStatement("select * from stockcard1");
        }

        localRs = stmt.executeQuery();
        List<MainModel> models = new ArrayList<>();
        while(localRs.next()) {
            MainModel model = new MainModel();
            model.setStockCode(localRs.getString("stokKodu"));
            // 删除重复的setStockCode行
            model.setStockName(localRs.getString("stokAdi"));
            model.setStockType(localRs.getInt("stokTipi"));
            model.setUnit(localRs.getString("birimi"));
            model.setBarcode(localRs.getString("barkodu"));
            model.setKdv(localRs.getDouble("kdvTipi"));
            model.setExplanation(localRs.getString("aciklama"));
            model.setCreateDate(localRs.getString("olusturmaTarihi"));
            models.add(model);
        }
        // 关闭局部资源
        localRs.close();
        stmt.close();
        return models;
    } catch (Exception ex){
        Logger.getLogger(MainController.class.getName()).log(Level.SEVERE, null, ex);
    }
    return new ArrayList<>();
}

方案2:基于可滚动ResultSet的导航(适合大数据量场景)

这种方案直接操作数据库的可滚动ResultSet,无需加载所有数据到内存,但需要保持数据库连接,注意资源释放。

修改MainModel代码步骤:

  1. 修改getConnection后的Statement创建,指定可滚动ResultSet类型
// 初始化可滚动的ResultSet
public void initScrollableResultSet() {
    try {
        // 创建可滚动、只读的Statement
        statement = getConnection().prepareStatement(
            "select * from stockcard1",
            ResultSet.TYPE_SCROLL_INSENSITIVE,
            ResultSet.CONCUR_READ_ONLY
        );
        rs = statement.executeQuery();
        // 定位到第一条记录并同步到模型
        if (rs.first()) {
            syncModelWithResultSet();
        }
    } catch (SQLException ex) {
        Logger.getLogger(MainModel.class.getName()).log(Level.SEVERE, null, ex);
    }
}

// 将当前ResultSet指针的记录同步到模型属性
private void syncModelWithResultSet() throws SQLException {
    this.setStockCode(rs.getString("stokKodu"));
    this.setStockName(rs.getString("stokAdi"));
    this.setStockType(rs.getInt("stokTipi"));
    this.setUnit(rs.getString("birimi"));
    this.setBarcode(rs.getString("barkodu"));
    this.setKdv(rs.getDouble("kdvTipi"));
    this.setExplanation(rs.getString("aciklama"));
    this.setCreateDate(rs.getString("olusturmaTarihi"));
}
  1. 实现导航方法
public boolean nextRecord() {
    try {
        if (rs != null && rs.next()) {
            syncModelWithResultSet();
            return true;
        }
    } catch (SQLException ex) {
        Logger.getLogger(MainModel.class.getName()).log(Level.SEVERE, null, ex);
    }
    return false;
}

public boolean previousRecord() {
    try {
        if (rs != null && rs.previous()) {
            syncModelWithResultSet();
            return true;
        }
    } catch (SQLException ex) {
        Logger.getLogger(MainModel.class.getName()).log(Level.SEVERE, null, ex);
    }
    return false;
}

public boolean firstRecord() {
    try {
        if (rs != null && rs.first()) {
            syncModelWithResultSet();
            return true;
        }
    } catch (SQLException ex) {
        Logger.getLogger(MainModel.class.getName()).log(Level.SEVERE, null, ex);
    }
    return false;
}

public boolean lastRecord() {
    try {
        if (rs != null && rs.last()) {
            syncModelWithResultSet();
            return true;
        }
    } catch (SQLException ex) {
        Logger.getLogger(MainModel.class.getName()).log(Level.SEVERE, null, ex);
    }
    return false;
}

// 关闭数据库资源,在窗口关闭时调用
public void closeResources() {
    try {
        if (rs != null) rs.close();
        if (statement != null) statement.close();
        if (connection != null) connection.close();
    } catch (SQLException ex) {
        Logger.getLogger(MainModel.class.getName()).log(Level.SEVERE, null, ex);
    }
}

控制器层调用示例

在MainController中初始化导航,并绑定按钮事件:

// 初始化(以方案1为例)
MainModel model = new MainModel();
model.loadAllStocks();
// 更新View显示模型数据
updateView(model);

// 下一条按钮事件
btnNext.addActionListener(e -> {
    if (model.nextRecord()) {
        updateView(model);
    }
});

// 上一条按钮事件
btnPrevious.addActionListener(e -> {
    if (model.previousRecord()) {
        updateView(model);
    }
});

额外优化建议

  • 将connection和statement成员变量改为private,避免外部直接操作
  • 在save/update/delete方法中添加资源关闭逻辑,避免泄漏
  • 内部类Service和数据库操作方法重名,建议重命名为InMemoryStockService,区分内存操作和数据库操作

内容的提问来源于stack exchange,提问作者weit

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 01:45:35