1

I've been building a little project with gui that retrieves data from a sample database called 'world', which contains a country, a city and a countrylanguage tables. (I'm only using the first two.) This project has a main window with a JMenuBar which opens a JDialog on click. In the JDialog you can select a country from a JList and it will show its cities in a JTable. I'm also trying to use MVC design pattern.

My problem has occured when I set modality to true in my JDialog's contructor. GUI of my JDialog appears but the code stops running. I tried to start a new Thread for the JDialog but it didnt solve my problem.

setModal(false): image here

setModal(true): image here

  • Model contains: City.java, Country.java, dbConnection.java
  • View contains: MainWindow.java, CitiesDialog.java
  • Controller: Controller.java, SwingWorldJDBC.java

Am I doing MVC pattern wrong? Or is it a Thread-handling issue?

Notes: I've started learning programming a half year ago, I'm trying to learn from my mistakes so if you have any advice or better solution for my project that I should have done differently please let me know.

City.java

package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class City implements dbConnection, Comparable<City> {

    private int ID;
    private String name;
    private Country country;
    private String district;
    private int population;

    public City() {
    }

    public City(int ID, String name, Country country, String district, int population) {
        this.ID = ID;
        this.name = name;
        this.country = country;
        this.district = district;
        this.population = population;
    }

    public int getID() {
        return ID;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Country getCountry() {
        return country;
    }

    public void setCountryCode(Country country) {
        this.country = country;
    }

    public String getDistrict() {
        return district;
    }

    public void setDistrict(String district) {
        this.district = district;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    @Override
    public int compareTo(City o) {
        return name.compareTo(o.name);
    }

    public List<City> getCities(Country country) {
        List<City> cities = new ArrayList<>();

        try (Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
            String selectStmt = "SELECT * FROM city "
                + "INNER JOIN country "
                + "ON city.CountryCode = country.Code "
                + "WHERE country.Code = ? ";
            PreparedStatement ps = con.prepareStatement(selectStmt);
            ps.setString(1, country.getCode());
            ResultSet rs = ps.executeQuery();

            while (rs.next()) {
                ID = rs.getInt("ID");
                name = rs.getString("Name");                
                district = rs.getString("District");
                population = rs.getInt("Population");

                City city = new City(ID, name, country, district, population);
                cities.add(city);
            }
        } catch (SQLException ex) {
            System.out.println("SQLExeption - getCities()");
        }
        Collections.sort(cities);
        return cities;
    }

    public void deleteCity(City city) {                
        try (Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
            String deleteStmt = "DELETE FROM city "
                            + "WHERE ID = ?";
            PreparedStatement ps = con.prepareStatement(deleteStmt);
            ps.setInt(1, city.ID);
            ps.executeUpdate();
        } catch (SQLException ex) {
            System.out.println("SQLExeption - deleteCity()");
        }
    }   

}

Country.java

package model;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Country implements dbConnection, Comparable<Country> {

    private String code;
    private String name;
    private String continent;
    private String region;
    private float surfaceArea;
    private Short indepYear;
    private int population;
    private Float lifeExpectancy;
    private Float GNP;
    private Float GNPOld;
    private String localName;
    private String governmentForm;
    private String headOfState;
    private Integer capital;
    private String code2;

    public Country() {
    }

    public Country(String code, String name) {
        this.code = code;
        this.name = name;
    }

    public Country(String code, String name, String continent, String region,
            float surfaceArea, Short indepYear, int population,
            Float lifeExpectancy, Float GNP, Float GNPOld,
            String localName, String governmentForm,
            String headOfState, Integer capital, String code2) {
        this.code = code;
        this.name = name;
        this.continent = continent;
        this.region = region;
        this.surfaceArea = surfaceArea;
        this.indepYear = indepYear;
        this.population = population;
        this.lifeExpectancy = lifeExpectancy;
        this.GNP = GNP;
        this.GNPOld = GNPOld;
        this.localName = localName;
        this.governmentForm = governmentForm;
        this.headOfState = headOfState;
        this.capital = capital;
        this.code2 = code2;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getContinent() {
        return continent;
    }

    public void setContinent(String continent) {
        this.continent = continent;
    }

    public String getRegion() {
        return region;
    }

    public void setRegion(String region) {
        this.region = region;
    }

    public float getSurfaceArea() {
        return surfaceArea;
    }

    public void setSurfaceArea(float surfaceArea) {
        this.surfaceArea = surfaceArea;
    }

    public Short getIndepYear() {
        return indepYear;
    }

    public void setIndepYear(Short indepYear) {
        this.indepYear = indepYear;
    }

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }

    public Float getLifeExpectancy() {
        return lifeExpectancy;
    }

    public void setLifeExpectancy(Float lifeExpectancy) {
        this.lifeExpectancy = lifeExpectancy;
    }

    public Float getGNP() {
        return GNP;
    }

    public void setGNP(Float GNP) {
        this.GNP = GNP;
    }

    public Float getGNPOld() {
        return GNPOld;
    }

    public void setGNPOld(Float GNPOld) {
        this.GNPOld = GNPOld;
    }

    public String getLocalName() {
        return localName;
    }

    public void setLocalName(String localName) {
        this.localName = localName;
    }

    public String getGovernmentForm() {
        return governmentForm;
    }

    public void setGovernmentForm(String governmentForm) {
        this.governmentForm = governmentForm;
    }

    public String getHeadOfState() {
        return headOfState;
    }

    public void setHeadOfState(String headOfState) {
        this.headOfState = headOfState;
    }

    public Integer getCapital() {
        return capital;
    }

    public void setCapital(Integer capital) {
        this.capital = capital;
    }

    public String getCode2() {
        return code2;
    }

    public void setCode2(String code2) {
        this.code2 = code2;
    }

    @Override
    public int compareTo(Country o) {
        return name.compareTo(o.getName());
    }

    public List<Country> getCountries() {
        List<Country> countries = new ArrayList<>();

        try (Connection con = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
            Statement stmt = con.createStatement();
            String query = "SELECT Code, Name FROM country";
            ResultSet rs = stmt.executeQuery(query);

            while (rs.next()) {
                code = rs.getString("Code");
                name = rs.getString("Name");
                Country country = new Country(code, name);
                countries.add(country);
            }
        } catch (SQLException ex) {
            System.out.println("SQLException - getCountries()");
        }
        Collections.sort(countries);
        return countries;
    }

    @Override
    public String toString() {
        return getName();
    }   

}

dbconnection.java

package model;

public interface dbConnection {
    String URL = "jdbc:mysql://localhost:3306/world";
    String USERNAME = "root";
    String PASSWORD = "1234";
}

MainWindow.java

package view;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionListener;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class MainWindow {

    private JFrame frame;
    private JMenuBar menuBar;
    private JMenu file;
    private JMenu data;
    private JMenuItem exitMenuItem;
    private JMenuItem citiesMenuItem;

    public MainWindow() {
        initComponents();        
    }

    private void initComponents() {
        frame = new JFrame("Swing application with JDBC using World sample database");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(625, 650);
        frame.setResizable(false);
        frame.setJMenuBar(createMenuBar());

        try {
            frame.getContentPane().add(new MyBackgroundPanel());
        } catch (NullPointerException ex) {
            JOptionPane.showMessageDialog(null, "Background image is missing!", "Error", JOptionPane.ERROR_MESSAGE);
        }

        frame.setVisible(true);
        frame.setLocationRelativeTo(null);
    }

    private JMenuBar createMenuBar() {
        menuBar = new JMenuBar();
        file = new JMenu("File");
        exitMenuItem = new JMenuItem("Exit");
        file.add(exitMenuItem);

        data = new JMenu("Data");
        citiesMenuItem = new JMenuItem("Load cities");
        data.add(citiesMenuItem);

        menuBar.add(file);
        menuBar.add(data);
        return menuBar;
    }

    public JMenuItem getExitMenu() {
        return exitMenuItem;
    }

    public JMenuItem getLoadCitiesMenu() {
        return citiesMenuItem;
    }

    public void addMenuActionListener (ActionListener menuActionListener) {
        exitMenuItem.addActionListener(menuActionListener);
        citiesMenuItem.addActionListener(menuActionListener);        
    }    

    private class MyBackgroundPanel extends JPanel {
        URL url = getClass().getResource("worldMap03.jpg");
        Image img = new ImageIcon(url).getImage();

        @Override
        protected void paintComponent(Graphics g) {
            g.drawImage(img, 0, 0, null);
        }
    }
}

CitiesDialog.java

package view;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import java.util.Vector;
import javax.swing.Box;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;

public class CitiesDialog extends JDialog {

    private JPanel listPanel;
    private JPanel tablePanel;
    private JList list;
    private DefaultTableModel model;
    private JTable table;
    private JScrollPane tScrollPane;
    private DefaultTableCellRenderer rightRenderer;
    private JButton deleteButton;
    private Border listBorder;
    private final Object[] columnNames = {"City name", "District", "Population"};

    public CitiesDialog() {
        setTitle("Country dialog");
        setSize(800, 600);
        setLayout(new FlowLayout(FlowLayout.LEADING, 0, 0));
        setLocationRelativeTo(null);
        add(createListPanel());
        add(createTablePanel());
        //setModal(true);        
        setResizable(false);
        setVisible(true);        
    }

    private JPanel createListPanel() {
        listPanel = new JPanel();

    //JList for countries    
        list = new JList();
        JScrollPane listScrollPane = new JScrollPane(list);
        listScrollPane.setPreferredSize(new Dimension(200, 560));

        listPanel.add(listScrollPane);
        return listPanel;
    }

    private JPanel createTablePanel() {
        tablePanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 0, 10));
        tablePanel.setPreferredSize(new Dimension(575, 560));

    // Delete Button
        deleteButton = new JButton("Delete");
        deleteButton.setFocusable(false);

    // JTable with DefaultTableModel
        model = new DefaultTableModel(columnNames, 0) {
            @Override
            public boolean isCellEditable(int row, int column) {
                return false;
            }
        };

        table = new JTable(model);
        rightRenderer = new DefaultTableCellRenderer();
        rightRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
        table.getColumnModel().getColumn(2).setCellRenderer(rightRenderer);
        table.setPreferredScrollableViewportSize(new Dimension(570, 491));
        table.setSelectionMode(DefaultListSelectionModel.SINGLE_SELECTION);
        tScrollPane = new JScrollPane(table);

        tablePanel.add(deleteButton);
        tablePanel.add(Box.createRigidArea(new Dimension(15, 20)));
        tablePanel.add(tScrollPane);
        return tablePanel;
    }

    public int getSelectedIndex() {
        return list.getSelectedIndex();
    }

    public int getSelectedRow() {
        return table.getSelectedRow();
    }

    public void loadCountryNames(Vector listData) {
        list.setListData(listData);
    }    

    public void fillTableData(Vector cityData) {
        model.addRow(cityData);
    }

    public void clearTable() {
        model.getDataVector().removeAllElements();
    }

    public void removeRow(int row) {
        model.removeRow(row);
    }

    @Override
    public void addMouseListener(MouseListener mouseListener) {
        list.addMouseListener(mouseListener);
    }

    public void addDeleteButtonListener(ActionListener actionlistener) {
        deleteButton.addActionListener(actionlistener);
    }

}

