JDTO - Java DAO Transaction Framework

The main purpose of this jdto Framework is to have some generic classes, which are reusable to any project where plain DAO's are used.

See:
          Description

Packages
com.framework.jdto.exceptions  
com.framework.jdto.finder  
com.framework.jdto.jdbc.wrapper  
com.framework.jdto.properties  
com.framework.jdto.transaction  

 

The main purpose of this jdto Framework is to have some generic classes, which are reusable to any project where plain DAO's are used.

Purpose

In general, developers forget to close or release the database connections while writing DAO's, which leads to problems. The main purpose of this Framework is to have some generic classes where...

  1. The user or developer shouldn't  worry about getting or releasing the Database Connections in each and every method of the DAO's that are written for any project.

  2. Providing an optional Transaction Support for DAO's written by the developer.

Overview

The framework contains wrapper classes for java.sql.PreparedStatement and java.sql.Statement. These Wrapper classes are PreparedStatementWrapper and StatementWrapper. These classes should be used by the developer for writing the DAO's. These Wrapper Classes takes care of getting and releasing/closing the Database Connections.

0. Its important to keep the log4j.properties in the classpath as the framework uses log4j to log any errors.

1. Setting up the DataSource

One most important aspect of this framework is setting up the datasource.

A. The framework uses the ServiceFinder Class to locate the datasource. This class Follows the ServiceLocator Design Pattern, which is basically a SingleTon Class. it Locates the DataSource provided, its DataSourceJNDIName, InitialContextFactory and ProviderURL is set in jdto.properties. User needs to create a jdto.properties file and make the properties file available in the classpath for framework to use. Once these values are set, all the DAO's and the TransactionContextManager takes care of locating the DataSource.

Sample jdto.properties

DataSourceJNDIName = mydatasource

InitialContextFactory = weblogic.jndi.T3InitialContextFactory    //for weblogic server

ProviderURL = t3://localhost:7001 //for weblogic server

B. The framework also allows the developer to set a datasource if he already has one. This enables the developers to use this framework with datasources like apache dbcp datasource. The developer should call the setDataSource method of ServiceFinder class to set the datasource. In this case the it is not required to use the jdto.properties file to set the datasource.

 

2. Using PreparedStatementWrapper and StatementWrapper Classes in your DAO's

Database Insert Query - executeUpdate():

public int saveAddress(AddressVO address) {
    int result1=0;
    String sql = "insert into ADDRESS(ADDRESS_ID, NAME, STREET, CITY, PROVINCE)values(?,?,?,?,?)";
    PreparedStatementWrapper stmt = new PreparedStatementWrapper();
    try {
        stmt.setSql(sql);
        stmt.setString(1, address.getAddressID());
        stmt.setString(2, address.getName());
        stmt.setString(3, address.getStreet());
        stmt.setString(4, address.getCity());
        stmt.setString(5, address.getProvince());
        result1 = stmt.executeUpdate();
    } catch (DAOException daoe) {
    _log.error("Exception in saveAddress::AddressDAO", daoe);
    }
    return result1;
}
 

Database Update Query - executeUpdate():

public int updateAddress(AddressVO address) {
    int result1=0;
    String sql = "UPDATE ADDRESS SET NAME=?, STREET=?,CITY=?,PROVINCE=? WHERE ADDRESS_ID=?";
    PreparedStatementWrapper stmt = new PreparedStatementWrapper();
    try {
        stmt.setSql(sql);
        stmt.setString(1, address.getName());
        stmt.setString(2, address.getStreet());
        stmt.setString(3, address.getCity());
        stmt.setString(4, address.getProvince());
        stmt.setString(5, address.getAddressID());
        result1 = stmt.executeUpdate();
    } catch (DAOException daoe) {
        _log.error("Exception in updateAddress of AddressDAO", daoe);
    }
        return result1;
}

Database Delete Query - execute():

 public boolean deleteAddress(AddressVO address) {
    boolean result=false;
    String sql = "DELETE FROM ADDRESS WHERE ADDRESS_ID=?";
    PreparedStatementWrapper stmt = new PreparedStatementWrapper();
    try {
        stmt.setSql(sql);
        stmt.setString(1, address.getAddressID());
        result = stmt.execute();
    } catch (DAOException daoe) {
        _log.error("Exception in updateAddress of AddressDAO", daoe);
    }
        return result;
}

Database Select Query - executeQuery(RowMapper rm):

This is a different case as the executeQuery method takes RowMapper object as an argument and returns an java.lang.Object type of Object. So all your DAO should implement RowMapper interface in order to call the executeQuery() method of PreparedStatementWrapper or  StatementWrapper classes and override the mapRow(ResultSet addressRsltSet) method of the RowMapper interface.

See the getAddress() method of AddressDAO.java provided as a sample in this package. The getAddress() method calls executeQuery(this); on the PreparedStatementWrapper Object in AddressDAO class. The executeQuery(RowMapper) method of PreparedStatementWrapper Object in turn calls the mapRow(ResultSet addressRsltSet) of the AddressDAO class by passing the java.sql.ResultSet Object. The mapRow(ResultSet addressRsltSet) of AddressDAO maps the ResultSet to the required Object and returns an java.lang.Object type of Object, which should be type casted in the calling method (i.e.,getAddress()).

