Chapter 13

Employee Files


CONTENTS


Introduction

Now that you've learned the foundation for creating intranet applications, let's get cooking. This chapter and the next several chapters guide you through some sample intranet applications. These applications are real-world examples. You don't find any animated buttons or scrolling text classes sprinkled in either. This is hardcore corporate database programming, but because you've developed such an excellent set of foundation classes, it isn't difficult at all to create the applications.

This chapter covers the following topics in regard to the Employee Files application:

This four-step format is used throughout the sample application chapters. Hopefully it provides you with valuable insight and ideas for creating your own intranet applications.

Application Design

This is the first sample application, and it is quite simple. It is the Employee Files. This application enables you to maintain records on the employees in your company. This application is sort of the "big toe" for the rest of the programs, because many of the other programs use information that is stored in the employee table. Programs such as the online phone book or the in/out board use this data.

Figure 13.1 is the proposed user interface for the Employee Files program.

Figure 13.1 : The Employee files user interface.

This application is semi-modeless, which means that it has no operating mode. You don't have to inform the program that you are going to be adding new records or removing records. You can flow through the program, and it determines what is done.

The basic functionality of this application is to create, read, update, and delete employee records. These records are stored in a database. You will learn about database design later in this chapter.

Think of this application as a pointer into the employee table. When the pointer is situated on a record, it is the current record. This current record is displayed to the user and the user can do whatever he chooses with it. The user can also insert records into the table at the end.

The user requires a method of moving this pointer from employee to employee. The best way to present this information to the user is through the use of a pick list.

Using a Pick List

The pick list is a selection of all the records in the employee table. This selection includes the first and last name of each employee. The user is allowed to select one name from the displayed list. After the selection is made, the chosen record is fully retrieved and displayed.

To select a record, double-click the list item or single-click and then press the OK button.

Figure 13.2 shows the concept of the employee pick list.

Figure 13.2 : The employee pick list.

This pick list is opened when the user presses the Choose button, a standard SimpleDBUI button.

Now that you have a sense of the application design, let's look at the database.

Database Design

This application is responsible for manipulating employee records. These records are stored in a single table. Currently, there is no need to extend the scope of the employee data to a second table.

The information stored in the employee table is basic information. Table 13.1 shows the columns needed in the employee table.

Table 13.1. The employee table layout.

DescriptionColumn Name Type
Can Be Null?
Default
Employee IDemp_idnumber(5)
N
None
First Namefirst_name char(40)
N
None
Middle Namemid_name char(40)
Y
None
Last Namelast_namechar(40)
N
None
Social Security Numberssn char(15)
Y
None
Address Line 1addr_line_1 char(80)
Y
None
Address Line 2addr_line_2 char(80)
Y
None
Citycitychar(80)
Y
None
Statestatechar(80)
Y
None
Zip Codezip_codechar(20)
Y
None
Salarysalarynumber(7,2)
Y
None
Home Phone Numberhome_phone_nbr char(20)
Y
None
Work Extension Numberwork_ext_nbr char(20)
Y
None
In/Out Indicatorin_out_ind char(1)
N
'N'

The entire data model is laid out into an entity relationship diagram or ERD. An ERD represents all the tables in your data model as an entity. The relationship between each entity is then shown. There are many types of relationships between entities: one-to-one, one-to-many, zero-or-more-to-one, zero-or-more-to-many, and so on.

Figure 13.3 shows the entity relationship diagram for the database as it stands in this chapter. As you get deeper into the sample applications, you see the entity relationship diagram grow to encompass all the tables.

Figure 13.3 : The employee entity.

In addition to creating a table, you create a database synonym for the table. This allows everyone to access the table with the same name and not have to worry about the schema in which the table resides.

Before you see the SQL for creating the table, there is a small matter of users and schemas. The data model for the sample applications in this book relies on no particular user or schema. However, the same model is used in the development of the applications.

All of the tables in this book are created by the master user for the database. Because you use Oracle for developing the database, this user is system. You also create an Oracle role and a user for the database. Full access to all of the tables is granted to the role. The user is granted access to the role.

Note
Although this book uses Oracle as a database, the table definitions are easily converted to other database management systems. After the tables are created in your own DBMS, this application and the others that follow will run just fine.

What's a Role?
A role is like a user group. It can have specific rights granted to it. These rights can be table access or possible system administration capabilities.
After a role is created, you can grant users access to the role. When a user is granted access to a role, that user can perform all of the functions granted to the role. Users can also be granted to several roles at one time. Many users can also be granted to a single role. This flexible structure allows complex database security schemes to be implemented without much work.

