【问题标题】:How can I resolve different entities from an id in Spring Data JPA?如何从 Spring Data JPA 中的 id 解析不同的实体?
【发布时间】:2022-01-26 16:50:05
【问题描述】:

我的申请的一些背景:
我正在使用 Spring Boot、Spring Boot Data JPA 和 JDA 在 Java 中开发 Discord-Bot。
该机器人应该在 Discord 中有几个用于不同目的的按钮。当用户单击按钮时,机器人会在事件处理程序中获取按钮的 id(我在创建它时预先给它)。

不同的示例场景:

  1. ID 为 20 的 Button1 解析为具有 Role1 的角色按钮实体,因此该成员获得了分配的 Role1。
  2. ID 为 32 的 Button2 解析为带有一些在点击时运行的逻辑的票据按钮实体。

(不同的按钮实体也有不同的字段)

问题:我怎样才能设计出这样的东西?

我想到的一个选项是拥有一个通用按钮实体,以便 JPA 的 id 代表 Discord 中按钮的 id 并存储另一个 id(例如,来自角色按钮实体,以运行它的逻辑与它的字段而不是票按钮实体的逻辑/字段)。
这个选项是否有意义,如果有,是否可以将Long buttonEntityId 与例如级联角色按钮实体?

提前致谢

【问题讨论】:

  • 你的意思是不同类型的按钮有一些共同的领域和一些不同的领域?
  • 两者的字段只有id,其余字段不同
  • 那么,这是 JPA 的问题吗?从实体设计的角度来看,这将是两个不同的实体,唯一的共同点是名称中的 uave“按钮”。会是这样吗?或者可能是我没有做对。
  • 没错。但我需要以类似以下流程的方式解析这些 id:用户单击按钮 -> 使用 id 调用事件处理程序 -> 获取按钮(所以角色按钮或票证按钮,此时我不知道按钮的类型对应于数据库中的 id) -> 根据按钮实体类型调用角色处理程序或票证处理程序,并在处理程序中执行一些逻辑。
  • 我不会使用 Id 来区分元素类型,而是使用特定的 type 属性。您不能创建带有type 属性的按钮(理想情况下由enum 管理,但普通的String 可以完美匹配)可用于调用处理程序?

标签: java jpa spring-data-jpa


【解决方案1】:

有一些设计模式适合您的业务案例。例如,Factory 决定创建哪种按钮类型,或 Strategy 决定执行哪种行为。

但无论哪种方式,每个人都需要根据某些参数/属性/属性选择一条或另一条路径。您是说要使用元素的ID 来执行此操作,这与 ddbb 中将用作PRIMARY KEYID 相同。所以意思是两个不同的独立表,但在 ID...

这就像软件中的几乎所有东西一样,都可以实现,但这是一种明显的代码味道,一种设计味道。使用某个 id 作为鉴别器,不是针对 id 值本身,而是针对它所代表的数据……没有意义。

您可以设法做到这一点,但您将要使用的解决方案、您必须执行的解决方法以及所有这些暗示的陷阱肯定会降低您的代码的可读性、可维护性和可扩展性。

更有意义的路径是绑定到指定其类型的按钮的特定属性,并在模式/逻辑中使用它来执行/创建一个想法或另一个想法。朝着这个解决方案努力会给你带来更好的结果。

关于此解决方案的数据库表示,除非您找到原因,否则您甚至不需要TYPE 列,因为可以直接从 TABLE/entity 推断出来,这意味着它可以直接实体中的常量。

更新

好的。我认为您在这里混合了不同的关注点。实体应该代表数据。在数据库中有按钮的目的是什么?它们不是数据,而是演示。角色是数据,工单是数据,但按钮不是数据(除非您在其中构建按钮的特定类型的应用程序)。

我会为每个按钮类型创建一个@Component,其中包含与该按钮类型相关的所有业务逻辑。

@Component
public class RoleButtonHandler {

    public doRoleStuff(...) {
        // do roles stuff
    }
   
}
@Component
public class TicketButtonHandler {

    public doTicketsStuff(...) {
        // do tickets stuff
    }
   
}

然后,我只需要一个表示不同按钮类型的枚举,而不是一个实体:

public enum ButtonType {
    ROLE(20),
    TICKET(32);

    private static Map<Integer, ButtonType> ID_VALUES_MAP = Arrays.stream(values()).collect(Collectors.toMap(b -> b.id, b -> b));

    public int id;

    ButtonType(int id) {
        this.id = id;
    }

    public ButtonType getButtonTypeFromId(int id) {
        return ID_VALUES_MAP.get(id);
    }
}

在事件处理程序中,您会收到id,您会获得 ButtonType,并基于此调用一个组件或另一个组件。

使用这种方法,即使您想将按钮保留在数据库中,它们将位于不同的表中,正如您所说的它们具有完全不同的字段,您也可以在专用的@Component 中轻松地无缝地做到这一点。

改进

我不知道这是否是您的情况,但是如果您在休息服务中通过 json 获取按钮的 ID,并且按钮的 ID 由您决定,则可以将其设为 String。在这种情况下,enum 只能有值,因为从 json 转换为具有相同 String 值的枚举是自动的,无需手动翻译所有内容的样板。并且代码更具可读性。

【讨论】:

  • 您的意思是,Discord 中按钮的 id 类似于(顺便说一句。Discord 中的按钮 id 始终是字符串)ticket-12,而ticket 代表类型,12 JPA 中的实体 ID?
  • 你想保存在数据库中的信息是什么?每个按钮单击一行?或每个存在的按钮一行。
  • 我想为每个现有按钮存储一行信息,例如角色 ID(在角色按钮的情况下)、创建按钮的用户以及创建按钮的时间。您认为创建 ID 为例如按钮的方法吗? role-1role-2 等,并将 role/ticket 作为字符串存储在枚举中,如您所写:ROLE("role"), TICKET("ticket"); 适合我的用例吗? (在按钮处理程序中,将 ID 拆分为 -,并通过枚举中的类型调用特定的处理程序,并使用 jpa ID [仅数字])
  • 不,抱歉。我以为你创建了按钮,用户只点击了它们,你的机器人对点击事件做出反应。你是说用户可以创建按钮?
  • 对不起,我表达的不清楚。我(与其他管理员)通过 REST 端点创建按钮,然后将按钮附加到 Discord 消息(此时我可以为按钮提供我想要的任何字符串 [ID] [例如ticket-3])并将其保存到数据库。然后,当用户单击按钮时,将使用按钮的 ID(它是一个字符串)调用 ButtonClickHandler。基于这个 ID,我想调用适当的处理程序(例如票证或角色按钮处理程序)并将按钮从数据库加载到这个处理程序中,然后用它做一些事情。
猜你喜欢
  • 2016-03-14
  • 2023-01-27
  • 2019-09-30
  • 2013-08-09
  • 1970-01-01
  • 2019-04-17
  • 2022-06-11
  • 1970-01-01
  • 2020-05-11
相关资源
最近更新 更多