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 ofDAODelete
,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, PreparedStatement
s 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 gettingPreparedStatement
for insert and update. Each of these in turn haveswitch
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 inOracleSpecifics.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?