The role that you created is called ia_user, for "intranet application" user. The user that you created shares the same name, ia_user. The user is granted to the role. Therefore, all of the granting that is done in the creation SQL is only to the role.

Note
The SQL code to create the user and role is on the CD-ROM in the examples directory for this chapter. It is in a file called user.sql. This file is Oracle-specific, but should work on other databases with little or no modifications.

Finally, Listing 13.1 is the SQL commands to create the database.


Listing 13.1. The Employee table creation SQL.
/*    Create the table */
create table emp_t
(
    emp_id                number( 5 ) not null,
    first_name            char( 40 ) not null,
    mid_name            char( 40 ),
    last_name            char( 40 ) not null,
    ssn                    char( 15 ),
    addr_line_1            char( 80 ),
    addr_line_2            char( 80 ),
    city                char( 80 ),
    state                char( 80 ),
    zip_code            char( 20 ),
    salary                number( 7,2 ) not null,
    home_phone_nbr        char( 20 ),
    work_ext_nbr        char( 20 ),
    in_out_ind            char( 1 ) default 'N' not null
);
/*    Create a primary key */
alter table emp_t
    add
    (
        primary key
        (
            emp_id
        )
    );
/*    Create the synonym */
create public synonym emp for emp_t ;

Note
The SQL in Listing 13.1 is quite generic, but it might not work on every database. This particular SQL has been tested with Oracle.

Caution
The code in Listing 13.1 does not work with ODBC.

The first SQL clause creates the table emp_t. The second clause creates a primary key using the emp_id column. Making this the primary key ensures that the values in the column are unique across all rows. Lastly, the public synonym emp is created for the table emp_t.

After you create the table, you are ready to build the application.

Implementation

The rest of this chapter discusses the implementation of the Employee Files program. The first feature discussed is the user interface and how it is created. Second, the database access used in the program is discussed. Finally, you learn about any programming pitfalls that came up during the application construction.

Each sample application in this book uses a different approach to developing the user interface. This variety shows the different ways you can do your own interfaces. Hopefully, you get a cross-section of many different styles and can choose the one that suits you the best.

User Interface

The screen layout for this application is presented in a nice manner that is achieved through the use of a GridBagLayout. This ogre of a layout manager is difficult to work with, but when it is tamed it can provide wonderful layout capabilities.

Tip
The JifPanel class provides a method called addWithConstraints() that allows you to specify GridBagLayout constraints in a simple manner and add them with the component to the layout. It is used throughout many of the applications in this chapter.


Listing 13.2 gives the user interface construction code for the Employee program.


Listing 13.2. Employee files interface construction source code.
//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    Label                         l_emp_id;
    Label                         l_fn;
    Label                         l_mn;
    Label                         l_ssn;
    Label                         l_ln;
    Label                         l_address;
    Label                         l_city;
    Label                         l_state;
    Label                         l_zc;
    Label                        l_salary;
    Label                        l_home_phone_nbr;
    Label                        l_work_ext_nbr;
    Label                        l_in_out_ind;

    JifTextField                 emp_id;
    JifTextField                 first_name;
    JifTextField                 mid_name;
    JifTextField                 ssn;
    JifTextField                 last_name;
    JifTextField                 addr_line_1;
    JifTextField                 addr_line_2;
    JifTextField                 city;
    JifTextField                 state;
    JifTextField                 zip_code;
    JifTextField                salary;
    JifTextField                home_phone_nbr;
    JifTextField                work_ext_nbr;

    JifCheckbox                    in_out_ind;

