0

I am designing a simple database application that features 2 jComboBoxes in the GUI. The first jComboBox is populated with the results of an SQL query. I would like the second jComboBox to populate with the results of a second query that incorporates the user selected value in the first box, but I can't quite get it to work.

I have created 2 classes, one that draws the GUI and contains the main method, and a second class that queries my Oracle database.

My GUI class:

public class TestUI extends javax.swing.JFrame {
     // Create new form TestUI
    public TestUI() {
        initComponents();
    }

    @SuppressWarnings("unchecked")

    private void initComponents() {

        jPanel1 = new javax.swing.JPanel();
        jComboBox1 = new javax.swing.JComboBox<>();
        jTextField1 = new javax.swing.JTextField();
        jComboBox2 = new javax.swing.JComboBox<>();
        jButton1 = new javax.swing.JButton();
        jButton2 = new javax.swing.JButton();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        // Combo box 1 population

        jComboBox1.removeAllItems();
        createConnection c1 = new createConnection();
        c1.getEmployee().forEach((employee) -> {
            jComboBox1.addItem(employee);
        });
        jComboBox1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jComboBox1ActionPerformed(evt);
            }
        });

        // ComboBox 2 population

        jComboBox2.removeAllItems();


    }                     

    private void jComboBox1ActionPerformed(java.awt.event.ActionEvent evt) {                                           
        // TODO add handling code here:
    }                                    

    public static void main(String args[]) {
        DRAW GUI
    }
}

And my database class:

import java.util.List;
import java.util.ArrayList;
import java.sql.*;

public class createConnection {

    String empName;

    public Connection createConnection() {
        try {
            Class.forName(driver);
            java.sql.Connection conn = DriverManager.getConnection(DB_URL, DB_username, DB_password);
            return conn;
        } catch (ClassNotFoundException | SQLException e) {
            return null;
        }
    }

    // ComboBox 1
    public List<String> getEmployee() {
        List<String> list = new ArrayList();
        Connection conn = createConnection();
        try {
            Statement stmt = conn.createStatement();
            String query = "SELECT * FROM hr.employees ORDER BY last_name";
            ResultSet results = stmt.executeQuery(query);
            while (results.next()) {
                list.add(results.getString("last_name"));
            }
        } catch (Exception e) {
            System.out.println("Exception = " + e);
        }
        return list;
    }

    // Combo Box 2
    public List<String> getEmpLocation() {
        List<String> list = new ArrayList();
        Connection conn = createConnection();
        try {
            Statement stmt = conn.createStatement();
            String query = "SELECT country_id FROM hr.location WHERE hr.location.emp_name = " + empName;
            ResultSet results = stmt.executeQuery(query);
            while (results.next()) {
                list.add(results.getString("last_name"));
            }
        } catch (Exception e) {
            System.out.println("Exception = " + e);
        }
        return list;
    }    
}

I have left out irrelevant code like db connection variables and GUI coordinates etc.

I am wondering how to properly get the getEmpLocation() method in the database class to populate the 2nd ComboBox. This will involve adding code to both classes and passing the variable value but I can't figure it out! Any help would be greatly appreciated here.

  • 1
    I'd start by having a look at [How to Use Combo Boxes](https://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html), you're looking for [`JComboBox#getSelectedItem`](https://docs.oracle.com/javase/8/docs/api/javax/swing/JComboBox.html#getSelectedItem--). Then have a look at [Passing Information to a Method or a Constructor](https://docs.oracle.com/javase/tutorial/java/javaOO/arguments.html), [Using Prepared Statements](http://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html) – MadProgrammer Jan 26 '17 at 22:40
  • and [The try-with-resources Statement](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) – MadProgrammer Jan 26 '17 at 22:40
  • Possible duplicate of [*Dynamic JComboBoxes*](http://stackoverflow.com/q/3191837/230513). – trashgod Jan 26 '17 at 23:07
  • @MadProgrammer - thank you for this. I am in the process of reading through these linked pages. Often I find it hard to know what to ask for - I don't know names of things that I'm looking for, so this input is really helpful. –  Jan 28 '17 at 15:37

1 Answers1

0

I'm assuming that you'd like select a value from your first JComboBox then click on a button to process your selected data and load new data to your second JComboBox.

In this case you need an ActionListener to your JButton instead of your JComboBox:

jButton1.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        selectedName = (String) jComboBox1.getSelectedItem();                
    }
});

You also need to store your selected value in a variable. The getSelectedItem() method returns an Object so it needs to be cast to a String in your case.

Since we added an ActionListener to a button you dont need this one:

