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
abstractmethods for creation ofDAODelete,DAOUpdateand 2 other interfaces to be implemented by concrete implementation classes publicClass 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
Connectionis passed by the service layer and closed there for transaction management. TableNameis 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
OracleSpecificthe things which should have strong coupling has weak coupling. e.g. there are different methods for gettingPreparedStatementfor insert and update. Each of these in turn haveswitchcase for calling the functions in lower utility class. - Although the
schema-specificmethods are tied together in different utility classes the method call themselves inOracleSpecifics.javado not have any coupling. - I am thinking whether I should change the enum
TableNameto contain a specific state. The state I am thinking for the schema-specific lowest level utility classes. These
specific statescontained 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?