How to Build J2EE Applications Using Novell Technologies: Part 6
Articles and Tips: article
Chief Architect
Zareus Inc.
jeff@jeffhanson.com
01 Nov 2002
This AppNote is the sixth in a series that outlines a platform and methodology for Java 2 Platform Enterprise (J2EE) application development on NetWare using a service-oriented architecture. In this AppNote, we will learn about how the platform interfaces with the data tier and how we can build our data-access components to be highly flexible and robust.
- Introduction
- The Need for Data Access Abstractions
- Overview of JDBC
- The Data Access Object Pattern
- Reducing Redundancy Abundancy
- hsqldb on NetWare
- Conclusion
Topics |
Java application development, J2EE, Java Management Extensions (JMX) |
Products |
Java 2 Enterprise Edition |
Audience |
developers |
Level |
intermediate |
Prerequisite Skills |
familiarity with Java programming |
Operating System |
n/a |
Tools |
none |
Sample Code |
yes |
Introduction
This is the sixth in a series of AppNotes outlining a platform and methodology for Java 2 Platform Enterprise Edition (J2EE) application development on NetWare using a service-oriented architecture.
In the previous AppNote, we thoroughly examined the platform kernel relating to services that JMX offers to empower our platform. In this AppNote, we will discuss how our platform interfaces with the data tier and how we can build our data-access components to be highly flexible and robust.
The Need for Data Access Abstractions
The data tier of enterprise application development offers integration and access to all services and data that are prevalent in enterprise applications and systems, including relational databases, directory services, and legacy systems.
Direct access to a data source links business logic and implementation details of an external resource. If the API to that resource changes, it will be necessary to also change your application source code throughout the application, resulting in a significant testing and maintenance burden.
However, a data access framework that is able to abstract data resources insulates callers from those resource changes. A data access framework presents a vendor-neutral interface to the caller; in addition, it manages the details of passing service requests from the caller to the business tier. This design is particularly beneficial because ultimately a single service request may access multiple data resources. The replacement of an existing data resource will also cause the existing data access class to be replaced, which then presents the new service to the caller. It is effortless for a system design to evolve with time when the business tier (with an abstract data-access layer) is isolated.
Overview of JDBC
JDBC technology allows you to access nearly any record-oriented data source from the Java programming language. It offers cross-DBMS connectivity to an extensive collection of SQL databases, and also provides access to non-relational data sources, such as spreadsheets or flat files.
The JDBC API is the industry standard database-connectivity mechanism between the Java programming language and relational databases. JDBC allows developers the full advantage of Java's "Write Once, Run Anywhere" promise for cross-platform applications that require access to enterprise data. A JDBC-enabled application allows a developer to effortlessly connect all available relational data, even in a heterogeneous environment.
The JDBC API is offered as a core part of the Java 2 platform and makes it possible to establish a connection with a relational database, send SQL statements to the database and process the results.
The Data Access Object Pattern
Code dependent on specific features of data resources connects business logic and data access logic. Therefore, replacing or modifying an application's data resources is very complicated.
The Data Access Object (DAO) pattern reduces the complications arising from tightly-coupled business logic and data access code by abstracting data sources to enable transparent access to the data. The DAO pattern has the following advantages:
Separates a data resource's interface from its data access implementation.
Adapts a specific data resource's access API to a generic interface.
Allows data access implementation to adjust independently from the code that uses the DAO.
Offers access to a particular data resource without coupling the resource's API to the business logic.
Permits application developers or deployment engineers the choice of a data access mechanism with no changes to the program code.
This approach allows a developer access to a data source only in terms of its DAO interface. Based on configuration data, an application uses a factory object to select the DAO implementation at runtime.
A DAO interface has at least one concrete class that implements the interface for a specific type of data source. To add a new type of data source, an application developer simply follows the steps listed below:
-
Create a class that implements the DAO interface for the new data source type.
-
Specify the implementing class's name in a meta-data configuration source.
-
Facilitate a re-initialization by the client service, which causes the creation of an instance of the new DAO class by the factory, which the application will now use.
Figure 1 shows the implementation of the DAO design pattern for a DAO that accesses data for a business-contacts resource. Notice how the factory is used to select the DAO implementation.
Figure 1: Implementation of the DAO design pattern for a DAO that accesses data for a business-contacts resource.
The DAO Interface
Using the DAO pattern, a developer accesses a data source only in terms of its DAO interface. This approach shields the application or client service from modifications to the underlying data source. The following is an example of a DAO interface for a DAO that abstracts data and functionality for a data source that stores information for business contacts:
public interface ContactsDAO { public Contact getContact(String personID, String addressID, String companyID) throws DAOException; public String addContact(String addStr) throws DAOException; public void modifyContact(String contactID, String modifyStr) throws DAOException; public void deleteContact(Contact contact) throws DAOException; }
The DAO Implementation
A DAO interface has at least one concrete class that implements the interface for a specific type of data source. The following is an implementation of the Contacts DAO interface for an hsqldb data source:
public class HSQLDBContactsDAO implements ContactsDAO { public static String GET_PERSON_STATEMENT = "select * from person where personID = ?"; public static String GET_ADDRESS_STATEMENT = "select * from address where addressID = ?"; public static String GET_COMPANY_STATEMENT = "select * from company where companyID = ?"; public HSQLDBContactsDAO() { } protected PersonDAO getPerson(String personID) throws DAOException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; PersonDAO ret = null; try { conn = getDataSource().getConnection(); ps = conn.prepareStatement(GET_PERSON_STATEMENT, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ps.setString(1, personID); rs = ps.executeQuery(); if (rs.first()) { ret = new HSQLDBPersonDAO(personID, rs); } rs.close(); ps.close(); conn.close(); return ret; } catch (SQLException se) { throw new DAOException("SQLException: " + se.getMessage()); } } protected AddressDAO getAddress(String addressID) throws DAOException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; AddressDAO ret = null; try { conn = getDataSource().getConnection(); ps = conn.prepareStatement(GET_ADDRESS_STATEMENT, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ps.setString(1, addressID); rs = ps.executeQuery(); if (rs.first()) { ret = new HSQLDBAddressDAO(addressID, rs); } rs.close(); ps.close(); conn.close(); return ret; } catch (SQLException se) { throw new DAOException("SQLException: " + se.getMessage()); } } protected CompanyDAO getCompany(String companyID) throws DAOException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; CompanyDAO ret = null; try { conn = getDataSource().getConnection(); ps = conn.prepareStatement(GET_COMPANY_STATEMENT, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); ps.setString(1, companyID); rs = ps.executeQuery(); if (rs.first()) { ret = new HSQLDBCompanyDAO(companyID, rs); } rs.close(); ps.close(); conn.close(); return ret; } catch (SQLException e) { throw new DAOException("SQLException: " + e.getMessage()); } } public Contact getContact(String personID, String addressID, String companyID) throws DAOException { PersonDAO person = getPerson(personID); AddressDAO address = getPerson(addressID); CompanyDAO company = getPerson(companyID); Contact contact = new Contact(person, address, company); } public String addContact(String addStr) throws DAOException { ... } public void modifyContact(String contactID, String modifyStr) throws DAOException; { ... } public void deleteContact(Contact contact) throws DAOException; { ... } }
The DAO Factory
An application or service uses a factory object to select the DAO implementation class at runtime based on configuration data. Configuration data can be stored in a Java constants class, an XML file, a directory service, etc. The following is an example of an abstract factory used as the base class for multiple factories:
public abstract class DAOFactory { // List of DAO types supported by this factory public static final int HSQLDB = 1; public static final int ORACLE = 2; public static final int DB2 = 3; // An abstract method is provided for each DAO that can be // created. Each concrete factory provides an implementation // for these methods. public abstract ContactsDAO getContactsDAO(); public abstract PersonDAO getPersonDAO(); public abstract AddressDAO getAddressDAO(); public abstract CompanyDAO getCompanyDAO(); public static DAOFactory getDAOFactory(int factory) { switch (factory) { case HSQLDB: return new HSQLDBDAOFactory(); case ORACLE: return new OracleDAOFactory(); case DB2: return new DB2DAOFactory(); default: return null; } } }
A Concrete DAO Factory Implementation
A specific DAO factory class is provided for each DAO data source. The following is an example of an hsqldb DAO factory:
public class HSQLDBDAOFactory extends DAOFactory { public static final String DRIVER = "org.hsqldb.jdbcDriver"; public static final String DBURL = "jdbc:hsqldb:hsql://localhost:1476"; // provide the database-specific connection method public static Connection createConnection(String username, String passwd) { Connection conn = null; try { Class.forName(DRIVER).newInstance(); if ((username != null) && (passwd != null)) conn = DriverManager.getConnection(DBURL, username, passwd); else conn = DriverManager.getConnection(DBURL, null); return conn; } catch (Exception e) { // log error } } // protected JNDI-lookup method to be used // for all member DAOs protected Object lookupDAO(String daoClass) { Object dao = null; try { InitialContext ctx = new InitialContext(); String className = (String)ctx.lookup(daoClass); dao = Class.forName(className).newInstance(); } catch (NamingException e) { throw new DAOException(HSQLDBDAOFactory.lookupDAO: " + e.getMessage()); } catch (Exception e) { throw new DAOException("HSQLDBDAOFactory.lookupDAO: " + e.getMessage()); } return dao; } public ContactsDAO getContactsDAO() { return (ContactsDAO)lookupDAO(ConfigFile.CONTACTS_DAO_CLASS); } public PersonDAO getPersonDAO() { return (PersonDAO)lookupDAO(ConfigFile.PERSON_DAO_CLASS); } public AddressDAO getAddressDAO() { return (AddressDAO)lookupDAO(ConfigFile.ADDRESS_DAO_CLASS); } public CompanyDAO getCompanyDAO() { return (CompanyDAO)lookupDAO(ConfigFile.COMPANY_DAO_CLASS); } }
The DAO Client
A client application or service retrieves the desired DAO factory, which is used to retrieve the desired DAO instance. Once the DAO instance is retrieved, the client can call data access methods on the instance without worrying about the underlying data access code:
// retrieve the desired DAO Factory DAOFactory hsqldbFactory = DAOFactory.getDAOFactory(DAOFactory.HSQLDB); // retrieve the DAO ContactsDAO ContactsDAO = hsqldbFactory.getContactsDAO(); // create a new Contact String contactID = contactsDAO.addContact(addStr); // get the new Contact object. Contact contact = contactsDAO.getContact(contactID); // modify the Contact object using SQL statements contactsDAO.modifyContact(contactID, modifyStr); // delete the Contact object contactsDAO.deleteContact(aContact);
Reducing Redundancy Abundancy
A great deal of redundant code is produced when you write a separate class for data source types that have similar characteristics. For instance, JDBC data sources are different from one another mainly in how the SQL is used to access them. In fact, the only distinctions between our hsqldb DAO and a DAO for a different SQL database are the connection strings and the SQL utilized to access the data.
Therefore, an application can decrease redundant code by simply using a generic DAO that abstracts the SQL for different JDBC data sources. Figure 2 shows how an application can employ an XML file to identify the SQL for numerous JDBC data sources.
Figure 2: How an application can employ an XML file to identify the SQL for numerous JDBC data sources.
A Sample XML Configuration
<DAOConfiguration> <DAOStatements database="hsqldb"> <SQLStatement method="GET_PERSON"> <SQLFragment parameterNb="1"> select * from person where personID = ? </SQLFragment> </SQLStatement> ... <DAOStatements database="oracle"> <SQLStatement method="GET_ PERSON "> <SQLFragment parameterNb="1"> select * from person where personID = ? </SQLFragment> </SQLStatement> ... </DAOConfiguration>
hsqldb on NetWare
hsqldb is an open-source relational database engine written in Java, with a JDBC driver, which supports a subset of ANSI-92 SQL. It offers a small, fast database engine that provides in-memory, embedded and server modes. Furthermore, it contains tools such as a minimal Web server and other management tools.
hsqldb is shipped with JBoss, so, since JBoss was already installed on our NetWare machine via a previous article, we already have the core Java classes we need to create and access our sample database. The only additional item necessary is to add a startup .ncf script.
Starting the Database Server
The following script defines the runDBServer.ncf file that will start the hsqldb database server:
java -classpath $CLASSPATH;../lib/ext/hsqldb.jar org.hsqldb.Server
Creating and Populating the Database
The following script creates and populates our Person database:
create table person(name varchar,ssnum varchar,age varchar) insert into person (name,ssnum,age) values('John Doe','123-45-6789','25') insert into person (name,ssnum,age) values('Jane Smith','456-78-9012','32') insert into person (name,ssnum,age) values('Jim Taylor','111-22-3333','54') set autocommit true
The following script creates and populates our Address database:
create table address(street varchar,city varchar,state varchar,zip varchar) insert into address (street,city,state,zip) values('123 Anywhere St.','Hippsville','Utah','84321') insert into address (street,city,state,zip) values('456 Nowhere Ave.','Mytown','Utah','84789') insert into address (street,city,state,zip) values('111 S. 222. E.','Yourtown','Utah','84654') set autocommit true
The following script creates and populates our Company database:
create table company(name varchar,industry varchar) insert into company (name,industry) values('Acme','Textiles') insert into company (name,industry) values('Company B','Manufacturing') set autocommit true
Conclusion
This AppNote introduced how the DAO strategy supports multiple JDBC databases with a single DAO class. It reduces redundant code and makes new database types simpler to include. To support a new database type, you merely add SQL statements for that database type to an XML configuration file, update the environment entry to use the new type, and then re-initialize the DAO.
In the next AppNote in this series, we will discuss how our DAO framework can also be used to simplify and unify access to Web service provider sources.
For Additional Information
For more information about the technologies discussed in this AppNote, refer to the following resources:
For more information about JDBC, visit http://java.sun.com/products/jdbc/learning.html.
For more information about the DAO pattern, visit http://java.sun.com/blueprints/patterns/DAO.html.
For more information about the hsqldb Database Engine, visit http://hsqldb.sourceforge.net/.
For Novell-specific Java programming information, see http://developer.novell.com/ndk.
For information about Zareus and its products for building enterprise applications for the J2EE, Web Services, and xInternet environments, visit http://www.zareus.com.
* Originally published in Novell AppNotes
Disclaimer
The origin of this information may be internal or external to Novell. While Novell makes all reasonable efforts to verify this information, Novell does not make explicit or implied claims to its validity.