Skip to main content
Code Review

Return to Question

replaced http://programmers.stackexchange.com/ with https://softwareengineering.stackexchange.com/
Source Link

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO Thoughts on refactoring a generic DAO. Then I asked on codereview meta here and decided to put up the code here.

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO. Then I asked on codereview meta here and decided to put up the code here.

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO. Then I asked on codereview meta here and decided to put up the code here.

replaced http://meta.codereview.stackexchange.com/ with https://codereview.meta.stackexchange.com/
Source Link

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO. Then I asked on codereview meta here here and decided to put up the code here.

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO. Then I asked on codereview meta here and decided to put up the code here.

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO. Then I asked on codereview meta here and decided to put up the code here.

Question Protected by Jamal
Source Link
Aseem Bansal
  • 2.3k
  • 3
  • 22
  • 37

Generic DAO written in Java

I initially thought that this was just an architecture issue so I placed it on programmers as Thoughts on refactoring a generic DAO. Then I asked on codereview meta here and decided to put up the code here.

  • Here's a github link if someone wants to see there.
  • There are four DAOs - DAODelete, DAOUpdate, DAORead, DAOInsert but I am just putting DAORead and its implementation here so that it is easier to review.
  • I have trimmed the code a lot so if something is amiss please tell me and I'll correct it. Otherwise you can see github link as it has complete code(kinda).

My architecture is like this

  • Abstraction layer of DAO at top
  • DataBase specific implementation of DAO BUT Table-independent
  • Table dependency is passed down to a lower utility layer
  • A Enum is passed down to the lower utility layer by which it gives table specific results
  • Table-specific utility classes for the lower utility layer mentioned in the previous point.
  • There is a Student pojo that I am omitting because well it is just a pojo. private variables and all. Just note that enrollmentDate is of type java.sql.Date

My concern

  • I am satisfied with the upper layer of DAO but I think that at the the lower level specifically at the OracleSpecific.java the things which should have strong coupling have weak coupling. e.g. there are different methods for getting pojo from resultset or getting primary key. 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?


Enums Used

QueryType.java

package aseemEnums;
public enum QueryType {
 READ, DELETE;
}

Databases.java

package aseemEnums;
public enum Databases {
 Oracle;
}

TableName.java

package aseemEnums;
public enum TableName {
 STUDENT_TABLE("STUDENT_TABLE");
 private final String tableName;
 TableName(String tableName) {
 this.tableName = tableName;
 }
 public String toString() {
 return tableName;
 }
}

The Abstraction layer

DAOFactory.java

package aseemDao;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.naming.NamingException;
import aseemEnums.Databases;
public abstract class DAOFactory {
 // Abstract Instance methods
 public abstract DAOInsert getDAOInsert() throws SQLException;
 public abstract DAORead getDAORead() throws SQLException;
 public abstract DAODelete getDAODelete();
 public abstract DAOUpdate getDAOUpdate();
 // Concrete Class Methods
 public static DAOFactory factoryProducer(Databases db)
 throws NamingException {
 switch (db) {
 case Oracle:
 return new OracleFactory();
 default:
 return null;
 }
 }
 static void closeAll(PreparedStatement ps, ResultSet rs) {
 try {
 rs.close();
 } catch (Exception e) {
 }
 try {
 ps.close();
 } catch (Exception e) {
 }
 }
}

DAORead.java

package aseemDao;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import aseemEnums.TableName;
public interface DAORead {
 public abstract <T> List<T> getAll(Connection con, TableName tableName)
 throws SQLException;
 public abstract <T> List<T> getAllForInput(Connection con,
 TableName tableName, String columnName, String searchValue)
 throws SQLException;
 public abstract <T> T getPojoForPrimarKey(Connection con,
 TableName tableName, String primaryKey) throws SQLException;
 public abstract <T> boolean alreadyExisting(Connection con,
 TableName tableName, String primaryKey) throws SQLException;
 public abstract <T> boolean alreadyExisting(Connection con,
 TableName tableName, T currentPojo) throws SQLException;
}

Concrete Implementation of DAO's abstraction ----

OracleFactory.java

package aseemDao;
import java.sql.SQLException;
public class OracleFactory extends DAOFactory {
 @Override
 public DAOInsert getDAOInsert() throws SQLException {
 return new OracleInsert(this);
 }
 @Override
 public DAORead getDAORead() throws SQLException {
 return new OracleRead(this);
 }
 @Override
 public DAODelete getDAODelete() {
 return new OracleDelete(this);
 }
 @Override
 public DAOUpdate getDAOUpdate() {
 return new OracleUpdate(this);
 }
}

