NoSQL 数据存储(如 MongoDB)的主要优势之一是它们是无模式的。对于动态类型语言,这似乎很自然。您可以接收一些任意的 JSON 输入,在已知字段上执行业务逻辑,并在无需首先定义对象的情况下保留整个内容。
如果您选择的语言仅限于静态类型,比如 Java,该怎么办?我怎样才能达到相同水平的灵 active ?
典型的数据流如下:
- JSON 输入
- 序列化为 Java 对象以执行业务逻辑
- 反序列化为 BSON 以在 Mongo 中持久化
因为您想使用 POJO 而不是 JSON 字符串来执行业务逻辑,所以序列化到对象的步骤是必要的。但是,在将输入序列化为对象之前,我必须先定义它。如果输入包含对象中未定义的其他字段怎么办?虽然它们可能不会在业务逻辑中使用,但我可能仍然希望能够持久化它们。我似乎有将未定义字段放入 map 的实现,但我不确定这是否是最好的方法。一方面,未定义的字段也可能是复杂的对象。
最佳答案
无架构数据并不一定意味着无结构数据;这些字段通常是预先知道的,并且可以在其上应用一些类型安全的模式以避免 Magic Container anti-pattern但情况并非总是如此。有时 key 是由用户输入的,无法提前知道。
我用过 Role Object Pattern多次使动态结构连贯。我认为它非常适合这两种情况。
角色对象模式定义了一种访问对象的不同 View 的方法。典型的例子是一个用户可以承担多个角色,例如客户、供应商和卖家。这些 View 中的每一个都有它可以执行的不同操作,并且可以从任何其他 View 访问。公共(public)字段通常在界面级别可用(特别是 userId()
,或者在您的情况下为 toJson()
)。
这是使用模式的示例:
public void displayPage(User user) {
display(user.getName());
if (user.hasView(Customer.class))
displayShoppingCart(user.getView(Customer.class);
if (user.hasView(Seller.class))
displayProducts(user.getView(Seller.class));
}
对于具有已知结构的数据,您可以使用多个 View 将不同的键集放入内聚单元中。这些不同的 View 可以读取构建上的json数据。
对于具有动态结构的数据,权威的 RawDataView 可以拥有动态形式的数据(即 Magic Container 类似于 HashMap<String, Object>
)。这可用于查询动态数据。同时,可以延迟创建类型安全的包装器,并可以委托(delegate)给 RawDataView 以帮助提高程序的可读性/可维护性:
public class Customer implements User {
private final RawDataView data;
public CustomerView(UserView source) {
this.data = source.getView(RawDataView.class);
}
// All User views must specify this
@Override
public long id() {
return data.getId();
}
@Override
public <T extends UserView> T getView(Class<T> view) {
// construct or look up view
}
@Override
public Json toJson() {
return data.toJson();
}
//
// Specific to Customer
//
public List<Item> shoppingCart() {
List<Item> items = (List<Item>) data.getValue("items", List.class);
}
// etc....
}
这两种方法我都取得了成功。以下是我在此过程中发现的一些额外提示:
- 尽可能对您的数据采用静态结构。这使得事情更容易维护。在遗留系统上工作时,我不得不打破这条规则并使用 RawDataView 方法。如上所述,您可能还必须使用动态输入的用户数据来打破它。在这种情况下,使用非动态字段名称的约定,例如前导下划线 (
_userId
) - 有
equals()
和hashcode()
实现使得user.getView(A.class).equals(user.getView(B.class))
对于同一用户始终为真。 - 有一个 UserCore 类来完成所有繁重的公共(public)代码,例如创建 View ;执行常见操作(如
toJson()
)返回常见字段(如userId()
);并实现equals()
和hashcode()
.将所有 View 委托(delegate)给这个核心对象 - 有一个 AbstractUserView 委托(delegate)给 UserCore 并实现 equals() 和 hashcode()
- 使用类型安全的异构容器(如 ClassToInstanceMap )构建/缓存 View 。
- 允许查询 View 的存在。这可以通过
hasView()
来完成。方法或让 getView 返回Optional<T>
关于java - NoSQL 无模式数据和静态类型语言,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8005309/