java 如何在Spring Data JDBC中实现“一对一”关系和互斥替代方案

uinbv5nw  于 2023-05-12  发布在  Java
关注(0)|答案(1)|浏览(120)

给出以下三个表格,描述一个可以是玩具、食物或未知的物品:

CREATE TABLE item (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(255) NOT NULL CHECK (type IN ('TOY', 'FOOD', 'UNKNOWN'))
);

CREATE TABLE item_toy (
    item INTEGER NOT NULL REFERENCES item(id),
    color VARCHAR(255) NOT NULL,
    material VARCHAR(255) NOT NULL
);

CREATE TABLE item_food (
    item INTEGER NOT NULL REFERENCES item(id),
    smell VARCHAR(255) NOT NULL,
    taste VARCHAR(255) NOT NULL
);

玩具物品将只有玩具属性,食品物品只有食品属性,而未知的物品两者都没有。
我们可以使用Spring Data JDBC在Java中对这些表进行建模,方法如下:

public class Item {
    private int id;
    private String name;
    private ItemType type;
    private ItemToy toyProperties;
    private ItemFood foodProperties;
}

public enum ItemType {
    TOY,
    FOOD,
    UNKNOWN
}

public class ItemToy {
    private String color;
    private String material;
}

public class ItemFood {
    private String smell;
    private String taste;
}

public class ItemRepository extends CrudRepository<Item, Integer> {
    // ...
}

我在这个实现中遇到的问题是,字段toyfood可能是null,也可能不是,而不管type的值是什么:理想情况下,我希望未知物品没有任何属性,玩具物品只有toyProperties,食品物品只有foodProperties
有没有一种更优雅的方式来建模这种关系,通过改变数据库结构和/或Java类,使用继承或组合?在这种情况下,存储库类看起来会是什么样子?

x7rlezfr

x7rlezfr1#

这里的根本问题是,您的模型中存在冗余信息,因此存在不同表示相互矛盾的风险。type实际上是由不同属性的存在或不存在决定的。因此,我建议将其从数据库和Java模型中完全删除。然后将一对一关系更改为@Embedded关系,这样您就只有一个包含所有列的表。
如果你需要数据库中的类型,如果你的数据库支持的话,你可以把它添加为function based virtual column。或者将其作为普通列并使用触发器填充。
如果你需要Java中的类型,你可以创建一个getter来计算它,或者把它保存在一个@Transient字段中。
如果这对你不起作用,不管是什么原因,你在这里留下了两个问题。数据库级的一致性和应用程序/Java端的一致性。

数据库

让我们从数据库端开始。我有三个选择:
1.您可以将模型更改为在单个表中具有所有属性,并且将引用标记为@Embedded而不是一对一关系,然后可以在数据库中创建检查约束,以确保仅创建有效的组合。
1.如果您不想将这些表合并为一个表,则可以基于这些表创建一个物化视图,并对其施加约束。要使其按预期工作,您需要自动更新的实体化视图。我知道Oracle支持这些,也知道至少有些数据库不支持。
1.最后一种方法是使用触发器来验证约束。
由于它最容易实现,适用于所有数据库,并且可以更简单/更快地阅读和写入,因此我推荐第一种方法。

Java

对于Java端,您可以简单地依靠标准Java代码来检查约束。为了使这个失败、保存和简单,我建议使你的模型不可变。它允许您检查构造函数中的约束并完成它。
如果这导致API难以在应用程序中使用,您可以创建一个构建器,在构建示例之前收集示例的所有数据。

相关问题