Controller.java

package controller;

import java.awt.EventQueue;
import view.CitiesDialog;
import model.Country;
import model.City;
import view.MainWindow;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.List;
import java.util.Vector;

public class Controller {

    private City city;
    private Country country;
    private MainWindow mainWindow;
    private CitiesDialog citiesDialog;
    private Country selectedCountry;
    List<Country> countries;
    List<City> cities;

    public Controller() {
    }

    public Controller(City city, Country country, MainWindow mainWindow) {
        this.city = city;
        this.country = country;
        this.mainWindow = mainWindow;
        selectedCountry = null;
        mainWindow.addMenuActionListener(new MenuActionListener());
    }

    private class MenuActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == mainWindow.getExitMenu()) {
                System.exit(0);
            } else if (e.getSource() == mainWindow.getLoadCitiesMenu()) {
                EventQueue.invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        citiesDialog = new CitiesDialog();
                        citiesDialog.addMouseListener(new ListMouseListener());
                        citiesDialog.addDeleteButtonListener(new DeleteButtonActionListener());

                        countries = country.getCountries();
                        Vector vector = new Vector();

                        for (Country ct : countries) {
                            vector.add(ct.getName());
                        }
                        citiesDialog.loadCountryNames(vector);
                    }
                });

            }
        }
    }

    private class DeleteButtonActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            int selectedRow = citiesDialog.getSelectedRow();

            if ((selectedCountry != null) && (selectedRow >= 0)) {
                city.deleteCity(cities.get(selectedRow));
                citiesDialog.removeRow(selectedRow);
            }
        }
    }

    private class ListMouseListener implements MouseListener {

        @Override
        public void mouseClicked(MouseEvent e) {
            citiesDialog.clearTable();
            selectedCountry = countries.get(citiesDialog.getSelectedIndex());
            cities = city.getCities(selectedCountry);

            for (City c : cities) {
                Vector row = new Vector();
                row.addElement(c.getName());
                row.addElement(c.getDistrict());
                row.addElement(String.format("%, d", c.getPopulation()));
                citiesDialog.fillTableData(row);
            }
        }

        @Override
        public void mousePressed(MouseEvent e) {
        }

        @Override
        public void mouseReleased(MouseEvent e) {
        }

        @Override
        public void mouseEntered(MouseEvent e) {
        }

        @Override
        public void mouseExited(MouseEvent e) {
        }
    }
}

