J2EE developers use the data access object (DAO) design mode to separate the underlying data access logic from the high-level business logic, so that the DAO mode can focus more on writing data access code.
Let's first review the DAO design mode and data access objects.
Dao Basics
The DAO mode is one of the standard J2EE design patterns. developers use this mode to separate the underlying data access operations from the upper-layer business logic. A typical DAO implementation has the following components:
1. A Dao factory class;
2. A Dao interface;
3. A specific class implementing the DaO interface;
4. Data Transmission object (sometimes called value object ).
The specific DAO class contains the logic for accessing data from a specific data source. In the following section, you will learn the technology to design and implement data access objects.
Transaction Division:
One important thing to remember about Dao is that they are transactional objects. Each operation executed by Dao (such as creating, updating, or deleting data) is associated with a transaction. Similarly, the concept of Transaction Division is particularly important.
Transaction Division is the way in which transactions are defined. The J2EE specification describes two transaction partitioning modes: programmatic and declarative. The following table describes the splitting of these two modes:
Declarative Transaction Division programming Transaction Division
Programmers use the deployment descriptor of EJB to declare transaction attributes. Programmers are responsible for compiling transaction logic code.
The runtime environment (EJB container) uses these attributes to automatically manage transactions. An application uses an API to control transactions.
I will focus my attention on the division of programming transactions.
As described earlier, daos is a transaction object. A typical Dao performs transactional operations such as create, update, and delete. When designing a Dao, you must first ask yourself the following questions:
1. How to start a transaction?
2. How will the transaction end?
3. Which object will assume the responsibility for starting a transaction?
4. Which object will assume the responsibility for ending a transaction?
5. Should Dao start and end the transaction?
6. Do applications need to cross-Access Multiple daos?
7. Does a transaction contain one or more Dao statements?
8. Does a DaO contain other methods in Dao?
Answering these questions will help you select the best transaction partitioning strategy for Dao objects. There are two main strategies for dividing transactions in ADO. One method is to use DAO to take responsibility for Transaction Division, and the other is to extend the transaction into the method for calling Dao objects. If you choose the former, you will embed the transaction code in the DAO class. If you select the latter, the transaction code will be written outside the DAO class. We will use simple code instances to better understand how these two methods work.
Instance 1 shows a Dao with two data operations: create and update ):
Public void createwarehouseprofile (whprofile profile );
Public void updatewarehousestatus (whidentifier ID, statusinfo status );
Instance 2 shows a simple transaction. The transaction division code is outside the DAO class. Note: In this example, the caller combines multiple DOA operations into this transaction.
TX. Begin (); // start the transaction
Dao. createwarehouseprofile (profile );
Dao. updatewarehousestatus (id1, status1 );
Dao. updatewarehousestatus (Id2, status2 );
TX. Commit (); // end the transaction
This transaction Division policy is especially important for applications that access multiple Dao in a single transaction.
You can use JDBC APIs or Java transaction APIs (JTA) to divide transactions. JDBC Transaction Division is simpler than JTA Transaction Division, but JTA provides better flexibility. In the following section, we will further look at the Transaction Division mechanism.
Use JDBC Transaction Division
JDBC transactions are controlled using connection objects. The JDBC connection interface (Java. SQL. Connection) provides two transaction modes: Automatic commit and manual commit. Java. SQL. Connection provides the following methods to control transactions:
. Public void setautocommit (Boolean)
. Public Boolean getautocommit ()
. Public void commit ()
. Public void rollback ()
Instance 3 shows how to use the jdbc api to divide transactions:
Import java. SQL .*;
Import javax. SQL .*;
//...
Datasource DS = obtaindatasource ();
Connection conn = Ds. getconnection ();
Conn. setautocommit (false );
//...
Pstmt = conn. preparestatement ("update movies ...");
Pstmt. setstring (1, "The Great Escape ");
Pstmt.exe cuteupdate ();
//...
Conn. Commit ();
//...
Using JDBC Transaction Division, you can combine multiple SQL statements into a single transaction. One of the disadvantages of JDBC transactions is that the transaction scope is limited to a single database connection. A jdbc transaction cannot span multiple databases. Next, we will see how to use JTA for Transaction Division. JTA is not as widely known as JDBC, so I will give a brief introduction to JTA first.
JTA Overview
Java transaction API (JTA Java transaction API) and its fellow Java Transaction Service (JTs Java Transaction Service) provide distributed transaction services for the J2EE platform. A Distributed Transaction (Distributed Transaction) includes one transaction manager and one or more resource managers ). A resource manager is a type of persistent data storage. The transaction manager is responsible for the communication between all transaction participants. The lower station shows the relationship between the Transaction Manager and resource management.
JTA transactions are more powerful than JDBC transactions. A jta transaction can have multiple participants, while a JDBC transaction is limited to a single database connection. Any of the following Java platform components can be involved in a JTA transaction:
. JDBC connection
. JDO persistencemanager object
. JMS queue
. JMS topic
. Enterprise JavaBeans (EJB)
. A resource distributor compiled using J2EE Connector Architecture specifications.
Use JTA Transaction Division
To use JTA to divide a transaction, the application calls the method in the javax. transaction. usertransaction interface. Example 4 shows a typical usetransaction object for JNDI search.
Import javax. transaction .*;
Import javax. Naming .*;
//...
Initialcontext CTX = new initialcontext ();
Object txobj = CTX. Lookup ("Java: COMP/usertransaction ");
Usertransaction utx = (usertransaction) txobj;
After the application has a reference to the usertransaction object, it can start the transaction as in Example 5.
Utx. Begin ();
//...
Datasource DS = obtainxadatasource ();
Connection conn = Ds. getconnection ();
Pstmt = conn. preparestatement ("update movies ...");
Pstmt. setstring (1, "Spinal Tap ");
Pstmt.exe cuteupdate ();
//...
Utx. Commit ();
//...
When an application calls commit (), the Transaction Manager uses two Commit Protocols to end the transaction.
JTA Transaction Control Method
The. javax. transaction. usertransaction interface provides the following transaction control methods:
. Public void begin ()
. Public void commit ()
. Public void rollback ()
. Public void getstatus ()
. Public void setrollbackonly ()
. Public void settransactiontimeout (INT)
The application calls begin () to start the transaction. You can call commit () or rollback () to end the transaction.
Use JTA and JDBC
Developers often use JDBC for underlying data operations in Dao classes. If you plan to use JTA to divide transactions, you will need a driver that implements javax. SQL. xadatasource, javax. SQL. xaconnection, and javax. SQL. xaresource interface JDBC. Drivers that implement these interfaces will be able to participate in JTA transactions. A xadatasource object is the factory of A xaconnection object. Xaconnections is a connection that participates in a JTA transaction.
You need to use the application server management tool to create the xadatasource object. For special commands, see the application server documentation and JDBC driver documentation.
The J2EE application uses JNDI to find the data source. Once the application has a reference to the data source object, javax. SQL. datasource. getconnection () is called to obtain the database connection.
XA connections are different from non-Xa connections. Remember that Xa connections are participants in a JTA transaction. This means that the Xa connection does not support the automatic submission feature of JDBC. That is to say, the application does not have to call java. SQL. Connection. Commit () or Java. SQL. Connection. rollback () on the Xa connection (). On the contrary, the application should use usertransaction. Begin (), usertransaction. Commit (), and usertransaction. rollback ().
Best Choice
We have discussed how JDBC and JTA divide transactions. Each method has its advantages. You need to decide to select the most appropriate method for your application.
In many recent projects of our team that have divided transactions, we use JDBC APIs to create Dao classes. This DAO class is summarized as follows:
. The transaction division code is embedded into the DAO class.
. DAO class uses jdbc api for Transaction Division
The caller has no way to divide the transaction.
. The transaction scope is limited to a single JDBC connection
JDBC transactions are not always effective for complex enterprise applications. If your transaction spans multiple Dao objects or
Multiple databases, the following implementation policies may be more appropriate. :
. Use JTA to divide transactions.
. The transaction division code is separated by Dao
The caller is responsible for dividing transactions.
. Dao participates in a global transaction
The JDBC Method is attractive because of its simplicity. The JTA method provides more flexibility. The implementation you choose depends on the specific needs of your application.
Logging and Dao
A good DAO implementation class will use logging to capture details about its behavior at runtime. You can choose to record exceptions, configuration information, connection status, JDBC driver metadata or query parameters. Logs are useful for the entire development phase. I often check logs of applications during development, testing, and products.
In this section, we will show an example of how to combine Jakarta commaons logging into a Dao. Before we start, let's review some basic knowledge.
Select a logstore
The basic log formats used by many developers are system. Out. println and system. Err. println. println. This form is quick and convenient, but they cannot provide the capability of a complete log system. The following table lists the log class libraries on the Java platform:
Is the log class library open-source? URL
Java. util. Logging non http://java.sun.com/j2ee
Jakarta log4j is http://hajarta.apache.org/log4j/
Jakarta commons logging is http:/jakarta.apache.org/commons/logging.html
Java. util. logging is a standard API on the j2se1.4 platform. However, most developers believe that Jakarta log4j provides greater functionality and flexibility. One of the advantages of log4j over java. util. Logging is its support for j2se1.3 and j2se1.4 platforms.
Jakarta commons logging can be used to work with Java. util. Loggin or Jakarta log4j. Commons logging is an extraction layer that separates your applications from log implementations. With commons logging, you can change a configuration file to exchange data with the following log implementations. Commons logging is used in Jakarta struts1.1 and Jakarta httpclient2.0.
A log example
Example 7 shows how to use Jakarta commons logging in a DOA class
Import org. Apache. commons. Logging .*;
Class documentdaoimpl implements documentdao
{
Static private final log = logfactory. getlog (documentdaoimpl. Class );
Public void deletedocument (string ID)
{
//...
Log. debug ("deleting document:" + id );
//...
Try
{
//... Data operations...
}
Catch (someexception ex)
{
Log. Error ("unable to delete Document", ex );
//... Handle the exception...
}
}
}
Logs are the basic part of application evaluation. If you encounter a failure in a Dao, logs often provide the best information for understanding what errors occur. Integrate logs into your Dao to ensure effective means of debugging and solving problems.
Exception Handling in Dao
We have read the Transaction Division and logging, and have a deep understanding of how they are applied to data access objects. The third part is about exception handling. The following simple Exception Handling Guidelines make it easier to use your Dao, which is more robust and maintainability.
When implementing the DAO mode, consider the following questions:
Will the method in the public interface of Dao throw an exception to be checked?
. If yes, what kind of check exceptions will be thrown?
. How can I handle exceptions in the DAO implementation class.
While working in DAO mode, our team developed a set of guidelines for exception handling. The following guidelines will greatly improve your Dao:
The. DAO method should throw a meaningful exception.
The. DAO method should not throw a java. Lang. Exception exception. Java. Lang. Exception is too general to contain all information about potential problems.
The. DAO method should not throw a java. SQL. sqlexception exception. Sqlexception is an underlying JDBC exception. Dao applications try to encapsulate JDBC exceptions instead of leaving JDBC exceptions to other parts of the application.
The method in the DaO interface should only throw the checking exception that the caller expects to handle. If the caller cannot use appropriate methods to handle exceptions, the exam filters will throw a non-checking (Run-Time) exception.
. If your data access code captures an exception, you cannot ignore it. Ignore the DaO that captures exceptions is very processing.
. Use the exception chain to pass the underlying exception to a high-level processor.
. Filter defines a standard Dao exception class. The Spring framework provides a set of excellent predefined Dao exception classes.
Check resources to view more details about exception and exception handling technologies.
Implementation example: moviedao
Movedao demonstrates all the technologies discussed in this article, including transaction division, logging, and exception handling. You will find the moviedao source code in the resources section. It is divided into the following three packages:
. Daoexamples. Exception
. Daoexamples. Move
. Daoexamples. moviedemo
The DAO mode consists of the following classes and interfaces:
. Daoexamples. Movie. moviedaofactory
. Daoexamples. Movie. moviedao
. Daoexamples. Movie. moviedaoimpl
. Daoexamples. Movie. moviedaoimpljta
. Daoexamples. Movie. Movie
. Daoexamples. Movie. movieimple
. Daoexamples. Movie. movienotfoundexception
. Daoexamples. Movie. movieutil
The moviedao interface defines Dao data operations. This interface has the following five methods:
. Public movie findmoviebyid (string ID)
. Public java. util. Collection findmoviesbyyear (string year)
. Public void deletemovie (string ID)
. Public movie createmovie (string rating, string year, String title)
. Public void updatemovie (string ID, string rating, string year, String title)
The daoexamples. Movie package contains the implementation of two moviedao interfaces. Each implementation uses the same transaction division method, as shown in the following table:
Moviedaoimpl moviedaoimpljta
Has the moviedao interface been implemented? Yes
Obtain datasource through JNDI? Yes
Obtain the java. SQL. connection object from a datasource? Yes
Does Dao define internal transactions? Yes No
Are you using JDBC transactions? Yes No
Is an Xa datasource used? No Yes
Share JTA transactions? No Yes
Moviedao Demo Application
This demo application is a servlet class called daoexamples. moviedemo. demoservlet. demoservlet. It uses movie Dao to query and update movie data in a table.
This servlet demonstrates combining JTA-aware moviedao and Java Message Service into a single transaction, as shown in Example 8:
Usertransaction utx = movieutil. getusertransaction ();
Utx. Begin ();
Batman = Dao. createmovie ("r ",
"2008 ",
"Batman reloaded ");
Publisher = new messagepublisher ();
Publisher. publishtextmessage ("I'll be back ");
Dao. updatemovie (topgun. GETID (),
"PG-13 ",
Topgun. getreleaseyear (),
Topgun. gettitle ());
Dao. deletemovie (legallyblonde. GETID ());
Utx. Commit ();
To run this sample application, configure an Xa data source and a non-Xa data source on your application server. Then deploy the daoexamples. Ear file. This application will run on any J2EE-compatible application server.
Dao solves the problem of difficult JSP page maintenance, so that JSP cannot use Java. SQL. * All Database code is implemented using preaprestatemnet, hirnate, and ibait.
Dao is a J2EE database operation,
It encapsulates all database operations of a project.
All
Customer-VO----DAO
The VO object and the fields in the table are completely
The list is returned for all queries, and iterator is required for output.
Import java. util .*;
Import CN. mldn. lxh. VO .*;
// Specifies all the methods used to operate the person table in this project.
Public interface persondao
{
// Add operation
Public void insert (person) throws exception;
// Modify the operation
Public void Update (person) throws exception;
// Delete operation
Public void Delete (string ID) throws exception;
// Query by ID
Public Person querybyid (string ID) throws exception;
// Query all
Public list queryall () throws exception;
// Fuzzy search
Public list querybylike (string Cond) throws exception;
}
Package CN. mldn. lxh. Dao. impl;
Import java. SQL .*;
Import java. util .*;
Import CN. mldn. lxh. VO .*;
Import CN. mldn. lxh. DBC .*;
Import CN. mldn. lxh. Dao .*;
// This class requires specific database operations and jdb code
Public class persondaoimpl implements persondao
{
// Add operation
Public void insert (person) throws exception
{
String SQL = "insert into person (ID, name, password, age, email) values (?,?,?,?,?) ";
Preparedstatement pstmt = NULL;
Databaseconnection DBC = NULL;
// The following are specific database operations.
Try
{
// Connect to the database
DBC = new databaseconnection ();
Pstmt = DBC. getconnection (). preparestatement (SQL );
Pstmt. setstring (1, person. GETID ());
Pstmt. setstring (2, person. getname ());
Pstmt. setstring (3, person. GetPassword ());
Pstmt. setint (4, person. getage ());
Pstmt. setstring (5, person. getemail ());
// Update the database
Pstmt.exe cuteupdate ();
Pstmt. Close ();
}
Catch (exception E)
{
Throw new exception ("Operation exception ");
}
Finally
{
// Close the database connection
DBC. Close ();
}
}
// Modify the operation
Public void Update (person) throws exception
{
String SQL = "Update person set name = ?, Password = ?, Age = ?, Email =? Where id =? ";
Preparedstatement pstmt = NULL;
Databaseconnection DBC = NULL;
// The following are specific database operations.
Try
{
// Connect to the database
DBC = new databaseconnection ();
Pstmt = DBC. getconnection (). preparestatement (SQL );
Pstmt. setstring (1, person. getname ());
Pstmt. setstring (2, person. GetPassword ());
Pstmt. setint (3, person. getage ());
Pstmt. setstring (4, person. getemail ());
Pstmt. setstring (5, person. GETID ());
// Update the database
Pstmt.exe cuteupdate ();
Pstmt. Close ();
}
Catch (exception E)
{
Throw new exception ("Operation exception ");
}
Finally
{
// Close the database connection
DBC. Close ();
}
}
// Delete operation
Public void Delete (string ID) throws exception
{
String SQL = "delete from person where id =? ";
Preparedstatement pstmt = NULL;
Databaseconnection DBC = NULL;
// The following are specific database operations.
Try
{
// Connect to the database
DBC = new databaseconnection ();
Pstmt = DBC. getconnection (). preparestatement (SQL );
Pstmt. setstring (1, ID );
// Update the database
Pstmt.exe cuteupdate ();
Pstmt. Close ();
}
Catch (exception E)
{
Throw new exception ("Operation exception ");
}
Finally
{
// Close the database connection
DBC. Close ();
}
}
// Query by ID
Public Person querybyid (string ID) throws exception
{
Person = NULL;
String SQL = "select ID, name, password, age, email from person where id =? ";
Preparedstatement pstmt = NULL;
Databaseconnection DBC = NULL;
// The following are specific database operations.
Try
{
// Connect to the database
DBC = new databaseconnection ();
Pstmt = DBC. getconnection (). preparestatement (SQL );
Pstmt. setstring (1, ID );
// Query the database
Resultset rs = pstmt.exe cutequery ();
If (Rs. Next ())
{
// Query the content, and assign the queried content to the person object.
Person = new person ();
Person. setid (Rs. getstring (1 ));
Person. setname (Rs. getstring (2 ));
Person. setpassword (Rs. getstring (3 ));
Person. setage (Rs. getint (4 ));
Person. setemail (Rs. getstring (5 ));
}
Rs. Close ();
Pstmt. Close ();
}
Catch (exception E)
{
Throw new exception ("Operation exception ");
}
Finally
{
// Close the database connection
DBC. Close ();
}
Return person;
}
// Query all
Public list queryall () throws exception
{
List all = new arraylist ();
String SQL = "select ID, name, password, age, email from person ";
Preparedstatement pstmt = NULL;
Databaseconnection DBC = NULL;
// The following are specific database operations.
Try
{
// Connect to the database
DBC = new databaseconnection ();
Pstmt = DBC. getconnection (). preparestatement (SQL );
// Query the database
Resultset rs = pstmt.exe cutequery ();
While (Rs. Next ())
{
// Query the content, and assign the queried content to the person object.
Person = new person ();
Person. setid (Rs. getstring (1 ));
Person. setname (Rs. getstring (2 ));
Person. setpassword (Rs. getstring (3 ));
Person. setage (Rs. getint (4 ));
Person. setemail (Rs. getstring (5 ));
// Add the queried data to the list object
All. Add (person );
}
Rs. Close ();
Pstmt. Close ();
}
Catch (exception E)
{
Throw new exception ("Operation exception ");
}
Finally
{
// Close the database connection
DBC. Close ();
}
Return all;
}
// Fuzzy search
Public list querybylike (string Cond) throws exception
{
List all = new arraylist ();
String SQL = "select ID, name, password, age, email from person where name like? Or email like? ";
Preparedstatement pstmt = NULL;
Databaseconnection DBC = NULL;
// The following are specific database operations.
Try
{
// Connect to the database
DBC = new databaseconnection ();
Pstmt = DBC. getconnection (). preparestatement (SQL );
// Set Fuzzy search conditions
Pstmt. setstring (1, "%" + cond + "% ");
Pstmt. setstring (2, "%" + cond + "% ");
// Query the database
Resultset rs = pstmt.exe cutequery ();
While (Rs. Next ())
{
// Query the content, and assign the queried content to the person object.
Person = new person ();
Person. setid (Rs. getstring (1 ));
Person. setname (Rs. getstring (2 ));
Person. setpassword (Rs. getstring (3 ));
Person. setage (Rs. getint (4 ));
Person. setemail (Rs. getstring (5 ));
// Add the queried data to the list object
All. Add (person );
}
Rs. Close ();
Pstmt. Close ();
}
Catch (exception E)
{
Throw new exception ("Operation exception ");
}
Finally
{
// Close the database connection
DBC. Close ();
}
Return all;
}
};
Package CN. mldn. lxh. factory;
Import CN. mldn. lxh. Dao .*;
Import CN. mldn. lxh. Dao. impl .*;
Public class daofactory
{
Public static persondao getpersondaoinstance ()
{
Return new persondaoimpl ();
}
};