20.9 ListManager Model Classes
The ListManager web application uses a
User object to model a mailing list subscriber and
her mail delivery preferences. The User class does
not have a public constructor, however. Instead,
User objects are returned from a
UserFactory object, which uses a database backend
to provide a persistent store for subscriptions and delivery
preferences. The four public methods of
UserFactory are:
- insert( )
-
Adds a subscription to the database and returns a
User object for it
- select( )
-
Looks up the User object that corresponds to a
specified email address and password
- update( )
-
Updates email delivery preferences in the database to match the
current state of the specified User object
- delete( )
-
Deletes a subscription from the database, unsubscribing the specified
User from the mailing list
Note that these four methods are named after the SQL statements they
each use. The source code for User and
UserFactory are listed in Examples Example 20-7 and Example 20-8.
Example 20-7. User.java
package je3.servlet;
/**
* This class represents a mailing list subscriber.
* It has JavaBeans-style property accessor methods.
*/
public class User {
String email; // The user's e-mail address
boolean html; // Whether the user wants HTML-formatted messages
boolean digest; // Whether the user wants digests
boolean deleted; // Set by UserFactory.delete( ); tested by insert( )
// The constructor is package-private.
// See UserFactory for public methods to obtain a User object.
User(String email, boolean html, boolean digest) {
this.email = email;
this.html = html;
this.digest = digest;
this.deleted = false;
}
// The following property accessors follow JavaBeans naming conventions
public String getEmailAddress( ) { return email; }
public boolean getPrefersHTML( ) { return html; }
public boolean getPrefersDigests( ) { return digest; }
public void setEmailAddress(String email) { this.email = email; }
public void setPrefersHTML(boolean html) { this.html = html; }
public void setPrefersDigests(boolean digest) { this.digest = digest; }
}
Example 20-8. UserFactory.java
package je3.servlet;
import java.sql.*;
/**
* A class for creating new users in a database and retreiving existing users
* from the database. Note that it assumes that a database table with the
* specified name exists. You can create such a table with SQL like this:
*
* CREATE TABLE subscribers (
* email VARCHAR(64) PRIMARY KEY,
* password VARCHAR(20),
* html BIT,
* digest BIT);
**/
public class UserFactory {
Connection db; // The connection to the database
String tablename; // The name of the database table we'll use
// These prepared statements are created in the constructor, and used
// in the insert( ), select( ), update( ) and delete( ) methods.
// PreparedStatements are used for security so that the database cannot
// be attacked with usernames and passwords that include SQL quotes.
PreparedStatement insertUser, selectUser, updateUser, deleteUser;
// Create a new UserFactory object backed by the specified DB table
public UserFactory(Connection db, String tablename) throws SQLException {
this.db = db;
this.tablename = tablename;
// Prepare the SQL statements we'll use later. Parameters will be
// subsituted for the question marks in the methods below.
insertUser = db.prepareStatement("insert into " + tablename +
"(email,password,html,digest) " +
"values(?,?,0,0)");
selectUser = db.prepareStatement("select * from " + tablename +
" where email=?");
deleteUser = db.prepareStatement("delete from " + tablename +
" where email=?");
updateUser = db.prepareStatement("update " + tablename +
" set html=?,digest=? where email=?");
}
// Create a new User with the specified e-mail address and password
public User insert(String email, String password)
throws UserAlreadyExists, SQLException
{
// Check whether the user already exists
selectUser.setString(1, email);
ResultSet results = selectUser.executeQuery( );
if (results.next( )) throw new UserAlreadyExists(email);
// If not, create a new entry in the database
insertUser.setString(1, email);
insertUser.setString(2, password);
insertUser.executeUpdate( );
// And return a matching User object to the caller
return new User(email, false, false);
}
// Look up the User object for the specified address and password
public User select(String email, String password)
throws NoSuchUser, BadPassword, SQLException
{
// Look up the user
selectUser.setString(1, email);
ResultSet results = selectUser.executeQuery( );
// Check that the user exists
if (!results.next( )) throw new NoSuchUser(email);
// Check that the password is correct
String pw = results.getString("password");
if (!pw.equals(password)) throw new BadPassword(email);
// Return a User object representing this user and their mail prefs.
boolean html = results.getInt("html") == 1;
boolean digest = results.getInt("digest") == 1;
return new User(email, html, digest);
}
// Delete the specified User object from the database
public void delete(User user) throws SQLException {
if (user.deleted) return; // make sure we're not already deleted
// Delete the user from the database
deleteUser.setString(1, user.getEmailAddress( ));
deleteUser.executeUpdate( );
user.deleted = true; // Don't allow update( ) after delete( )
}
// Update the HTML and digest preferences of the specified User
public void update(User user) throws SQLException {
if (user.deleted) return; // Don't allow updates to deleted users
// Update the database record to reflect new preferences
updateUser.setInt(1, user.getPrefersHTML( )?1:0);
updateUser.setInt(2, user.getPrefersDigests( )?1:0);
updateUser.setString(3, user.getEmailAddress( ));
updateUser.executeUpdate( );
}
// The following are custom exception types that we may throw
public static class UserAlreadyExists extends Exception {
public UserAlreadyExists(String msg) { super(msg); }
}
public static class NoSuchUser extends Exception {
public NoSuchUser(String msg) { super(msg); }
}
public static class BadPassword extends Exception {
public BadPassword(String msg) { super(msg); }
}
}
|