SwingWorldJDBC.java

package controller;

import model.Country;
import model.City;
import view.MainWindow;

public class SwingWorldJDBC {

    public static void main(String[] args) {
        City city = new City();
        Country country = new Country();
        MainWindow mw = new MainWindow();
        Controller controller = new Controller(city, country, mw);            
    }
}
Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
Zsolt Ébel
  • 120
  • 1
  • 9
  • 1
    The question is, what were you expecting from a modal dialog? – MadProgrammer Jan 23 '17 at 19:25
  • 2
    So the "core" problem you have is you're making the dialog visible within its constructor, this is, as you've discovered, a bad idea. The constructor should not have these kind of side effects, it should just do enough to make the class initially stable. Instead, make the dialog visible AFTER you've configured it. One of my gut feelings is the dialog is doing to much work. IMHO, it should only allow the user to search for and select a city, when closed, the caller should be able to get this and then continue to load more information – MadProgrammer Jan 23 '17 at 19:31
  • As an observation, your `City` and `Country` class are doing to much work. IMHO they shouldn't be loading data from the database themselves, but should represent a single row of data. You should have one or more factory classes which can be provided with optional constraints, make the queries and generate the results, creating instances of the these objects as needed. This would allow you to create a greater level of abstraction and separation in your code – MadProgrammer Jan 23 '17 at 19:33
  • You may also like to have a look at [Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?](http://stackoverflow.com/questions/7229226/should-i-avoid-the-use-of-setpreferredmaximumminimumsize-methods-in-java-swi). The general consensus is, yes, you should avoid using them (there are exceptions to the rules, but generally speaking, none of your code really meets those) – MadProgrammer Jan 23 '17 at 19:35
  • MVC in Swing is difficult thing to do (as Swing is already a form of MVC), when dealing with this, I take a much higher level of abstraction approach, in your case, your controller doesn't care how an action occurs, only that it does. You might like to have a look at [this example](http://stackoverflow.com/questions/26517856/java-and-gui-where-do-actionlisteners-belong-according-to-mvc-pattern/26518274#26518274) and [this example](http://stackoverflow.com/questions/31576623/how-mvc-work-with-java-swing-gui/31576899#31576899) for some more ideas – MadProgrammer Jan 23 '17 at 19:45
  • That was a painful mistake to learn, but I don't think that I'll forget this one. Yeah I can feel that my project's design is far from optimal. Thank you so much for taking the trouble to look at my code thoroughly! – Zsolt Ébel Jan 23 '17 at 20:29

0 Answers0