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:
to introduce the MVC software design concepts applied to the creation
of a simple Java Swing GUI
to illustrate NetBeans' GUI Designer mode in which much of the code is
created automatically through swing component forms
(which are in reality XML files interpreted by NetBeans).
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:
the business logic & data access: typically a database
and the means of data retrieval
the user interaction: how the data and actions will be visually presented
the joining of data access to presentation
Towards this end one defines three components of such an architecture:
Model: This provides the means by which data is obtained
and manipulated. If a relational database is used,
one typically defines an Object Relational Model
by which access to the database is presented by member
functions of one or more classes.
In particular, the business logic should
rarely be allowed to access a DBMS directly since this would
tie the code to the DBMS.
View: This represents the visible user interface;
it knows enough about
the data to create a coherent presentation,
but does not actually do the work of presenting the data.
Instead, it provides the ability to manipulate data
outside the View through event handlers defined within.
Controller: This joins the Model
with the View. It is the source
of activity when an event occurs and
defines the event handling actions which access data (from the Model)
and are presented to the user (in the View).
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 oneJFrame 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:
classes in the views package should only use other
classes in this package
classes in the models package should only use other
classes in this package
there is only ever one JFrame class per application
events operations are handled in the controller
and connected
to event-generating objects in views classes through
interface functions.
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.
Select File New Project
In the New Project window, select the Java category,
and choose Java Application, then click Next.
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
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.
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.
Set the title property to BooksMVC
(or whatever you like).
Set the minimumSize property to [400,300].
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.
Again, right-click on the form and
select Set Layout BorderLayout
Make the following additions to the form:
From Swing Controls in the Palette,
select and drag a Text Area onto form.
It will expand to occupy the entire area.
From Swing Containers in the Palette,
select and drag a Panel onto the bottom of the form.
It will push the textarea up and occupy the bottom.
From Swing Controls
select and drag a Button onto panel (anywhere).
Right-click on the panel and change the Layout to FlowLayout.
Right-click on jButton1 and
change the Text to "Read Table" and
change the Variable name to "read".
Right-click on the textarea and change Variable name
to "display".
Right-click on the text area,
select Properties and
uncheck the checkbox for the editable property.
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!
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();
}
}
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.
Copy the model and model.db packages from the TestJDBC project into the Source Packages
of this project.
Add the MySQL driver JAR file as a project library as before.
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);
}
}
);
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.
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).
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:
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
(notjava.exe).
Click Next, then Finish.
Rename the shortcut whatever you like
and then right-click on it to obtain the properties.