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

实现MySQL外键关联的JComboBox与JTextField联动功能

解决方案

核心思路拆解

要实现「选主表ID → 加载关联的子表ID列表 → 选子表ID显示对应内容」的联动效果,需要分三步改造:

  1. 先加载所有可选的主表(habito)ID到第一个下拉框
  2. 监听第一个下拉框的选择事件,根据选中的主ID加载关联的子表(tb_timer)ID到第二个下拉框
  3. 监听第二个下拉框的选择事件,根据选中的子ID查询对应内容并显示到文本框

第一步:新增DAO查询方法

在现有DAO类中添加三个针对性的查询方法:

// 获取所有habito的ID(从主表tb_habito查询,更规范;若仅需tb_timer中存在的关联ID,可改用SELECT DISTINCT fk_habito_id FROM tb_timer)
public Integer[] obterTodosIdsHabito() throws Exception {
    String sql = "SELECT DISTINCT id FROM tb_habito";
    try (Connection conn = ConexaoBD.obterConexao();
         PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
         ResultSet rs = ps.executeQuery()) {

        int total = rs.last() ? rs.getRow() : 0;
        Integer[] idsHabito = new Integer[total];
        rs.beforeFirst();
        int contador = 0;
        while (rs.next()) {
            idsHabito[contador++] = rs.getInt("id");
        }
        return idsHabito;
    }
}

// 根据habito ID获取关联的所有alarme ID
public Integer[] obterIdsAlarmePorHabito(int idHabito) throws Exception {
    String sql = "SELECT id FROM tb_timer WHERE fk_habito_id = ?";
    try (Connection conn = ConexaoBD.obterConexao();
         PreparedStatement ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) {

        ps.setInt(1, idHabito);
        try (ResultSet rs = ps.executeQuery()) {
            int total = rs.last() ? rs.getRow() : 0;
            Integer[] idsAlarme = new Integer[total];
            rs.beforeFirst();
            int contador = 0;
            while (rs.next()) {
                idsAlarme[contador++] = rs.getInt("id");
            }
            return idsAlarme;
        }
    }
}

// 根据alarme ID获取对应的触发时间
public String obterHoraToquePorAlarmeId(int idAlarme) throws Exception {
    String sql = "SELECT hora_toque FROM tb_timer WHERE id = ?";
    try (Connection conn = ConexaoBD.obterConexao();
         PreparedStatement ps = conn.prepareStatement(sql)) {

        ps.setInt(1, idAlarme);
        try (ResultSet rs = ps.executeQuery()) {
            if (rs.next()) {
                return String.valueOf(rs.getTime("hora_toque"));
            }
            return null;
        }
    }
}

第二步:改造JFrame代码

替换原有的初始化和绑定逻辑,添加下拉框联动监听:

public TelaAlarmes() {
    super("Alarme");
    initComponents();
    inicializarComboBoxHabito();
    adicionarEventListeners();
    setLocationRelativeTo(null);
}

// 初始化第一个下拉框:加载所有habito ID
private void inicializarComboBoxHabito() {
    try {
        DAO dao = new DAO();
        Integer[] idsHabito = dao.obterTodosIdsHabito();
        cbbIdHabito.setModel(new DefaultComboBoxModel<>(idsHabito));
    } catch (Exception e) {
        JOptionPane.showMessageDialog(null, "无法加载 habitos 的ID列表!");
        e.printStackTrace();
    }
}

// 添加下拉框联动监听
private void adicionarEventListeners() {
    // 第一个下拉框选择事件:加载关联的alarme ID
    cbbIdHabito.addActionListener(e -> {
        Integer idHabitoSelecionado = (Integer) cbbIdHabito.getSelectedItem();
        if (idHabitoSelecionado != null) {
            carregarIdsAlarme(idHabitoSelecionado);
        }
    });

    // 第二个下拉框选择事件:加载对应的触发时间
    cbbAlarmeId.addActionListener(e -> {
        Integer idAlarmeSelecionado = (Integer) cbbAlarmeId.getSelectedItem();
        if (idAlarmeSelecionado != null) {
            carregarHoraToque(idAlarmeSelecionado);
        }
    });
}

// 根据选中的habito ID加载alarme ID到第二个下拉框
private void carregarIdsAlarme(int idHabito) {
    try {
        DAO dao = new DAO();
        Integer[] idsAlarme = dao.obterIdsAlarmePorHabito(idHabito);
        cbbAlarmeId.setModel(new DefaultComboBoxModel<>(idsAlarme));
        txtHoraToque.setText(""); // 清空文本框避免旧数据残留
    } catch (Exception e) {
        JOptionPane.showMessageDialog(null, "无法加载关联的 alarme ID列表!");
        e.printStackTrace();
    }
}

// 根据选中的alarme ID加载触发时间到文本框
private void carregarHoraToque(int idAlarme) {
    try {
        DAO dao = new DAO();
        String horaToque = dao.obterHoraToquePorAlarmeId(idAlarme);
        txtHoraToque.setText(horaToque != null ? horaToque : "");
    } catch (Exception e) {
        JOptionPane.showMessageDialog(null, "无法加载 alarme 的触发时间!");
        e.printStackTrace();
    }
}

为什么原有代码不符合需求?

原代码直接把所有Alarme对象塞到cbbIdHabito下拉框,导致下拉框显示的是Alarme类的默认toString值(类名+哈希码),完全没有实现「选主ID→关联子ID→显示内容」的联动逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 19:57:04