Java Swing Books
— print (last updated: Nov 12, 2009) print

Select font size:
This application features the Hibernate model for the MySQL books table (see Java Books Application) applied to a GUI Swing application. The frame layout is intended to be strictly a demonstration of various Java Swing and NetBeans designer usage. For expedience, you can download the SwingBooks.zip  archive and install it as a Java Project with Existing Sources (remembering to add the MySQL and Hibernate Libraries), but I recommend following the step-by-step construction outlined below.

JDialog

A Java GUI application should only ever use one JFrame object. The JDialog class is the Swing class used to generate additional windows. A JDialog object has all the capabilites of the JFrame; the biggest difference is that it must be "associated with" a JFrame. In particular, the constructor for a JDialog class must initialize the superclass with the JFrame "parent" object. A constructor for the dialog class usually requires at least one parameter in order to achieve this effect with code like this:
public class MyDialog extends JDialog {
  public MyDialog(java.awt.Frame parent) { 
    super(parent);
    ...
  }
}
The dialog is "popped up" and "popped down" using the setVisible call with values true and false, respectively.

modality

The other key feature of a JDialog is its boolean modal property. This property expresses whether the dialog should "block" actions in the original GUI. A modality of true means to block actions, a modality of false (the default) means to let the actions of the GUI continue. A common way to set the modality is through the superclass constructor passed via a second parameter in the dialog's constructor, like this:
public class MyDialog extends JDialog {
  public MyDialog(java.awt.Frame parent, boolean modal) { 
    super(parent, boolean modal);
    ...
  }
}
The dialog creation is likely to be this (expressing the most common situation):
... frame = /* the GUI application's JFrame */
MyDialog dialog = new MyDialog(frame,true);
The modality can be modified after instantiation using the setModal function:
dialog.setModal(<true or false>);

View Construction

You have to have the books table set up in the test database accessible by the guest user with empty password as discussed in Java Books Application. For definiteness sake, initialize the table using the mysql client in a shell:
mysql -u guest test < table.sql
  1. Create a new Java Application project. Set the project name to be SwingBooks.
  2. Add the MySQL and Hibernate libraries to the project.
  3. Add the .xml files and hibernatemodels package from BooksApp into this project. The easiest way to do so is to Repeat for the hibernatemodels package.
  4. Rename hibernatemodels to models. This can be achieved by right-clicking the hibernatemodels package, selecting Refactor Rename, changing the name to "models" and pressing the Refactor button. You may get a warning message which should just be ignored.

    Double-check that the refactoring was complete. Look at books.hbm.xml and check that you have
    <class name="models.Book" table="books">
    
  5. Create a new JFrame Form with:
    Class Name:  Frame
    Package:     views
    
  6. Modify Frame in design mode. Using the Free Design layout for simplicity, drag 4 JButtons, 1 JLabel and 1 JTextField onto the form in a design something like this:
  7. Edit Frame in source mode, adding the import:
    import java.awt.event.*;
    
    and adding this code within the Frame class:
      public String getIdText() {
        return id.getText();
      }
    
      public void addDumpActionListener(ActionListener listener) {
        dump.addActionListener(listener);
      }
    
      public void addSelectActionListener(ActionListener listener) {
        select.addActionListener(listener);
      }
    
      public void addAddActionListener(ActionListener listener) {
        add.addActionListener(listener);
      }
    
      public void addUpdateActionListener(ActionListener listener) {
        update.addActionListener(listener);
      }
    
  8. Create a new JDialog Form (first time, go through Other) with:
    Class Name:  AddUpdateDialog
    Package:     views
    
  9. Modify AddUpdateDialog in design mode. Using the Free Design layout for simplicity, drag 1 JButton, 3 JLabels and 3 JTextFields onto the form in a design something like this:
  10. Edit AddUpdateDialog in source mode, adding the import:
    import java.awt.event.*;
    
    and adding this code within the AddUpdateDialog class:
      private int id;
    
      public int getId() { return id; }
    
      public void setId(int id) { this.id = id; }
    
      public String getTitleText() { return title.getText(); }
    
      public void setTitleText(String text) { title.setText(text); }
    
      public String getTypeText() { return type.getText(); }
    
      public void setTypeText(String text) { type.setText(text); }
    
      public String getQtyText() { return qty.getText(); }
    
      public void setQtyText(String text) { qty.setText(text); }
    
      public enum Mode { ADD, UPDATE };
      
      private Mode mode;
    
      public Mode getMode() { return mode; }
    
      public void setMode(Mode mode) {
        this.mode = mode;
        if (mode == Mode.ADD) {
          button.setText("Add");
          setTitle("Add");
        } else if (mode == Mode.UPDATE) {
          button.setText("Update");
          setTitle("Update");
        }
      }
    
      public void addButtonActionListener(ActionListener listener) {
        button.addActionListener(listener);
      }
    

Controller Construction

To emphasize what effects what, we'll add functionality to the application one step at a time. In this demo application, the effects are seen in the Ouput window as standard output. You may want to uncomment the "System.err.close()" statement to clean up the appearance of the output. The code illustrates the user-defined interface of the Frame and AddUpdateDialog classes by bolding the member access.