OracleRead.java

package aseemDao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import aseemEnums.QueryType;
import aseemEnums.TableName;
public class OracleRead implements DAORead {
 DAOFactory fac = null;
 OracleRead(DAOFactory fac) throws SQLException {
 this.fac = fac;
 }
 @SuppressWarnings("unchecked")
 public <T> List<T> getAll(Connection con, TableName tableName)
 throws SQLException {
 List<T> list = new ArrayList<T>();
 PreparedStatement ps = null;
 ResultSet rs = null;
 try {
 ps = con.prepareStatement("select * from " + tableName);
 rs = ps.executeQuery();
 while (rs.next()) {
 list.add((T) OracleSpecifics
 .getPojoFromResultSet(tableName, rs));
 }
 } finally {
 DAOFactory.closeAll(ps, rs);
 }
 return list;
 }
 @SuppressWarnings("unchecked")
 public <T> List<T> getAllForInput(Connection con, TableName tableName,
 String columnName, String searchValue) throws SQLException {
 List<T> list = new ArrayList<T>();
 PreparedStatement ps = null;
 ResultSet rs = null;
 try {
 ps = con.prepareStatement("SELECT * FROM " + tableName + " WHERE "
 + columnName + " LIKE '%" + searchValue + "%'");
 rs = ps.executeQuery();
 while (rs.next()) {
 list.add((T) OracleSpecifics
 .getPojoFromResultSet(tableName, rs));
 }
 } finally {
 DAOFactory.closeAll(ps, rs);
 }
 return list;
 }
 @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;
 }
 @Override
 public <T> boolean alreadyExisting(Connection con, TableName tableName,
 String primaryKey) throws SQLException {
 if (getPojoForPrimarKey(con, tableName, primaryKey) != null) {
 return true;
 } else {
 return false;
 }
 }
 @Override
 public <T> boolean alreadyExisting(Connection con, TableName tableName,
 T currentPojo) throws SQLException {
 String primaryKey = OracleSpecifics.<T> getPrimaryKey(tableName,
 currentPojo);
 if (alreadyExisting(con, tableName, primaryKey) == false) {
 return false;
 } else {
 return true;
 }
 }
}

The lower utility layer in DAO

OracleSpecifics.java

package aseemDao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import aseemEnums.QueryType;
import aseemEnums.TableName;
import aseemPojo.Student;
public class OracleSpecifics {
 // These functions don't call any table-specific functions
 static String queryString(TableName tableName, String keyValue,
 QueryType type) {
 String firstHalf = null;
 switch (type) {
 case READ:
 firstHalf = "select * from " + tableName + " where ";
 break;
 case DELETE:
 firstHalf = "DELETE from " + tableName + " where ";
 break;
 default:
 }
 switch (tableName) {
 case STUDENT_TABLE:
 return firstHalf + "STUDENT_ID='" + keyValue + "'";
 default:
 return null;
 }
 }
 static <T> String getPrimaryKey(TableName tableName, T currentPojo) {
 switch (tableName) {
 case STUDENT_TABLE:
 return ((Student) currentPojo).getStudentId();
 default:
 return null;
 }
 }
 // These functions call table-specific functions
 @SuppressWarnings("unchecked")
 static <T> T getPojoFromResultSet(TableName tableName, ResultSet rs)
 throws SQLException {
 switch (tableName) {
 case STUDENT_TABLE:
 return (T) SpecStudent.getPojo(rs);
 default:
 return null;
 }
 }
}

One of the Table-Specifics utility Classes

SpecStudent.java

package aseemDao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import aseemEnums.TableName;
import aseemPojo.Student;
public class SpecStudent {
 public static Student getPojo(ResultSet rs) throws SQLException {
 Student currentStudent = new Student();
 currentStudent.setStudentId(rs.getString("Student_Id"));
 currentStudent.setRollNo(rs.getString("Roll_No"));
 currentStudent.setStudentName(rs.getString("Student_Name"));
 currentStudent.setAddress(rs.getString("Address"));
 currentStudent.setEmail(rs.getString("Email"));
 currentStudent.setContactNumber(rs.getString("Contact_Number"));
 currentStudent.setGuardianName(rs.getString("Guardian_Name"));
 currentStudent.setEnrollmentDate(rs.getDate("Enrollment_Date"));
 return currentStudent;
 }
}
lang-java

AltStyle によって変換されたページ (->オリジナル) /