在我的java应用程序中,我想实现数据库操作的抽象层。我不想将我的应用程序绑定到任何类型的数据库(实现可以是任意的:SQL,XML,基于文档,一堆丑陋的文本文件等)
实体之间有很多关系,大多数时候,关系是1对多。
更新和免责声明:虽然示例很简单,但它们只是整个更复杂模型的一部分,它们很有可能不适合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获取车辆我不需要了解其他权利)。
现在有趣的部分来了。 仓库与车辆之间的关系是1对多。所以我必须在我的实体类和DAO方法中实现这种关系。
现在我有两种方法:
List<Vehicle>
属性放在Depot
类中,并在每次获取Depot
实例时填充它(可能会进行延迟提取)。这样就不会改变DAO接口。Depot
和Vehicle
引入特殊标识符,以便实体类获得其他整数属性int id;
,并向DAO List<Vehicle> getVehiclesForDepot(int depotId)
添加方法。可以通过引入标识符的特殊类而不是普通整数来增强此方法。也许有其他方法?模拟实体之间的关系和设计DAO接口以保持数据库抽象易于使用而不绑定到任何类型的数据库的最佳方法是什么?我不一定会询问完整而准确的解决方案,而是在解决上述问题时的一些原则。
答案 0 :(得分: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
)所需的数据,以及一个或多个用于存储该实体的setter T2
数据持有者对象中的信息。
大多数DAO的加载方法会将关系数据集保留为null
,以便稍后加载。这当然意味着他们的数据持有者对象获取者必须能够处理null
值作为合同的一部分。