I have designed a generic DAO in Java and am having confusion about whether to refactor it to different design or not.

PS Don't say to use already existing 3rd person framework. I know there are some present but I am learning Java and design patterns so I need all the practice I can get.

If there's any confusion then please ask and I'll clarify.

The current Architecture


DAOFactory.java

  • Contains the abstract methods for creation of DAODelete, DAOUpdate and 2 other interfaces to be implemented by concrete implementation classes
  • public Class method for creating the concrete Factories for producing the concrete implementaton of the interfaces mentioned before.

DAORead.java

public abstract <T> T getPojoForPrimarKey(Connection con,
        TableName tableName, String primaryKey) throws SQLException;

Similarly in my other interfaces I have generic methods.

  • Here Connection is passed by the service layer and closed there for transaction management.
  • TableName is a enum that I am using to have a switch case in lower layer of DAO for calling Table Specific functions. Also using an enum is helping me in type checking.

OracleRead.java

@Override
    public <T> T getPojoForPrimarKey(Connection con, TableName tableName,
            String primaryKey) throws SQLException {
        T currentPojo = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            String queryString = OracleSpecifics.queryString(tableName,
                    primaryKey, QueryType.READ);
            ps = con.prepareStatement(queryString);
            rs = ps.executeQuery();
            if (rs.next()) {
                currentPojo = OracleSpecifics.getPojoFromResultSet(tableName, rs);
            }
        } finally {
            DAOFactory.closeAll(ps, rs);
        }
        return currentPojo;
    }

This is how I have implemented the deleteFrom function from the interface in concrete class.

Here OracleSpecifics is the lower layer of DAO that I was talking about. It basically contains switch cases to check for specific Table.

OracleSpecifics.java

// These functions call table-specific functions
    @SuppressWarnings("unchecked")
    static <T> T getPojoFromResultSet(TableName tableName, ResultSet rs)
            throws SQLException {

        switch (tableName) {
        case ASEEM_MASTER:
        case ASEEM_MASTER_TEMP:
            return (T) SpecAseemMaster.getPojo(rs);
        case AUTHENTICATE_TABLE:
            return (T) SpecAseemRole.getPojo(rs);
        default:
            return null;
        }
    }

Here SpecAseemMaster and SpecAseemRole are Table Specific (specifically schema specific) layer producing the required fields

SpecAseemRole.java

public static AuthenticationDetails getPojo(ResultSet rs)
            throws SQLException {
        AuthenticationDetails cur = new AuthenticationDetails();
        cur.setUserName(rs.getString(1));
        cur.setPassWord(rs.getString(2));
        cur.setRole(rs.getString(3));
        return cur;
    }

In service layer

The type by which the DAO's method is called is the Pojo representing the schema's data fields. e.g.

myFactory.getDAORead().<AuthenticationDetails> getPojoForPrimarKey(con, CUR_TABLE, primaryKey);

Here AuthenticationDetails is my Pojo aka DTO for communicating between my Controller and service layer (in Model).


Please Note that I am giving just a overview in the simplest way. There are 4 interfaces in the abstract DAOs containing total of 8 abstract methods implemented by 4 concrete classes. All these generic classes depend on a lower layer in DAO for getting specific queries, PreparedStatements etc. which further calls functions in auxiliary utility class's functions depending on the tableName passed down this hierarchy. The bottom layer is schema-specific.

My Confusion about refactoring

  • I am satisfied with the upper layers of DAO but I think that at the the lower level specifically at the OracleSpecific the things which should have strong coupling has weak coupling. e.g. there are different methods for getting PreparedStatement for insert and update. Each of these in turn have switch case for calling the functions in lower utility class.
  • Although the schema-specific methods are tied together in different utility classes the method call themselves in OracleSpecifics.java do not have any coupling.
  • I am thinking whether I should change the enum TableName to contain a specific state. The state I am thinking for the schema-specific lowest level utility classes.
  • These specific states contained in enums can be used to change the state of DAO and the DAO can then call the specific functions based on an interface implemented by all such states. Thus depending on the state the behavior can change automatically.

  • Is this design decision correct or would it be meaningless?

  • Any other thing that I might have overlooked?
  • Any thoughts about the design itself?
  • Would I lose type safety introduced by generics due to this change?
  • When you see a switch/case statement, think "opportunity for polymorphism". An enum on table names will not scale very well. This is an opportunity for a register where you register the tables and any specific classes/closures that need to be called for that table. Also try to keep the registration to a minimum. You should be able to handle most tables in a very generic manner and would benefit greatly from using run time type information (Reflection, RTTI or ... in other languages, don't know Java so don't know whether it has this or what it is called). – Marjan Venema Dec 1 '13 at 10:14
  • 1
    Scott Ambler's paper on mapping objects is a great read for anyone trying to design a generic data access layer. As is his paper The Design of a Robust Persistence Framework for Relational Databases Available as a pdf from that page. – Marjan Venema Dec 1 '13 at 10:17
  • @MarjanVenema Thanks, I'll take a look. – Aseem Bansal Dec 1 '13 at 10:44
up vote 3 down vote accepted
+50

The first question you need to answer is whether you intend to create a truly generic data access framework, or if you want it tailored to the particular project you're working on.

A generic framework has the benefit of being portable and fairly loosely coupled to the underlying DB + schema, but at the cost of pushing schema level information further up in the framework. The more generic you make the framework, the more of this knowledge you push up.

A tailored framework alleviates the higher levels of the application from worrying about the schema or DB details, but at the cost of cost of being less re-usable in other environments. As a corollary, the more specific you make the framework, the less easy it is to extend elsewhere.

If you're not sure on which of those to pick, the generic framework makes sense if you suspect there will be changes at the DB layer or the schema. Likewise, if those are pretty well locked down then the tailored framework is the better approach. Ultimately, you're optimizing the system to more easily handle where you think the change will occur.


As to whether or not you should refactor - the answer is No unless you're encountering a specific problem.

Worrying about the degree of coupling is nice, I suppose, but doesn't necessarily address a specific problem that you may be seeing. Refactoring based upon presumed issues within coupling is actually more likely to create a problem for you than just leaving it alone.

Likewise with expressing the state. If the current mechanism of an enum is sufficient and doesn't present an apparent problem then don't change what you've got.


Overall, it sounds like you've designed a reasonably robust data access method for the domain you're working worth. Your question doesn't list any specific issues that are holding back your development, so I'd call it good and move to the next stage of development.

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.