Inquirer: a Simple SQL Tester
Development Tutorial


Inquirer 1.0.1

(15.4 Kb)
See also Inquirer Description and Installation

Inquirer is a simple GUI application that tests SQL statements. This is The Making of Inquirer: a tutorial that explains classes and methods used in the development of this application. The complete source code is included in the zip file. For any question about the purpose of Inquirer and the installation procedure see Inquirer Description and Installation

Class and Methods

See also Javadoc generated Documentation

Inquirer uses the basic services provided by java.sql and java.awt packages. The first one is used to perform queries on the chosen database, while the last one builds the interface between the user and JDBC.

This overview is about version 1.0 of Inquirer but the downloaded zip file contains Inquirer 1.0.1. Added features not reviewed here are Toolbar PANEL buttons and Column Title buttons. Refer for details to the included source code.

First of all, let's take a glance at the SQL section. Three are the related chuncks of code:

  1. Database Connection Code implementing the connection to the database using the specified driver and database.
  2. Database Query Code responsible for the query submission and the retrieving and showing of the Result Set.
  3. Database Close Closes the database and all its related data.

Then consider the AWT section. Four are the related chunck of code:

  1. Inquirer Constructor method Initializes private variables.
  2. Inquirer init method Initializes and formats the dialog window.
  3. ItemListener implementation Inquirer implements ItemListener to be aware of user's actions on the List components representing the columns of the database.
  4. ActionListener implementation Inquiry implements ActionListener to be aware of a user's query submission (ENTER key hit).

Selected Chuncks of Code


public class Inquirer implements
  ActionListener, ItemListener
{
  private Frame fMain;
  private TextField tfDriver;
  private TextField tfDatabase; 
  private TextField tfQuery;
  private Label lStatus;
  private Connection DBConnection;
  private Statement SQLStatement;
  private ResultSet rsQuery;
  private List[] lQueryColumn;
  private Panel pResultSet;
  private ScrollPane scroll;
  public static void main(String args[]) {
    Inquirer inquirer = new Inquirer();
    inquirer.init();
  }
...
}

main method

Fields of the Inquiry Class and the main function. This function simply construct an Inquiry Object and initializes it. All the useful code in this class is wrapped in Adapter Classes or in Listener methods and called by user's actions.

Chuncks Index


public Inquirer() {
  super();
  fMain = new Frame("Inquirer");      
  WindowAdapter waMain =
    new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        closeSql();
        System.exit(0);
      }
    }
  ;
  fMain.addWindowListener(waMain);
  tfDriver = new TextField(
    "sun.jdbc.odbc.JdbcOdbcDriver",80
  );
  tfDatabase = new TextField("jdbc:odbc:",80);
  FocusAdapter faDatabase =
    new FocusAdapter() {
      public void focusLost(FocusEvent e) {
        DBConnect(tfDatabase.getText());
      }
    }
  ;
  tfDatabase.addFocusListener(faDatabase);
  tfDatabase.addActionListener(this);
  tfQuery = new TextField("SELECT * FROM",80);
  tfQuery.addActionListener(this);
  lStatus = new Label(copyright,Label.LEFT);
  pResultSet = new Panel();
  scroll = new ScrollPane();
  scroll.setSize(200,200);
}

Inquirer Constructor method

The Constructor of Inquirer simply initializes all the fields of the Class with default values.

It defines also two Adapter Classes

  1. waMain a class that listens to the Window Closing Event and closes all the connections when such an event is performed
  2. faDatabase a class that listens to the Database Textfield Losing Focus Event and try to connect the specified database when such an event happens
Chuncks Index


protected void closeSql() {
  try {
    if(rsQuery != null) rsQuery.close();
    if(SQLStatement != null) SQLStatement.close();
    if(DBConnection != null) DBConnection.close();
  } catch(java.sql.SQLException exc) {
    lStatus.setText(exc.toString());
  }
}

Database Close

closeSql method closes the Connection, the Statement and the ResultSet Objects.

This method is declared protected because it must be accessible from the adapter method that listens to the window closing event. It's also used by the DBConnect method before the creation of a new connection.

Chuncks Index


public void init() {
  fMain.setLayout(new BorderLayout());
  Panel pTop = new Panel();
  GridBagLayout gbl = new GridBagLayout();
  pTop.setLayout(gbl);
  GridBagConstraints gbc =
    new GridBagConstraints()
  ;
  gbc.fill = GridBagConstraints.BOTH;
  gbc.gridwidth = 1;
  addComponent(
    pTop,new Label("Driver",Label.RIGHT),gbl,gbc
  );
  gbc.gridwidth = GridBagConstraints.REMAINDER;
  addComponent(pTop,tfDriver,gbl,gbc);
  gbc.gridwidth = 1;
  addComponent(
    pTop,new Label("Database",Label.RIGHT),gbl,gbc
  );
  gbc.gridwidth = GridBagConstraints.REMAINDER;
  addComponent(pTop,tfDatabase,gbl,gbc);
  gbc.gridwidth = 1;
  addComponent(
    pTop,new Label("Query",Label.RIGHT),gbl,gbc
  );
  gbc.gridwidth = GridBagConstraints.REMAINDER;
  addComponent(pTop,tfQuery,gbl,gbc);
  fMain.add(pTop,"North");
  fMain.add(lStatus,"South");
  scroll.add(pResultSet);
  fMain.add(scroll,"Center");
  fMain.pack();
  fMain.setVisible(true);
}

