MVC Example
— print (last updated: Mar 8, 2009) print

Select font size:
There is no code to download for completing the project in this document. Create the classes and enter the code by cut/paste or by keying it in yourself. This project relies on the code from the packages models and models.db used in the JDBC Test Programs document. It also assumes, as before, that MySQL has been installed so that the test database is accessible to the user guest with no password.

The goal of this document is twofold:

MVC Design

The Model-View-Controller (MVC) architectural pattern is used in software engineering to allow for the separation of three common features in GUI applications: Towards this end one defines three components of such an architecture: De-coupling these parts allows us to modify the visual presentation independently from the data representation and vice versa.

Java/Swing Presentation

The basic idea is to create three packages:
views
models
controller
The Main class will be in the controller package. We'll allow NetBeans to give name it. The central JFrame class will be called Frame; it's in the views package. Although NetBeans creates a main function in Frame, we will not use it in order to achieve the desired MVC separation. The structure of the Main class will look like this:
package controller;  // we'll let NetBeans name this one

import java.awt.event.*;     // action, window listeners
import javax.swing.event.*;  // other listeners
import javax.swing.*;        // Swing Components

// individual java.awt classes like Font, Color, etc.
// avoid java.awt.* because java.awt.Frame conflicts with views.Frame

import views.*;
import models.*;

public class Main {
  private Frame frame = new Frame();
  // plus other data members, particularly from models
  
  Main() {
    frame.setVisible(true);
    /*
      This is where the bulk of the code will go
    */
  }

  public static void main(String[] args) { 
    /* 
      Everything is based on invoking the Main() constructor.
      In many situations Main will throw an Exception in which
      case we must surround by a try .. catch block.
    */
    new Main(); 
  }
}
A GUI application contains only one JFrame object; in our case it's represented by frame. Other independent windows must be JDialog components connected to frame. Database tables and records will appear as object from classes defined in the models package.

The Frame class will be initially constructed using the GUI designer, but we will be accessing the source code quite a bit. In particular, we want to permit external control of the components used in Frame. For example, if Frame uses JButton doSomething, then we will make an interface which allow us to add an ActionListener to doSomething, like this:
package views;
import java.awt.event.*;

public class Frame extends javax.swing.JFrame {

  public void addDoSomethingActionListener(ActionListener al) {
    doSomething.addActionListener(al);
  }
  ...
}
Back in Main, we'll then define what to do on activating the button with code like this:
  ...
  Main() {
    ...
    frame.addDoSomethingActionListener(
      new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          // this is what to do
        }
      }
    );
  }
The object which handles the button's action event is an anonymous object defined by an anonymous inner class which implements the ActionListener interface. In Java terms this is what is represented by the code:
      new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
          // this is what to do
        }
      }
This type of construction is by far the most common for handling events since it is the most direct way to assign actions to these events without the creation of additional class name and object names.

Guidelines

Consolidating some of the ideas we've presented we come up with these guidelines:

NetBeans Construction

Although NetBeans' Designer gives the option of creating a so-called Desktop Application, we do not want to use this since it takes us too far afield from the basic Swing applications and the MVC design. Instead, we'll always work from Java Applications and add the the basic Swing containers (JFrame, JPanel, etc) as NetBeans' visual forms.
  1. Select File New Project
  2. In the New Project window, select the Java category, and choose Java Application, then click Next.
  3. Choose the project name "BooksMVC". Keep Create Main Class and Set as Main Project checkboxes checked. Click Finish.

We will let the automatically created package mvexample server as the repository for our Controller classes.

Creating the view

  1. Right-click on the project and select New JFrame Form. Enter:
    Class Name: Frame
    Package: views
    This automatically creates the views package with class Frame. Not shown is the companion file Frame.form which NetBeans uses for its GUI designer mode.
NetBeans presents you with the JFrame in design mode in which one can use the accompanying Palette and other tools to create many GUI code features automatically. In design mode, you should rely on the Inspector window to show all the components in a useful heirarchical manner.

In source mode you enter code at appropriate places. It also contains generated code which is not editable.
  1. Set the title, desired size and initial position of the frame.

    Right-click on the form and select Properties. You can set the properties most easily by clicking the button on the right-hand side of the line with the property.
    1. Set the title property to BooksMVC (or whatever you like).
    2. Set the minimumSize property to [400,300].
    3. Set the bounds property to [20,20,0,0].