//****************************************************************************
//* Constructor                                          ;                     *
//****************************************************************************

    public
    EmployeeUI( SimpleDBJiflet jiflet )
    {
        super( jiflet );

        GridBagLayout gbl = new GridBagLayout();

        int cw[] = { 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
            14, 14, 14 }; // 17

        int rh[] = { 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14 }; // 12

        double rc14_0[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        gbl.columnWidths = new int[ 17 ];
        gbl.rowHeights = new int[ 12 ];

        gbl.columnWeights = new double[ 17 ];
        gbl.rowWeights = new double[ 12 ];

        System.arraycopy( cw, 0, gbl.columnWidths, 0, 17 );
        System.arraycopy( cw, 0, gbl.rowHeights, 0, 12 );

        System.arraycopy( rc14_0, 0, gbl.columnWeights, 0, 17 );
        System.arraycopy( rc14_0, 0, gbl.rowWeights, 0, 12 );

        setLayout( gbl );

        l_emp_id = new Label( "Employee ID:", Label.RIGHT );
        addWithConstraints( l_emp_id, "anchor=east;x=0;y=0" );

        l_ssn = new Label( "SSN:", Label.RIGHT );
        addWithConstraints( l_ssn, "anchor=east;x=6;y=0" );

        l_fn = new Label( "First Name:", Label.RIGHT );
        addWithConstraints( l_fn, "anchor=east;x=0;y=1" );

        l_mn = new Label( "Middle Name:", Label.RIGHT );
        addWithConstraints( l_mn, "anchor=east;x=0;y=2" );

        l_ln = new Label( "Last Name:", Label.RIGHT );
        addWithConstraints( l_ln, "anchor=east;x=0;y=3" );

        l_address = new Label( "Address:", Label.RIGHT );
        addWithConstraints( l_address, "anchor=east;x=0;y=4" );

        l_city = new Label( "City:", Label.RIGHT );
        addWithConstraints( l_city, "anchor=east;x=0;y=6" );

        l_state = new Label( "State:", Label.RIGHT );
        addWithConstraints( l_state, "anchor=east;x=0;y=7" );

        l_zc = new Label( "Zip Code:", Label.RIGHT );
        addWithConstraints( l_zc, "anchor=east;x=6;y=7" );

        l_salary = new Label( "Salary:", Label.RIGHT );
        addWithConstraints( l_salary, "anchor=east;x=0;y=9" );

        l_home_phone_nbr = new Label( "Home Phone:", Label.RIGHT );
        addWithConstraints( l_home_phone_nbr, "anchor=east;x=0;y=10" );

        l_work_ext_nbr = new Label( "Work Ext:", Label.RIGHT );
        addWithConstraints( l_work_ext_nbr, "anchor=east;x=6;y=10" );

        l_in_out_ind = new Label( "In/Out:", Label.RIGHT );
        addWithConstraints( l_in_out_ind, "anchor=east;x=0;y=12" );

        emp_id = new JifTextField( "", "emp_id", true );
        addWithConstraints( emp_id, "x=1;y=0;width=5;fill=horizontal" );

        ssn = new JifTextField( "", "ssn" );
        addWithConstraints( ssn, "x=7;y=0;width=7;fill=horizontal" );

        first_name = new JifTextField( "", "first_name" );
        addWithConstraints( first_name, "x=1;y=1;width=13;fill=horizontal" );

        mid_name = new JifTextField( "", "mid_name" );
        addWithConstraints( mid_name, "x=1;y=2;width=13;fill=horizontal" );

        last_name = new JifTextField( "", "last_name" );
        addWithConstraints( last_name, "x=1;y=3;width=13;fill=horizontal" );

        addr_line_1 = new JifTextField( "", "addr_line_1" );
        addWithConstraints( addr_line_1, "x=1;y=4;width=13;fill=horizontal" );

        addr_line_2 = new JifTextField( "", "addr_line_2" );
        addWithConstraints( addr_line_2, "x=1;y=5;width=13;fill=horizontal" );

        city = new JifTextField( "", "city" );
        addWithConstraints( city, "x=1;y=6;width=13;fill=horizontal" );

        state = new JifTextField( "", "state" );
        state.setStyle( JifTextField.UPPER );
        addWithConstraints( state, "x=1;y=7;width=5;fill=horizontal" );

        zip_code = new JifTextField( "", "zip_code" );
        zip_code.setStyle( JifTextField.NUMERIC );
        addWithConstraints( zip_code, "x=7;y=7;width=7;fill=horizontal" );

        salary = new JifTextField( "", "salary" );
        addWithConstraints( salary, "x=1;y=9;width=5;fill=horizontal" );

        home_phone_nbr = new JifTextField( "", "home_phone_nbr" );
        addWithConstraints( home_phone_nbr, "x=1;y=10;width=5;fill=horizontal" );

        work_ext_nbr = new JifTextField( "", "work_ext_nbr" );
        addWithConstraints( work_ext_nbr, "x=7;y=10;width=7;fill=horizontal" );

        in_out_ind = new JifCheckbox( "in_out_ind" );
        addWithConstraints( in_out_ind, "x=1;y=12;width=7;fill=horizontal" );

        //    Disable buttons...
        saveButton.disable();
        chooseButton.disable();
        deleteButton.disable();

        //    Add the buttons...
        addWithConstraints( newButton, "x=15;y=0;width=2;fill=horizontal" );
        addWithConstraints( saveButton, "x=15;y=2;width=2;fill=horizontal" );
        addWithConstraints( chooseButton, "x=15;y=4;width=2;fill=horizontal" );

        //    Tell which are numeric...
        emp_id.setNumeric( true );
        salary.setNumeric( true );

        //    Set the focus to the first field...
        setFocus( emp_id );
    }

A notable item about this user interface (and other GridBagLayout jiflets in this book) is that the grid settings are hard-coded. A permanent 12 row by 17 column grid is used for the user interface. The components are placed within the grid only after the grid is set.

Tip
In this user interface, the Labels all have instance variables associated with them. Unless you plan to change the text of a label during the program life-cycle, this is completely unnecessary. It is done here for clarity only.

The Employee Pick List

Another class has been developed for this application. This is the EmployeePickList class. This class derives from the PickList class of jif.awt package (as discussed in Chapter 11, "User Interface Classes") and presents the user with a selection of employees. When one is chosen, the object stores the selection and waits for someone to ask who has been chosen. Let's take a look at some of the source code.

//****************************************************************************
//* EmployeePickList                                                          *
//****************************************************************************

public class
EmployeePickList
extends PickList

As stated earlier, you extend the PickList class. In order to do this, you must supply an init() method. The following is this method:

//****************************************************************************
//* init                                                                      *
//****************************************************************************

    public void
    init()
    {
        int rows = retrieveEmployees();

        if ( rows > 0 && getParent() instanceof Jiflet )
            ( ( Jiflet )getParent() ).verboseLog( "Retrieved " +
                Integer.toString( rows ) + " Employees" );
    }

This method calls the retrieveEmployees() method. Also, if this pick list is used with a jiflet and verbose mode is turned on, the number of employees that is retrieved is written to the log file.

The retrieveEmployees() method shown in Listing 13.3 is the meat of this class. It performs an SQL SELECT statement from the database, parses the results, and places them in the pick list for the user to select from.


Listing 13.3. The retrieveEmployees() method.
//****************************************************************************
//* retrieveEmployees                                         ;                *
//****************************************************************************

    int
    retrieveEmployees()
    {
        String        sql;
        boolean        rv = false;
        int            rows = 0;

        sql = "select * from emp order by last_name, first_name";

        try
        {
            rv = myConnection.getStatement().execute( sql );
        }
        catch ( SQLException e )
        {
            System.out.println( "Error during retrieve: " + e.toString() );

            //    No employees to return...
            return( 0 );
        }

        //    Is this a result set?
        if ( rv )
        {
            try
            {
                ResultSet rs = myConnection.getStatement().getResultSet();

                //    Spin through the results and add them to the list...
                while ( rs.next() )
                {
                    EmployeeRecord er = new EmployeeRecord( rs );

                    //    Add to list...
                    if ( er.emp_id != -1 )
                    {
                        myList.addItem( er.nice_name );

                        //    Add to row mapper...
                        rowMap.insertElementAt( er, rows );

                        //    Increment row counter...
                        rows++;
                    }
                }
            }
            catch ( SQLException e )
            {
                //    Indicate an error!
                rows = -1;
            }
        }

        //    We're done!
        return( rows );
    }

The interesting twist here is that each employee row is stored in another class called EmployeeRecord. This class has a corresponding instance variable for each column in the employee table. The class is smart and knows how to read a row out of a JDBC ResultSet object.

So as you walk through the results returned by the SQL statement, you create a new EmployeeRecord. You store these records in a Vector for later use.

At the end, you return the number of rows that are retrieved and added to the pick list. If there is an error, you return -1.

The reason you store each record is for easy access. When the user selects the employee from the list, you ask the pick list to give you a copy of the record that it already retrieved. This is done in the getRecord() method:

//****************************************************************************
//* getRecord                                         & nbsp;                      *
//****************************************************************************

    public EmployeeRecord
    getRecord( int where )
    {
        return( ( EmployeeRecord )rowMap.elementAt( where ) );
    }

The pick list returns the index of the item selected. This class uses a neat trick to keep track of what row is where in the List. A Vector called rowMap is created. As a row of data is retrieved from the database and placed into the pick list's List, it is also stored in the Vector object at the same index level.

Later, when you need an EmployeeRecord from the pick list, instead of rereading the data from the database, you retrieve the row from the Vector. This is done in the getRecord() method. You see this used quite a bit in various programs.

The EmployeePickList object is created and displayed in the main program when the user presses the Choose button. Listing 13.4 shows how it is done.


Listing 13.4. The action() and chooseEmployee() methods.
//****************************************************************************
//* action                                         &nbs p;                         *
//****************************************************************************

    /**
    * My child panel may start up picklists. It is my responsibility to
    * handle them. That is done here.
    */
    public boolean
    action( Event event, Object arg )
    {
        if ( event.target == getUIPanel() )
        {
            switch ( ( ( Integer )arg ).intValue() )
            {
                case JifMessage.chOOSE:
                    if ( getDBRecord().didDataChange() )
                    {
                        chgDlg = new ResponseDialog( this,
                        "Data Change",
                        "The record has changed.\n" +
                        "Do you wish to save your changes?",
                        "Yes,No,Cancel" );

                        chgDlg.show();
                    }
                    else
                        chooseEmployee();
                    return( true );
            }
        }

        //    Handle picklist events...
        if ( event.target instanceof EmployeePickList )
        {
            int                    rv = ( ( Integer )arg ).intValue();
             EmployeePickList     epl = ( EmployeePickList )event.target;

            if ( rv != -1 )
            {
                //    Disable save on choose...
                getUIPanel().saveButton.disable();

                //    Display it on the screen...
                setDBRecord( ( DBRecord )epl.getRecord( rv ) );
                getUIPanel().moveToScreen();
            }

            //    Kill the dialog box...
            epl.hide();
            epl.dispose();

            //    Reset the focus...
            getUIPanel().requestFocus();

            //    We handled it...
            return( true );
        }

        //    Not handled...
        return( super.action( event, arg ) );
    }

//****************************************************************************
//* chooseEmployee                                        &n bsp;                  *
//****************************************************************************

    public void
    chooseEmployee()
    {
        startWait();

        EmployeePickList epl = new EmployeePickList( this, getConnector() );

        epl.center( true );
        epl.show();

        endWait();
    }

When the Choose button is clicked, a JifMessage is sent to the parent. This is received in the action() event handler method. At this point, you need to check whether any changes have been made to the currently displayed record. If so, you ask the user whether he wants to save them.

If there are no changes to save, the method chooseEmployee() is called. This method creates and displays an EmployeePickList object.

When the user selects a pick list item or closes the pick list window, it generates an ACTION_EVENT event. You capture this event and act accordingly.

If the pick list returns a -1 value, you know that the user has canceled his selection. Otherwise, the value returned is the row number that is selected. You retrieve the EmployeeRecord at that row, make it the current record, and request that the user interface display it.

Finally, a little cleanup is in order. You hide() and dispose() of the pick list window and then reset the focus back to your window.

Database Access

The Employee program communicates with the database through the use of an EmployeeRecord object. This DBRecord derivation knows how to create, read, update, and delete records from the employee table. The following are the instance variables of this class:

//****************************************************************************
//* Constants                                         & nbsp;                      *
//****************************************************************************

    public final static String     TABLE_NAME = "emp";

//****************************************************************************
//* Members                                         &nb sp;                        *
//****************************************************************************

    //    A variable for each table column...
    public int                    emp_id = -1;
    public String                first_name = "";
    public String                mid_name = "";
    public String                last_name = "";
    public String                ssn = "";
    public String                addr_line_1 = "";
    public String                addr_line_2 = "";
    public String                city = "";
    public String                state = "";
    public String                zip_code = "";
    public int                    salary = 0;
    public String                home_phone_nbr = "";
    public String                work_ext_nbr = "";
    public String                in_out_ind = "N";

    //    A computed column...
    public String                nice_name;

Note
The EmployeeRecord, EmployeePickList, and other database classes are reused in several other applications. They are placed in their own package along with other shared code. This package is called jif.common. It contains all the common classes between all the applications.

Programming Considerations

This application is quite routine. Because you use the stock SimpleDBUI and SimpleDBJiflet classes as a base, not much extra work is required.

The one interesting programming consideration that comes up during the creation of this application is the caching of data in the pick list. This class purposely stores a copy of each row retrieved simply for the convenience of the calling program. When the user selects an item from the pick list, it does not have to be re-retrieved from the database because it has been cached.

Summary

This chapter introduces you to the first sample application in the intranet application suite-the Employee Files. This program is responsible for creating, updating, and deleting rows from the employee table, which is useful for human resources employees. Also, it can be modified for the employees to update their own information.

In Chapter 14, "Human Resources: Benefits Maintenance," you design and create an Employee Benefits Maintenance application. This program allows employees to change the parameters of their company-provided benefits such as 401K contributions and even W-4 exemptions.