11.11 Example Application: Contact Manager The example is an advanced mobile contact manager application provided by PointBase. The application itself is very simple: It mainly duplicates features commonly found in advanced address books. For example, it allows the user to store contact name, address, and phone numbers with pictures; provides intuitive browsing and searching interfaces; and synchronizes with backend database servers. Figure 11.3 demonstrates the application in action on a PocketPC device running Insignia's Jeode PersonalJava VM.
![](/image/library/english/10153_11fig03.gif) The clientside application contains a set of AWT UI classes that conforms to the PersonalJava specification. Behind those UI drivers, there is a database access layer and a generic on-device JDBC database layer. Now, we focus on the code in the data access layer, which is contained in a single class: DBManager.Class DBManager is a singleton class that provides a single point of entry to the database from the application. The singleton pattern avoids threading complexities involved with embedded databases. The code snippet below is the constructor and initialization method of DBManager. It makes a connection to the database, loads the table schema, populates the table with sample data, and creates SQL statement templates (PreparedStatement) for later use. As we can see, everything here is standard JDBC stuff. For enterprise Java developers, the DBManager class (Listing 11.2) should be easy to understand.Listing 11.2. The DBManager class (Part 1) in Contact Manager
class DBManager { // DBManager is a Singleton class private static DBManager instance; private String driver; private String url; private String user; private String password; private boolean delay; private Connection connection; private Statement statement; private PreparedStatement insert; private PreparedStatement find; private PreparedStatement delete; private PreparedStatement update; private PreparedStatement all; static DBManager getInstance() { if (instance == null) { instance = new DBManager(); } return instance; } private DBManager() { // get parameters from runtime properties // This allows us to switch to different JDBC // DBs without changing the application code. Properties properties = ContactManager.getProperties(); driver = properties.getProperty("driver", "com.pointbase.me.jdbc.jdbcDriver"); url = properties.getProperty("url", "jdbc:pointbase:micro:pbdemo"); user = properties.getProperty("user", "PBPUBLIC"); password = properties.getProperty("password", "PBPUBLIC"); delay = properties.getProperty("delayread","true").equals("true"); connect(); } private void connect() { try { // Load the driver class Class.forName(driver); // If the database doesn't exist, // create a new database. connection = DriverManager.getConnection(url, user, password); // Create template statement objects statement = connection.createStatement(); createStatement(); // If the database is newly created, // load the schema boolean newdb=initDatabase(); // Load sample data for the new tables if(newdb) { SampleDataCreator.insert(connection); } } catch (Exception e) { e.printStackTrace(); System.exit(1); } } void disconnect() { try { connection.commit(); statement.close(); insert.close(); find.close(); delete.close(); update.close(); all.close(); connection.close(); System.exit(0); } catch (Exception e) { e.printStackTrace(); System.exit(1); } } // Create the table and load the schema private boolean initDatabase() { try { String sql = "CREATE TABLE NameCard " + "(ID INT PRIMARY KEY, Name VARCHAR(254), " + "Company VARCHAR(254), Title VARCHAR(254), " + "Address1 VARCHAR(254), Address2 VARCHAR(254), " + "Phone VARCHAR(254), Email VARCHAR(254), "+ "Picture Binary(1000000))"; // if the table already exists, // this will throw an exception statement.executeUpdate(sql); // this means the database already exists return true; } catch (SQLException e) { // ignore the error - the table already // exists, which is good // so we don't need to add demo data later on return false; } } // create statement templates private void createStatement() { try { insert = connection.prepareStatement( "INSERT INTO NameCard (ID, Name, Company, Title, " + "Address1, Address2, Phone, Email, Picture) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"); find = connection.prepareStatement( "SELECT * FROM NameCard WHERE (Name LIKE ?) "+ "AND (Company LIKE ?) AND (Title LIKE ?) "+ "AND ((Address1 LIKE ?) OR (Address2 LIKE ?)) "+ "AND (Phone LIKE ?) AND (Email LIKE ?)"); delete = connection.prepareStatement( "DELETE FROM NameCard WHERE ID = ?"); update = connection.prepareStatement( "UPDATE NameCard SET ID=?, Name=?, Company=?, " + "Title=?, Address1=?, Address2=?, Phone=?, " + "Email=?, Picture=? WHERE ID = ?"); all = connection.prepareStatement( "SELECT ID, Name, Company, Title, Address1, " + "Address2, Phone, Email FROM NameCard"); } catch (SQLException e) { e.printStackTrace(); } } // Other methods }
Other methods in the DBManager provide access to the database via simple JDBC API calls. The following code snippet (Listing 11.3) demonstrates methods to search and manipulate name card records. These methods make heavy use of the SQL templates we defined earlier.Listing 11.3. The DBManager class (Part 2) in Contact Manager
Vector findNameCardsByKeyword(String name, String company, String title, String address1, String address2, String phone, String email) { Vector NameCards = new Vector(); String[] keywords = {name, company, title, address1, address2, phone, email}; try { for (int i = 0; i < keywords.length; i++) { String criteria = (keywords[i].equals(")) ? "%" : "%" + keywords[i] + "%"; find.setString(i + 1, criteria); } ResultSet resultSet = find.executeQuery(); while (resultSet.next()) { NameCard nameCard = new NameCard(resultSet.getInt(1), resultSet.getString(2), resultSet.getString(3), resultSet.getString(4), resultSet.getString(5), resultSet.getString(6), resultSet.getString(7), resultSet.getString(8)); if (!delay) loadPicture(nameCard); NameCards.addElement(nameCard); } } catch (SQLException e) { e.printStackTrace(); } return NameCards; } void addNameCard(NameCard nameCard) { nameCard.setID(getNewID()); try { insert.setInt(1, nameCard.getID()); insert.setString(2, nameCard.getName()); insert.setString(3, nameCard.getCompany()); insert.setString(4, nameCard.getTitle()); insert.setString(5, nameCard.getAddress1()); insert.setString(6, nameCard.getAddress2()); insert.setString(7, nameCard.getPhone()); insert.setString(8, nameCard.getEmail()); insert.setBytes(9, nameCard.getPicture().getBytes()); insert.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } void updateNameCard(NameCard nameCard) { try { update.setInt(1, nameCard.getID()); update.setString(2, nameCard.getName()); update.setString(3, nameCard.getCompany()); update.setString(4, nameCard.getTitle()); update.setString(5, nameCard.getAddress1()); update.setString(6, nameCard.getAddress2()); update.setString(7, nameCard.getPhone()); update.setString(8, nameCard.getEmail()); update.setBytes(9, nameCard.getPicture().getBytes()); update.setInt(10, nameCard.getID()); update.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } void deleteNameCard(NameCard nameCard) { try { delete.setInt(1, nameCard.getID()); delete.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } void loadPicture(NameCard nameCard) { try { ResultSet resultSet = statement.executeQuery( "SELECT Picture FROM NameCard WHERE ID = " + nameCard.getID()); resultSet.next(); Picture picture = new Picture(); picture.setBytes(resultSet.getBytes(1)); nameCard.setPicture(picture); } catch (SQLException e) { e.printStackTrace(); } } private int getNewID() { try { ResultSet resultSet = statement.executeQuery( "SELECT MAX(ID)+1 FROM NameCard"); if (resultSet.next()) { return resultSet.getInt(1); } else { return 0; } } catch (Exception e) { e.printStackTrace(); } return 0; }
|