如果有人想在这里看到一个github链接。
有四个DAO-DAODelete,DAOUpdate,DAORead,DAOInsert,但我只是将DAORead及其实现放在这里
我已经对代码进行了很多修整,因此如果出现问题,请告诉我,我会予以纠正。否则,您可以看到github链接,因为它具有完整的代码(kinda)。
我的体系结构是这样的
DAO的抽象层位于顶部
与数据库无关的DAO BUT特定于表的实现
表依赖性向下传递到较低的实用层
Enum
向下传递到较低的实用层,通过它它给出表特定的结果/>上一点提到的用于下层实用程序层的表特定的实用程序类。
我省略了一个
Student
pojo,因为它只是一个pojo。 private
变量和所有。请注意,enrollmentDate
属于java.sql.Date
类型我的担忧
我对DAO的上层感到满意,但我认为较低的水平,特别是在
OracleSpecific.java
处,应具有强耦合的物体具有弱耦合。例如有多种方法可以从结果集中获取pojo或获取主键。这些函数中的每一个都有switch
的大小写,用于调用较低实用程序类中的函数。 尽管
schema-specific
方法在不同的实用程序类中捆绑在一起,但是OracleSpecifics.java
中的方法本身没有任何耦合。 。我正在考虑特定于模式的最低级别实用程序类的状态。枚举中包含的这些
TableName
可用于更改DAO的状态,然后DAO可以基于由所有此类状态实现的接口调用特定功能。因此,根据状态,行为可以自动更改。此设计决策是否正确,或者是没有意义的?
我可能忽略了其他事情吗?
对设计本身有任何想法吗?
由于此更改,我是否会失去泛型引入的类型安全性?
枚举枚举
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;
}
}
抽象层
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;
}
具体实现DAO抽象的实现
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;
}
}
}
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;
}
}
}
表特定实用工具类之一
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;
}
}
#1 楼
常规将
if (<expr>) return true; else return false;
替换为return <expr>;
。将每个首字母缩写词的首字母以外的所有字母都改为小写:
DaoFactory
。虽然乍一看看起来很奇怪,但是它们的键入更容易,您会很快习惯它,并且像MySQLJDBCDAO
中那样组合它们时,您不会感到困惑。这个简单,一致的规则消除了很多猜测。与您的复数形式和单数形式命名保持一致。
QueryType
,TableName
和。 。 。 Databases
?代码完成显然可以防止您出错,但这会在一段时间后使您变慢。拼写检查代码。是的,您必须手动执行此操作,但是没有理由让
getPojoForPrimarKey
使其成为公共接口。还有h.j.k.就在这里:将“ pojo”换成“实体”或更多解释性的信息。创建SQLException
并使用实用程序类la Hibernate和Spring对其进行映射。Java包名称通常都是小写的,并形成树形结构: ,DaoException
和aseem.enums
。它们还倾向于避免使用复数形式,因此请使用aseem.dao
而不是aseem.pojo
。每个aseem.enum
都会创建一个新的aseem.enums
,附加字符串,并将结果打包在内部。在语句之间进行拆分会导致额外的临时生成器和不可变的字符串,从而导致垃圾回收器中的流失。接口中的每个方法在定义上都是公共的和抽象的,因此您可以删除这些修饰符。这更多是个人喜好,在那些情况下,我倾向于在可行的地方倾向于懒惰。任何适用于所有事物的多余信息都是噪音。
除了非常高级的事件捕捉或线程的
StringBuilder
方法之外,不要捕获x + y + ... + z
。 StringBuilder
可以捕获Exception
,以使无关的问题冒出来。原因是您可能会无意中捕获到一个不准备处理的异常(例如run
)。可以安全地忽略DAOFactory.closeAll
中的SQLException
,但是可以忽略其他可能表示实际问题的异常类型。因此,将两个InvalidStateException
子句都更改为SQLException
。为什么也不要列名?我没有在表上看到任何要切换的地方[哦,请参阅下一项],如果是的话,应该将其移至具有多态行为的适当类中。枚举应建模一组真正固定的项目。 closeAll
是一个很好的例子。catch
似乎随着它处理a)Oracle和b)每个表的发展而变成一个怪物。使用每个表的子类创建一个正确的SQLException
类,或者从文件中读取配置,以便QueryType
可以是100%Oracle特定的,而您不必在OracleSpecifics
和Table
中重复它。意思?此查询是否查询数据库以查看主键是否已经存在? OracleSpecifics
怎么样?与MySqlSpecifics
类似;使名称更明确,例如SqlServerSpecifics
,以将其与采用列/值映射的alreadyExisting
区分开。我一直喜欢exists
可能不返回任何结果的查询方法,以及getAllForInput
那些返回单个结果或引发异常的方法。 /> 评论
\ $ \ begingroup \ $
为了澄清起见,我应该手动进行拼写检查,以便处理命名中的不一致之处?我是对的还是错过了吗? getPojoForPrimaryKey必须在公共接口中,以便查询特定记录。还是我可以在没有该行的情况下获得特定行?仅供参考,我不是一个有经验的人。说了这么多其他类型的数据库呢?您说不要捕获Exception,因为它可能导致不相关的问题。什么样的问题我该怎么办?将用户定义的Exeption传播到呼叫层以向呼叫者提供信息?
\ $ \ endgroup \ $
– Aseem Bansal
2013年12月3日14:32
\ $ \ begingroup \ $
我真的很关心DAO设计。具体来说,在各种转换情况下,OracleSpecifics将如何发展并减少相关术语之间的衔接。关于如何安排表格特定内容的任何建议?到目前为止,我对反射有了一些了解。还有其他事吗关于名称和其他所有内容的好的建议。我会照顾他们的非常感激。
\ $ \ endgroup \ $
– Aseem Bansal
2013年12月3日14:37
\ $ \ begingroup \ $
@AseemBansal是的,(很遗憾)拼写检查是对类/变量名的手动处理。我已经阐明了Java下的第3和第4个项目符号。
\ $ \ endgroup \ $
– David Harkness
13年12月3日在18:01
#2 楼
我暂时将实际的通用DAO讨论排除在这个答案之外,只提供我两分钱的编码风格...您可以使用更好的方法名称代替
getPojo
。 .. Pojo易于理解,但仍然是一个非正式术语,无论是否保留全部上限,您都可能会受到平等的评论-毕竟是首字母缩写。 Spring框架使用mapRow
方法调用其帮助程序类RowMappers,所以也许您想遵循类似的方法。 http://docs.spring.io/spring/docs/3.0.x/api/org/springframework/jdbc/core/RowMapper.html 您可以将
return
语句中的条件检查内联为这样:@Override
public <T> boolean alreadyExisting(Connection con, TableName tableName,
String primaryKey) throws SQLException {
return getPojoForPrimarKey(con, tableName, primaryKey) != null;
}
@Override
public <T> boolean alreadyExisting(Connection con, TableName tableName,
T currentPojo) throws SQLException {
return alreadyExisting(con, tableName, OracleSpecifics.<T> getPrimaryKey(tableName, currentPojo));
}
实际上,我不确定您是否真的需要映射Pojo才能确定是否存在具有主键的行。 ..也许有一种更简单的方法?编辑:这是因为事情仍在进行中。还是意味着您将使用自己的方言为其他RDBMS软件(如MySQL,MariaDB等)创建新类?
DAO不需要保持“状态”超出正在为其进行映射的当前
Oracle*
对象。您可能需要重新考虑ResultSet
枚举的实现以包含特定状态。在这里需要更多说明。在快速阅读之后,您可能需要考虑方法的“通用性”,因为添加新类(又名实体)传递给DAO框架似乎需要
TableName
。类(或更新您现有的Spec<Table>
)以实现实际实现(例如,调用SpecStudent
类上的所有getter方法),以及更新您的Student
枚举。也许您也可以考虑将反射用于实现,这样从长远来看,需要更新的代码更少了?评论
\ $ \ begingroup \ $
+1排名第三!将第一种方法更改为仅查找PK而无需加载POJO。
\ $ \ endgroup \ $
– David Harkness
2013年12月3日,下午5:30
\ $ \ begingroup \ $
关于内联return语句的好处。关于没有什么是Oracle特定的。是的,完全正确。目前没有任何东西。我不得不与Oracle打交道,因此只制作了Oracle类。我认为,但是DAO本身的结构允许它被其他人使用。为了扩展性,我将其分开。关于包含特定状态的TableName枚举,我将更新问题。我知道当前的实现实际上不是通用的(纯英语是预期的)。 Oracle规范和下层阶级是一个大问题。
\ $ \ endgroup \ $
– Aseem Bansal
2013年12月3日14:20在
\ $ \ begingroup \ $
我来看一下反射。我对程序员也有同样的建议。目前,整个事情还很小,但是从长远来看,它将至少是Oracle Specifics成为一个巨大的怪物。非常感谢您的回复。
\ $ \ endgroup \ $
– Aseem Bansal
2013年12月3日14:23
\ $ \ begingroup \ $
关于映射pojo以确定是否存在具有主键的行,我这样做是为了使DAO的通用性保持一致。假设一个表的主键存储为Timestamp,则需要传递一个Timestamp对象。如果我为主键的数据类型添加了一个类型参数,那么在架构更改的情况下将存在巨大的可维护性问题,即必须更改来自其他架构层的所有函数调用。 Pojo aka DTO是我唯一的限制。那是我认为人们可以维持的最小分母。
\ $ \ endgroup \ $
– Aseem Bansal
13年12月3日,16:02
\ $ \ begingroup \ $
@AseemBansal ORM常用的方法是要求可序列化的主键。它处理整数,时间戳甚至复合键,例如VehicleKey {String state,String plate}。
\ $ \ endgroup \ $
– David Harkness
13年12月3日在17:55
评论
我强烈建议您在尝试推出自己的ORM之前,先检查一下Datanucleus和Hibernate,以解决任何严重的问题。尽管尝试编写ORM对于学生程序员来说是一种通过的仪式。@abuzittingillifirca我知道这样的事情存在,但我想这样做是出于学习目的。这就是为什么我添加了重塑车轮标签的原因。