Inquirer init method

The init method builds the Dialog Layout placing the components to their right place.

Chuncks Index


public void itemStateChanged(ItemEvent e) {
  for(
    int col = 0; col < lQueryColumn.length; col++
  ) {
    if(lQueryColumn[col].equals(e.getSource())) {
      int idx =
        lQueryColumn[col].getSelectedIndex()
      ;
      for(
        int ncol = 0;
        ncol < lQueryColumn.length;
        ncol++
      ) {
        if(ncol != col) {
          lQueryColumn[ncol].select(idx);
        }
      }
      break;
    }
  }
}

ItemListener implementation

The itemStateChanged method implements the ItemListener Interface in the Inquiry Class. No Adapter Class is provided for ItemListeners because there is only one method to implement (this one).

This method is called when the user selects an item in a List Object and highlights all the corresponding rows in the other Lists.

Chuncks Index


protected void DBConnect(String dbName) {
  try {
    closeSql();
    Class.forName(tfDriver.getText());
    DBConnection =
      DriverManager.getConnection(dbName)
    ;
    DatabaseMetaData DBMetadata =
      DBConnection.getMetaData()
    ;
    lStatus.setText(
      "Connected to "+DBMetadata.getURL()+
      " Driver "+DBMetadata.getDriverName()+
      " Version "+DBMetadata.getDriverVersion()
    );
    SQLStatement = DBConnection.createStatement();
  } catch(java.lang.Exception exc) {
    lStatus.setText(exc.toString());
  }
}

Database Connection

DBConnect method connects to the database through the getConnection method. It uses the data filled in the Driver and Database textfields using the getText method. The status of the connection is shown in the status bar. Then a java.sql.statement Object is created.

This method is declared protected because it must be accessible by the adapter method that listens to the textfield losing focus event.

Chuncks Index


public void actionPerformed(ActionEvent e) {
  if(e.getSource().equals(tfQuery)) {
    try {
      if(SQLStatement == null) {
        lStatus.setText("Database not connected");
        return;
      }
      lStatus.setText(copyright);

ActionListener implementation

The actionPerformed method implements the ActionListener Interface in the Inquiry Class. No Adapter Class is provided for ActionListeners since there is only one method to implement (this one).

This method is called when the user hits the ENTER key on the Query Textfield submitting the query to the database.

Chuncks Index


      rsQuery = SQLStatement.executeQuery(
        e.getActionCommand()
      );
      ResultSetMetaData rsmd =
        rsQuery.getMetaData()
      ;
      int nCols = rsmd.getColumnCount();
      lQueryColumn = new List[nCols];
      pResultSet.removeAll();
      GridBagLayout gbl = new GridBagLayout();
      pResultSet.setLayout(gbl);
      GridBagConstraints gbc =
        new GridBagConstraints()
      ;
      gbc.fill = GridBagConstraints.BOTH;
      for(int ctCol = 1; ctCol <= nCols; ctCol++) {
        if(ctCol != nCols)
          gbc.gridwidth = 1
        ; else
          gbc.gridwidth =
              GridBagConstraints.REMAINDER
        ;
        addComponent(
          pResultSet,new Label(
            rsmd.getColumnLabel(ctCol),
            Label.CENTER
          ),gbl,gbc
        );
      }
      for(int ctCol = 0; ctCol < nCols; ctCol++) {
        lQueryColumn[ctCol] = new List(10,false);
        lQueryColumn[ctCol].addItemListener(this);
        if(ctCol != nCols-1)
          gbc.gridwidth = 1
        ; else
          gbc.gridwidth =
            GridBagConstraints.REMAINDER
        ;
        addComponent(
          pResultSet,lQueryColumn[ctCol],gbl,gbc
        );
      }
      fMain.pack();
      fMain.invalidate();
      fMain.repaint();
      while(rsQuery.next()) {
        for(int ctCol = 0; ctCol < nCols; ctCol++) {
          String str = rsQuery.getString(ctCol+1);
          if(str == null) str = "-";
          lQueryColumn[ctCol].add(str);
        }
      }
    } catch(java.sql.SQLException exc) {
      lStatus.setText(exc.toString());
    }
  }
}

Database Query

This chunck of code is executed when the actionPerformed method is called by the Query Textfield (tipically when the user hits the ENTER key). The query is read by a getActionCommand on the ActionEvent Object. The resulting Result Set, named rsQuery is asked for its metadata (i.e. number of columns). If the query doesn't give a Result Set an executeUpdate is better than executeQuery and avoids the SQLException.

Then two cycles begin:

  • a cycle for writing the labels of the columns
  • another one to construct a List Object for every column

All these components are added to a ScroolPane and formatted using GridBagLayout and GridBagConstraints Objects. A Listener is associated to every List to catch selection events.

Then the window is packed, repainted and, finally, begins the cycle for filling the Lists with the data in the rows of the columns.

Chuncks Index

More Questions?

For any question or suggestion, please use the comments form or write directly to TETRACTYS Freeware.
TETRACTYS Freeware Main Page

In the next Issue Java Tutorials will be

JDBC: SQL Statements with Inquirer

GeoCities