DAO设计和数据结构
Posted
技术标签:
【中文标题】DAO设计和数据结构【英文标题】:DAO design and data structure 【发布时间】:2012-01-25 22:39:36 【问题描述】:在我的 java 应用程序中,我想为数据库操作实现抽象层。我不想将我的应用程序绑定到任何类型的数据库(实现可以是任意的:SQL、XML、基于文档、一堆丑陋的文本文件等)
实体之间有很多关系,大多数时候,关系是一对多的。
更新和免责声明:虽然例子很简单,但它们只是整个更复杂模型的一部分,它有可能不适合 ORM/SQL 模型(两者都是因为大量数据:~当归一化为关系并且由于数据的不同性质时,有数十亿条记录)。这里我问的是实现简单关系,但这并不意味着它们构成了应用的唯一问题。
一个简化的例子如下:
public class Vehicle
String mark;
String model;
String registrationId;
public class Depot
String name;
String address;
每个实体都有自己的 DAO 接口:
public interface VehicleDAO
List<Vehicle> getVehicles();
Vehicle getVehicleByRegistrationId(String registrationId);
public interface DepotDAO
List<Depot> getDepots();
Depot getDepotByName(String name);
这些 DAO 也被简化了,只是为了展示一些针对特定实体隔离的方法(通过其注册 id 获取车辆,我不需要了解其他实体的任何信息)。
现在有趣的部分来了。 Depot 和 Vehicle 之间的关系是一对多的。所以我必须在我的实体类和 DAO 方法中实现这种关系。
现在我有两种方法:
将List<Vehicle>
属性放在Depot
类中,并在我获取Depot
实例时填充它(可能会改进延迟获取)。这样 DAO 接口就不会改变。
引入Depot
和Vehicle
的特殊标识符,以便实体类获得额外的整数属性int id;
,我们在DAO中添加一个方法List<Vehicle> getVehiclesForDepot(int depotId)
。可以通过为标识符引入特殊类而不是普通整数来增强这种方法。
也许还有其他方法?对实体之间的关系进行建模并设计 DAO 接口以保持数据库抽象易于使用且不绑定到任何类型的数据库的最佳方法是什么?我不一定要问完整准确的解决方案,而是解决上述问题时的一些原则。
【问题讨论】:
这被称为重新发明*** - 使用像 Hibernate 这样的 ORM。 @PetarMinchev 使用 ORM 违反了不绑定到任何类型数据库的要求之一 然后只对数据库部分使用 ORM。而且我还没有在实际项目中看到将数据存储从数据库更改为xml
例如。
很难理解为什么要抽象任何类型的数据源。您是否要将 Depot
数据源从 XML 更改为关系“on-the-fly”?您的示例似乎仅适用于关系数据模型(例如 SQL)。我看不太清楚,XML/文本/文档等是如何适应的。为什么需要抽象?你应该先解释一下,然后才能期待一个好的答案
我认为关系数据库比xml
文件或任何其他存储更适合存储大量信息。
【参考方案1】:
在您的示例中,您使用本地化为 DAO 类型的方法,最好定义一组一致的 DAO 方法来涵盖这些功能,即
// load object of DAO type T
<T> load(id)
// load objects of DAO type T
List<T> load(List<id>)
// load all objects of DAO type T
List<T> find()
// load multiple objects of DAO type T
List<T> find(relation)
等等,如果你使用一致的id
类型(例如long
),你可以定义一个interface
覆盖你的基本方法。
要加载关系,您有几个选项,最好取决于您对对象及其关系的使用:
使List<T>
成为数据持有者的属性并将其作为load()
的一部分填充
当相关实体与其他实体没有关系时,这适用于少量关系。如果是这样,您将不得不部分加载它们以防止预先加载过多(您提到的延迟加载是一种策略。)
使List<T_id>
成为数据持有者的一个属性,并将其作为load()
的一部分填充
这适用于中等数量的关系,与load(List<id>)
方法一起用于访问相关实体。
对于大量数据,正如您所提到的,您要解决的大部分问题是,您可以进一步解耦关系并使用 DAO 方法,例如:
// retrieve related entity id's for this DAO T
List<id> loadIds(T)
为与作为参数传递的数据持有者对象有关系的实体加载一组(外部)id
。然后,您的经理/业务/服务层使用该 id 列表加载下一组实体,可能通过将偏移量传递到 id 列表和要加载的实体数量来分块。
或者,您可以通过添加 DAO 方法来解耦有关关系的知识,以填充另一种 DAO 类型的数据持有者对象中的外部关系:
// fill entity relations for T2 to this DAO tyoe T
void fill(T2)
T::fill()
方法将使用T2
上的getter 来获取确定要加载的相关实体(或其id
's)所需的数据,并使用一个或多个setter 将该信息存储在T2
中数据持有者对象。
大多数这些 DAO 的加载方法会将关系数据集保留为 null
,以便稍后加载。这当然意味着他们的数据持有者对象获取器必须能够处理 null
值作为合同的一部分。
【讨论】:
谢谢!至于泛型——从接口的角度来看,它们可能很有用,但是当涉及到特定的实现时,这些方法将变得非常庞大。那是因为不同的实体有不同的属性。那么实际的实现不会充斥着检查if (T instanceof X)
吗?
目前我认为第二种方法(使用 id 列表来维护关系)是最好的方法。大量数据仅适用于一小部分实体(车辆的运营数据 - 对于它们,我可以使用稍微不同的策略),我宁愿坚持使设计更简洁的解决方案。
@pavel_kazlou,你没有过多使用泛型是对的。如果您的数据模型支持它,则仅使用非通用接口,只有 long
、String
、List
和您的 DAO 数据持有者类型作为参数。如果加载的列表没有改变你的 DAO 方法,如果 id 和数据持有者对象而不是 List
s,则可能返回数组。 (当您处理大量数据时,内存就会成为问题。)以上是关于DAO设计和数据结构的主要内容,如果未能解决你的问题,请参考以下文章