Frame functionality

  1. Modify Main to be the code below. Test run your application to ensure that the GUI frame appears.
    package swingbooks;
    
    import javax.swing.*;
    import java.awt.event.*;
    import org.hibernate.*;
    import org.hibernate.criterion.*;
    import java.util.*;
    import views.*;
    import models.*;
    
    public class Main {
      private Frame frame = new Frame();
      private DB db = new DB();
      private AddUpdateDialog dialog = new AddUpdateDialog(frame, true);
    
      private void error(String message) {
        JOptionPane.showMessageDialog(frame, message);
      }
    
      public Main() {
        frame.setBounds(0, 0, 250, 350);
        frame.setTitle("Books GUI Example");
        frame.setVisible(true);
        /* add functionality here */
      }
    
      public static void main(String[] args) {
        //System.err.close();
        new Main();
      }
    }
    
  2. Add functionality for the Dump Books Table button by appending this code to Main():
        frame.addDumpActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            try {
              Session sess = db.getSession();
              Criteria crit =
                sess.createCriteria(Book.class).addOrder(Order.asc("id"));
              List<Book> books = crit.list();
              for (Book book : books) {
                System.out.println(book);
              }
              System.out.println("------------------------");
            } catch (Exception x) {
              error(x.toString());
            }
          }
        });
    
  3. Add initial functionality for the Add Book button by appending this code to Main():
        frame.addAddActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            dialog.setMode(AddUpdateDialog.Mode.ADD);
    
            dialog.setTitleText("");
            dialog.setTypeText("");
            dialog.setQtyText("");
    
            dialog.setVisible(true);
          }
        });
    
    The dialog should pop up, but nothing will happen yet.
  4. Add functionality for the Select by id button by appending this code to Main():
        frame.addSelectActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            try {
              int id = Integer.parseInt(frame.getIdText().trim());
              Session sess = db.getSession();
              Book book = (Book) sess.load(Book.class, id);
              System.out.println(book);
              System.out.println("------------------------");
            } catch (NumberFormatException x) {
              JOptionPane.showMessageDialog(frame, "id field is incorrect");
            } catch (Exception x) {
              JOptionPane.showMessageDialog(frame, x.toString());
            }
          }
        });
    
    Enter an id into the text field and activate the button. Try correct id's (1-5), junk values like "", and out-of range values like -1, 10.
  5. Add initial functionality for the Update by id button by appending this code to Main():
        frame.addUpdateActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent evt) {
            try {
              int id = Integer.parseInt(frame.getIdText().trim());
              Session sess = db.getSession();
              Book book = (Book) sess.load(Book.class, id);
              dialog.setMode(AddUpdateDialog.Mode.UPDATE);
              dialog.setId(id);
              dialog.setTitleText(book.getTitle());
              dialog.setTypeText(book.getType());
              dialog.setQtyText("" + book.getQty());
              dialog.setVisible(true);
            } catch (NumberFormatException x) {
              error("id field is incorrect");
            } catch (Exception x) {
              error(x.toString());
            }
          }
        });
    
    The dialog should pop up with the correct values inserted, but nothing will happen yet. You can see how we reuse this dialog for both add and update functions.

Dialog functionality

We continue the process by adding functionality for the dialog.
  1. Complete the functionality for the Add and Update by id buttons by appending this code to Main():
        dialog.addButtonActionListener(
          new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent evt) {
              Session sess = db.getSession();
              sess.getTransaction().begin();
              try {
                String title = dialog.getTitleText().trim();
                String type = dialog.getTypeText().trim();
                int qty = Integer.parseInt(dialog.getQtyText().trim());
    
                AddUpdateDialog.Mode mode = dialog.getMode();
    
                if (mode == AddUpdateDialog.Mode.UPDATE) {
                  int id = dialog.getId();
                  Book book = (Book) sess.load(Book.class, id);
                  book.setTitle(title);
                  book.setType(type);
                  book.setQty(qty);
                } else {
                  // mode == AddUpdateDialog.Mode.ADD
                  Book book = new Book(title, type, qty);
                  sess.save(book);
                }
                sess.getTransaction().commit();
                dialog.setVisible(false);
              } catch (NumberFormatException x) {
                error("id field is incorrect");
              } catch(HibernateException x) {
                error(x.getCause().toString().replaceFirst(".*: ", ""));
              } catch (Exception x) {
                error(x.toString());
              }
            }
          });
    
    Test the Add Book button by keying in:
       title: Another Book
        type: paper
    quantity: 22
    
    and pressing the dialog's Add button. Activate the Dump Books Table button to confirm.

    Test the Update button by entering an id of your choice, modifying it (like changing the quantity), pressing the dialog's Update button and then Dump Books Table button to confirm.
  2. Modify the closing behavior of the dialog by appending this code to Main():
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
    
        dialog.addWindowListener(new WindowAdapter() {
          @Override
          public void windowClosing(WindowEvent evt) {
            if (dialog.getMode() == AddUpdateDialog.Mode.ADD) {
              if (dialog.getTitleText().trim().equals("") &&
                dialog.getTypeText().trim().equals("") &&
                dialog.getQtyText().trim().equals("")) {
                dialog.setVisible(false);
              } else {
                // in "Add Mode" query the user to close if something
                // has been entered into one of the fields
                String message = "Do you want to exit\n" +
                  "without saving changes ?";
                int response = JOptionPane.showOptionDialog(
                  frame, message, null, JOptionPane.YES_NO_OPTION,
                  JOptionPane.WARNING_MESSAGE, null, new String[]{"yes", "no"}, "no");
                if (response == JOptionPane.YES_OPTION) {
                  dialog.setVisible(false);
                }
              }
            } else {
              // dialog.getMode() == AddUpdateDialog.Mode.UPDATE;
              // this would be more difficult to check for changes
              dialog.setVisible(false);
            }
          }
        });
    
    This change only affects the Add dialog. Test it by activating the Add Book button, entering something in any of the fields and closing the window with the "x". You'll see that you have to "say yes" to complete the closing. The idea is to prevent unintended loss of an addition.


© Robert M. Kline