if there is a need for more than one select queries in your DAO then see the getAddressByID(AddressVO address) method of AddressDAO class, which uses a new class called AddressByID implements RowMapper to pass as an argument to executeQuery() method of PreparedStatementWrapper Class.

AddressVO address = (AddressVO) addressPrepStmt.executeQuery(new AddressByID());

The class AddressByID is written in the same AddressDAO.java

class AddressByID implements RowMapper {

    public Object mapRow(ResultSet addressRsltSet) throws SQLException {
        AddressVO addressVO = new AddressVO();
        while (addressRsltSet.next()) {
            addressVO.setAddressID(addressRsltSet.getString("ADDRESS_ID"));
            addressVO.setName(addressRsltSet.getString("NAME"));
            addressVO.setStreet(addressRsltSet.getString("STREET"));
            addressVO.setCity(addressRsltSet.getString("CITY"));
            addressVO.setProvince(addressRsltSet.getString("PROVINCE"));
        }
        return addressVO;
    }
}
 

Similarly you can use the StatementWrapper class....

3. Using Transactions with your DAO's

TransactionContextManager class is mainly responsible for managing the transactions. it is responsible for beginning the transaction and ending the transaction based on the current transaction status. User needs to call the beginTransaction() method to begin the transaction and should end the transaction by calling the endTransaction() method. All the DAO's which are executed after the beginTransaction() method and before the endTransaction() method by default runs in a transaction, provided the DAO's use the PreparedStatementWrapper or StatementWrapper classes to interact with the database (i.e., Insert, Update, Delete and Select Queries) instead of the actual java.sql.PreparedStatement and java.sql.Statement class.

Currently TransactionContextManager doesn't support Nested Transactions. it throws a NotSupportedException if the user tries to get a Nested Transaction.

TransactionContextManager class ends the transaction by calling commit or rollback, when the user calls the endTransaction() method based on the current Transaction Status.

PreparedStatementWrapper and StatementWrapper objects throws DAOException whenever an SQL Query Fails or a database error occurs. If this happens the TransactionContextManager rollback's the Transaction.

  • Transaction can be started By calling TransactionContextManager.beginTransaction() method

  • If the user starts the transaction by calling (TransactionContextManager.beginTransaction()), then all the DAO's which are executed after that will by default runs in a Transaction.

    User can call the TransactionContextManager.endTransaction() to end the transaction.

    NOTE: User / Developer should not used commit or rollback on the actual connection when a Transaction is running

    How To Use This Framework

    By calling TransactionContextManager.beginTransaction() method

    Example using TransactionContextManager's beginTransaction() and endTransaction().

    TransactionContextManager.beginTransaction();
    //DAO - 1
    MemberDAO memberDAO = new MemberDAO();
    memberDAO.saveMember(new MemberVO("1","Cindy","Lake Hill","Halifax","5R6T8O"));

    //DAO - 2
    AddressDAO addressDAO = new AddressDAO();
    addressDAO.saveAddress(new AddressVO("1","Cindy","Lake Hill","Halifax","5R6T8O"));

    //DAO - 3
    //create some LoanDAO and save/update the Details of LoanDAO
    //LoanDAO.saveLoanDetails(MemberVO);

    //....... DAO - 4 .... and so on...

    TransactionContextManager.endTransaction();

    saveMember(MemberVO member) Method

    public int saveMember(MemberVO member) {

    int result1=0;
    String sql = "insert into ADDRESS(ADDRESS_ID, NAME, STREET, CITY, PROVINCE)values(?,?,?,?,?)";
    PreparedStatementWrapper stmt = new PreparedStatementWrapper();
    try {
        stmt.setSql(sql);//stmt.setSql(sql, true); will avoid running the current sql statement in the transaction
        stmt.setString(1, member.getMemberID());
        stmt.setString(2, member.getName());
        stmt.setString(3, member.getStreet());
        stmt.setString(4, member.getCity());
        stmt.setString(5, member.getProvince());
        result1 = stmt.executeUpdate();
    } catch (DAOException daoe) {
        _log.error("Exception in saveMember::MemberDAO", daoe);
    }
        return result1;
    }

    saveAddress(MemberVO member) Method

     public int saveAddress(AddressVO address) {

    int result1=0;
    String sql = "insert into ADDRESS(ADDRESS_ID, NAME, STREET, CITY, PROVINCE)values(?,?,?,?,?)";
    PreparedStatementWrapper stmt = new PreparedStatementWrapper();
    try {
        stmt.setSql(sql);//stmt.setSql(sql, true); will avoid running the current sql statement in the transaction
        stmt.setString(1, address.getAddressID());
        stmt.setString(2, address.getName());
        stmt.setString(3, address.getStreet());
        stmt.setString(4, address.getCity());
        stmt.setString(5, address.getProvince());
        result1 = stmt.executeUpdate();
    } catch (DAOException daoe) {
        _log.error("Exception in saveAddress::AddressDAO", daoe);
    }
    return result1;
    }
     

    Note: If this framework is used for J2EE 1.3 and above for IBM Websphere the DataSource must be configured as an UnSharable Resource.

    =================================================================================

    Developed by: Satish Madhavarapu (mbvvsatish)