Of these properties, the mimimumSize is often the most important since it usually dictates the actual initial size of the application. The bounds property setting we chose gives the initial (top,left) starting position of (20,20). Normally the last two coordinates of the bounds property dictate the initial width and height, but NetBeans designer code nullifies their effect.
  1. Again, right-click on the form and select Set Layout BorderLayout
  2. Make the following additions to the form:
  3. Right-click on the panel and change the Layout to FlowLayout.
  4. Right-click on jButton1 and change the Text to "Read Table" and change the Variable name to "read".
  5. Right-click on the textarea and change Variable name to "display".
  6. Right-click on the text area, select Properties and uncheck the checkbox for the editable property.
  7. Change to source mode. Add this import line (just underneath the package statement at the top):
    import java.awt.event.*;
    
    Add these member functions (just inside the class Frame declaration):
      public void setDisplayText(String text) {
        display.setText(text);
      }
    
      public void addReadActionListener(ActionListener a) {
        read.addActionListener(a);
      }
    
    We're done with views.Frame!
  8. Edit booksmvc.Main and add the import line:
    import views.*;
    
    Modify the class code to be this:
      private Frame frame = new Frame();
    
      public Main() throws Exception {
        frame.setVisible(true);
      }
        
      public static void main(String[] args) {
        try {
          new Main();
        } catch(Exception x) {
          x.printStackTrace(); 
        }
      }
    
  9. Test run the program. The visual appearance of the application should be right although nothing happens yet.
From the MVC point of view, we have made the views.Frame class act like a Java GUI component in its own right by allowing us to assign manipulate the behavior externally by providing the minimal interface needed for this application.

Establishing the controller

To complete the application we create the Model and hook the Model to the View inside the Controller, i.e. the booksmvc.Main class.
  1. Copy the model and model.db packages from the TestJDBC project into the Source Packages of this project.
  2. Add the MySQL driver JAR file as a project library as before.
  3. Edit booksmvc.Main. Add these imports:
    import java.awt.event.*;
    import java.util.List;
    import models.*;
    
    Add this data declaration inside class Main (same as the frame variable):
      private Books books = new Books();
    
    Add this code inside the Main constructor after the "frame.setVisible(true);" statement:
        frame.addReadActionListener(
          new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
              String display = "";
              List<Book> bookList = null;
              try {
                bookList = books.fetchAll();
                for (Book book : bookList) {
                  display += book + "\n";
                }
              } catch (Exception x) {
                display = x.toString();
                x.printStackTrace();
              }
              frame.setDisplayText(display);
            }
          }
        );
    
  4. Again, test-run the program. It should work now, displaying the books table rows in the text area.

Taking charge of closing

One last step, although not necessary in such a simple application, is to take charge of the window closing event. By default, the Frame will perform "exit on close." Although this suits us here, in many situations we want to make sure the user did not inadvertently close the application.
  1. Go back to views.Frame in design mode. Select the JFrame component from the Inspector window, and bring up its Properties. Set the defaultCloseOperation property to DO_NOTHING_ON_CLOSE (choose it from the drop-down).
  2. Edit booksmvc.Main and add this import line:
    import javax.swing.JOptionPane;
    
    Add this code to the end of the Main() constructor:
        frame.addWindowListener(
          new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent evt) {
              int response = JOptionPane.showConfirmDialog(
                frame, "Are you sure?", "OK to quit?", JOptionPane.YES_NO_OPTION
              );
              if (response == JOptionPane.YES_OPTION) {
                System.exit(0);
              }
            }
          }
        );
    

The standalone application

Do a "Build Clean" to create the project JAR file in the dist folder. As suggested in the output, to run it, change directory to this folder and do:
java -jar BooksMVC.jar
In Linux, disconnect the shell from the application by backgrounding it with a terminal "&" after the command:
java -jar BooksMVC.jar &
In Windows, the executable javaw must be used to disconnect the application from the shell:
javaw -jar BooksMVC.jar
In the Windows case, however, using javaw will disconnect the standard output from the shell.

Desktop activation

In Linux using the GNOME Desktop, you can create a Launcher which uses the command:
java -jar path-to-the-dist-folder/BooksMVC.jar 
In Windows, create a Shortcut:
  1. Right-click on the desktop and select New Shortcut. This brings up wizard which asks for a location. You should be able to simply type javaw.exe (not java.exe). Click Next, then Finish.
  2. Rename the shortcut whatever you like and then right-click on it to obtain the properties.
  3. Change Start in to the path the the dist folder
  4. In the Target entry append this:
    -jar BooksMVC.jar
    Click OK to quit.


© Robert M. Kline