JPA 与存储在不同数据库中的用户实体的多对多关系

Posted

技术标签:

【中文标题】JPA 与存储在不同数据库中的用户实体的多对多关系【英文标题】:JPA Many-to-Many Relationship with Users Entity Stored in a Different Database 【发布时间】:2021-08-11 12:43:49 【问题描述】:

TLDR;

我正在尝试创建与存储在不同数据库中的实体(用户)的关系,但不知道如何在 JPA 中执行此操作。

用例

我正在 Spring Boot/JPA 中实现汽车服务。我创建了一个名为Car 的实体/表。一辆车可能有多个所有者(用户),一个用户可能拥有多辆汽车。但是,User 表存储在单独的用户服务中。

由于这是一个多对多的关系,我觉得JoinTable 是合适的。这里的问题是没有要加入的User 表。我只需要将用户的 UUID 存储在连接表中,因此GET 方法可以获取给定用户或给定汽车的所有车主的所有汽车。

源代码

这是Car 实体的尝试。删除ForeignKey 约束允许在连接表中创建条目,而不会由于缺少子表而导致错误。

@Data
@Entity
@Table(name = "car")
@EqualsAndHashCode(callSuper = true)
public class Car 
  @Id @GeneratedValue private UUID id;

  @ManyToMany
  @JoinTable(
      name = "car_owner",
      joinColumns = @JoinColumn(name = "car_id"),
      inverseJoinColumns = @JoinColumn(name = "user_id", insertable = false, updatable = false),
      foreignKey = @ForeignKey(name="user_id", value = ConstraintMode.NO_CONSTRAINT))
  private Set<User> users;

  private String model;
  private double price;

这是 User 实体 - JPA JoinTable 所必需的,但仅用于其 ID:

@Data
@Entity
@EqualsAndHashCode()
public class User 

  @Id private UUID id;

  @ManyToMany(mappedBy = "users")
  @JsonIgnore
  private Set<Car> cars;

将下面的模型发送到 JPA Repository save() 方法会成功创建连接表中的汽车和条目:


  "model": "Ford Model T",
  "price": 850.00,
  "users": [
    "id": "e048b593-aad9-4285-b3e6-49475ad9bd1d",
    "id": "1d0f7b1e-bc36-4b99-80a1-8835779598ca"
  ]

问题陈述

但是,当我尝试检索属于特定用户 ID 的所有汽车时:

  @Query(
      value = "select * from car c inner join car_owner co on c.id = co.car_id where co.user_id = :userId",
      nativeQuery = true)
  List<Car> findByUserId(UUID userId);

我收到以下错误:

错误:列 user1_.id 不存在 已解决[org.springframework.http.converter.HttpMessageNotWritableException:无法写入JSON:无法提取ResultSet;嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException: could not extract ResultSet (through reference chain: java.util.ArrayList[0]->Car["users"])]

如何回读给定用户的汽车列表?我会以错误的方式解决这个问题吗?我对其他实现持开放态度。

【问题讨论】:

是存储在同一个数据库但不同的架构还是不同的数据库服务器? 如果是同一个数据库,但是schema不同,可以在当前schema中创建一个视图,查询多个schema。但如果它是一个不同的数据库,我也不确定 @SridharPatnaik 用户数据库尚未创建,但我想使用微服务架构,这需要将这两个服务解耦(不同的数据库)。 如果用户由其他服务管理,您的汽车服务中不应包含用户实体。所以只需使用 @ElementCollection 私有 Set 所有者 @vincendep 你说得对,我需要删除User 实体和JoinTable。但我仍然不清楚如何将car_owner 表和列与@ElementCollection 映射。将继续朝这个方向寻找。谢谢! 【参考方案1】:

正如@vincendep 所述,解决方案是删除User 实体并将Car 实体中的users 属性替换为以下内容:

@ElementCollection
@CollectionTable(name = "car_owner", joinColumns = @JoinColumn(name = "car_id"))
@Column(name = "user_id")
private Set<UUID> owners;

这仍将使用car_owner 连接表。可以使用以下型号创建具有多个用户的汽车:


  "model": "Ford Model T",
  "price": 850.00,
  "owners": [
    "32a7967d-8580-418d-b53a-221ed0c52222",
    "a708455c-c37c-4712-a7ce-59c9be5a5eb5"
  ]

您可以按如下方式通过用户 ID 查询汽车(感谢 @vincendep):

@Query(value = "from Car c where :ownerId member of c.owners")
List<Car> findByUserId(UUID ownerId);

【讨论】:

查询也应该是这样的 @Query(value = "FROM Car c WHERE :owner MEMBER OF c.owners") List findByUserId(UUID owner); 感谢您的帮助@vincendep 一百万!解决方案更清洁。我一定会把它添加到我的 JPA 工具箱中。

以上是关于JPA 与存储在不同数据库中的用户实体的多对多关系的主要内容,如果未能解决你的问题,请参考以下文章

JPA多对多映射

Entity Framework Core:与同一实体的多对多关系

清除与 JPA / JPQL 的多对多关系

具有额外多对多关系的 JPA 多对多

JPA中的多对多双向映射

在 TypeORM 与 GraphQL 的多对多关系上使用数据加载器,查询多对多