jComboBox1.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
        jComboBox1ActionPerformed(evt);
    }
});


In your createConnection class (by naming convention class names should start with a capital letter):

If you are not using try-with-resources statement you should close your connections after the catch block.

    } finally {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }

        }
    }

You need to pass your selectedName variable to getEmpLocation() method:

public List<String> getEmpLocation(String name) {

You should use a PreparedStatement instead of Statement:

String query = "SELECT first_name FROM employees WHERE last_name = ?";
PreparedStatement ps = conn.prepareStatement(query);
ps.setString(1, name);            
ResultSet results = ps.executeQuery();

To be honest I don't know what you'd like to achieve with your select queries. First, this select query won't work. Table name is LOCATIONS instead of location, and it doesnt have a column called emp_name.

"SELECT country_id FROM hr.location WHERE hr.location.emp_name = ?"

If you'd like to get locations you should use a query like this:

"SELECT dep.department_name, loc.city, cou.country_name
 FROM employees emp, departments dep, locations loc, countries cou
 WHERE emp.last_name = ?
 AND emp.department_id = dep.department_id 
 AND dep.location_id = loc.location_id
 AND loc.country_id = cou.country_id"

You can choose which location you'd like to use department, city or country name. But my main problem is that if you select last names first and put them in a JComboBox it is most likely you will get only one row of data, so there is no point in using the second JComboBox. Let's approach this problem from the other side. What if you select location first and then select your employee. That could solve this issue.


Quick Example: You select all first names from database, then you can select proper last name.

Selecting all first name from database:

    public List<String> getEmpFirstName() {
        List<String> list = new ArrayList();
        Connection conn = createConnection();

        try {
            Statement stmt = conn.createStatement();
            String query = "SELECT DISTINCT first_name "
                            + "FROM hr.employees "
                            + "ORDER BY first_name";
            ResultSet results = stmt.executeQuery(query);

            while (results.next()) {
                list.add(results.getString("first_name"));
            }
        } catch (Exception e) {
            System.out.println("Exception = " + e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }                    
            }
        }
        return list;
    }

Selecting last name(s) based on first name using PreparedStatement:

    public List<String> getEmpLastName(String name) {
        List<String> list = new ArrayList();
        Connection conn = createConnection();

        try {
            String query = "SELECT last_name "
                            + "FROM employees "
                            + "WHERE first_name = ?";
            PreparedStatement ps = conn.prepareStatement(query);
            ps.setString(1, name);            
            ResultSet results = ps.executeQuery();

            while (results.next()) {
                list.add(results.getString("last_name"));
            }
        } catch (Exception e) {
            System.out.println("Exception = " + e);
        } finally {
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }                    
            }
        }
        return list;
    }

Update your ActionListener:

    jButton1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // Store selected value
                selectedName = (String) jComboBox1.getSelectedItem();

                // Create Connection and pass selected value to getEmpLastName
                createConnection c1 = new createConnection();
                names = c1.getEmpLastName(selectedName);

                // Clear your second comboBox and fill with data
                jComboBox2.removeAllItems();                
                for (String lastName : names) {
                    jComboBox2.addItem(lastName);
                }              
            }
        });    

Try to select common names like Alexander, David, James, John, Julia and so on.

Zsolt Ébel
  • 120
  • 1
  • 9
  • hi, and wow thank you for an amazing in-depth explanation of so much! Firstly - I don't want the button to trigger the 2nd ComboBox. I want the selection of a value in the 1st ComboBox to trigger the 2nd ComboBox. So user selects a value in box1, and then box2 becomes populated with the relevant values. –  Jan 28 '17 at 15:34
  • regarding the SQL - yes you're quite right, and the real queries I want to run are actually quite different from this. Some of the data is private so I just used the HR schema as a quick example. However I rushed the example, your use of the demo data makes much more sense :) So I need to add an action listener to the ComboBoxes I think. Is this done in a similar way to the method you have used for the button? –  Jan 28 '17 at 15:35
  • Yeah exactly. Add the ActionListener to your JComboBox instead of JButton. – Zsolt Ébel Jan 28 '17 at 15:45
  • jComboBox2.addActionListener(new java.awt.event.ActionListener() { @Override public void actionPerformed(java.awt.event.ActionListener e) { String selectedName = (String) jComboBox1.getSelectedItem(); } }); I have tried this but I get an error: is not abstract and does not override abstract method actionPerformed(ActionEvent) in ActionListener This anonymous inner class creation can be turned into a lambda expression. Am I doing something wrong here? –  Jan 28 '17 at 16:04