为了账号安全,请及时绑定邮箱和手机立即绑定

spring-data-jpa 3路交叉表

spring-data-jpa 3路交叉表

犯罪嫌疑人X 2024-01-05 10:32:58
我将尝试说明我想尽快实现的目标...假设我有一个用户表:USER_INFO  USER_ID [PK]  USER_NAME  PASSWORD用于定义每个用户连接的交集表(N:M - ManyToMany)CONNECTION_INFO  CONNECTION_ID [PK]  USER_A_ID [FK - references USER_INFO(USER_ID)]  USER_B_ID [FK - references USER_INFO(USER_ID)]  CONNECTION_TYPE_ID [FK - references CONNECTION_TYPE(CONNECTION_TYPE_ID)]CONNECTION_TYPE 很简单:CONNECTION_TYPE  CONNECTION_TYPE_ID [PK]  CONNECTION_TYPE_NAME [CHECK allowed values are: FRIEND, FAMILY, ...]在 Spring 方面,我将我的 User 实体定义为:@Entity@Table(name = "USER_INFO")public class User implements Serializable {  @Id  @NotNull  @Column(name = "USER_ID")  @GeneratedValue(strategy = GenerationType.IDENTITY)  private Integer userId;  @Column(name = "USER_NAME)  private String userName;  @Column(name = "PASSWORD)  private char[] password;  @ManyToMany(fetch = FetchType.LAZY)  @JoinTable(name = "CONNECTION_INFO",             joinColumns = { @JoinColumn(name = "USER_A_ID") },             inverseJoinColumns = { @JoinColumn(name = "USER_B_ID") })  private List<User> connections;  // ctor, getters, setters, toString, ...}我有一个 UserRepository 接口,它扩展了 JpaRepository 等。现在,它工作得很好,我可以检索所有连接,无论是朋友、家人、最讨厌的人、被阻止、恶魔等...现在,我的问题是,如何根据 Connection.Types 只获取给定用户的特定连接?例如,我只想找到朋友,或者只想找到家人,我想你明白我的意思。这个三路交叉表让我很头疼。@Clarification:我想要的是在我的用户实体上定义的 @ManyToMany 关系,该实体恰好有额外的列。我知道在这种情况下有建议的解决方案,例如LINK。在我的例子中,这个额外的列是第三个表的外键(USER_INFO(保存用户),CONNECTION_INFO(保存用户之间的连接N:M +有关连接类型的信息),CONNECTION_TYPE。如果我可以用它来建模spring-data-jpa 据我了解,我只需要 UserRepository 下的一个神奇的命名方法,类似于(完全不正确):public interface UserRepository extends JpaRepository<User, Integer> {    List<User> findUserFriendsByConnectionType(User userWhoseFriendsWeAreSearching, String connectionTypeFromTheThirdTable);}这就是我想要的。我知道通过为交叉表创建一个实体并打破ManyToMany到OneToMany和ManyToOne,使用普通的额外列很简单,碰巧我有第三个表和可能的ManyToOne(1个连接可以有1个关联类型,而一个连接可以有1个关联类型) type 可以链接到任意数量的连接)与 connection_type 表的交集实体上。我希望它能澄清一切。上面只是一个示例,我从未想过我们会挂在枚举上,因为我想让它看起来简单,我可能让它变得太简单了:)。
查看完整描述

1 回答

?
慕工程0101907

TA贡献1887条经验 获得超5个赞

我设法解决了这个问题,但我不确定这是否是正确的方法。无论如何,这是我的解决方案。考虑以下 3 个表:


create table USER_INFO (

  USER_ID int not null primary key,

  USER_NAME varchar(16),

  PASSWORD varchar(64)

);


create table CONNECTION_TYPE (

  CONNECTION_TYPE_ID int not null primary key,

  CONNECTION_TYPE_NAME varchar(16) not null,

  CONNECTION_TYPE_DESCRIPTION varchar(128),

  unique (CONNECTION_TYPE_NAME)

);


create table CONNECTION (

  CONNECTION_ID int not null primary key,

  CONNECTION_TYPE_ID int,

  RELATED_USER_ID int,

  RELATING_USER_ID int,

  foreign key (CONNECTION_TYPE_ID) references CONNECTION_TYPE(CONNECTION_TYPE_ID),

  foreign key (RELATED_USER_ID) references USER_INFO(USER_ID),

  foreign key (RELATING_USER_ID) references USER_INFO(USER_ID)

通过上述 3 个表,我想提供一种功能来根据连接类型获取任何给定用户的连接。为此,我创建了 3 个实体,如下所示:


@Entity

@Table(name = "CONNECTION_TYPE")

public class ConnectionType implements Serializable {

  @Id

  @NotNull

  @Column(name = "CONNECTION_TYPE_ID")

  @GeneratedValue(strategy = GenerationType.IDENTITY)

  private Integer connectionTypeId;


  @NotNull

  @Column(name = "CONNECTION_TYPE_NAME", unique = true)

  private String connectionTypeName;


  @Column(name = "CONNECTION_TYPE_DESCRIPTION")

  private String connectionTypeDescription;


  ...

}

这里没有什么特别有趣的地方,我省略了构造函数、getter、setter 等,并且从 ConnectionType 中我不想为该类型的所有连接提供映射,因此不存在方向。


@Entity

@Table(name = "CONNECTION")

public class Connection implements Serializable {

  @Id

  @NotNull

  @Column(name = "CONNECTION_ID")

  @GeneratedValue(strategy = GenerationType.IDENTITY)

  private Integer connectionId;


  @NotNull

  @ManyToOne(fetch = FetchType.LAZY)

  @JoinColumn(name = "CONNECTION_TYPE_ID", referencedColumnName = "CONNECTION_TYPE_ID")

  private ConnectionType connectionType;


  @NotNull

  @ManyToOne(fetch = FetchType.LAZY)

  @JoinColumn(name = "RELATED_USER_ID", referencedColumnName = "USER_ID")

  private User relatedUser;


  @NotNull

  @ManyToOne(fetch = FetchType.LAZY)

  @JoinColumn(name = "RELATING_USER_ID", referencedColumnName = "USER_ID")

  private User relatingUser;


  ...

}

如果对其他人来说,至少对我而言,这更有趣。这将是我的交集表实体。对于使用的 ConnectionType 与 ManyToOne 存在单向映射,因为一个 Connection 只能具有一个 ConnectionType,而同一 ConnectionType 可以重复用于任意数量的 Connection。其他 2 个用户映射我确信我已经搞砸了,但在此之前这是用户实体:


@Entity

@Table(name = "USER_INFO")

public class User implements Serializable {

  @Id

  @NotNull

  @Column(name = "USER_ID")

  @GeneratedValue(strategy = GenerationType.IDENTITY)

  private Integer userId;


  @NotNull

  @Column(name = "USER_NAME")

  private String userName;


  @NotNull

  @Column(name = "PASSWORD")

  private char[] password;


  @OneToMany(fetch = FetchType.LAZY, mappedBy = "relatedUser", cascade = CascadeType.ALL, orphanRemoval = true)

  private List<Connection> connections;

}

现在我更加确定我完全搞砸了,但我会显示实际的错误。我的存储库很简单:


@Repository

public interface UserRepository extends JpaRepository<User, Integer> {

}

我有一个带有简化的 findAllConnectionsForUserById 函数的 UserService :


@Service

public interface UserService {

    List<User> findAllConnectionsForUserById(Integer userId);

}

该方法的实现非常简单:


@Override

@Transactional

public List<User> findAllConnectionsForUserById(Integer userId) {

    Optional<User> _user = userRepository.findById(userId);

    // omitted exception handling...

    User user = _user.get();

    List<Connection> connections = user.getConnections();


    return connections.strea.map(Connection::getRelatingUser).collect(Collectors.toList());

这样,对于简单的情况,它似乎工作得很好,并且如果我也采用 ConnectionType:


connections.stream().filter(c -> c.getConnectionType().getConnectionTypeName().equals("FRIEND")).map(Connection::getRelatingUser).collect(Collectors.toList());

它似乎也有效。同样,不确定这是否是正确的方法,但至少它可以完成工作。


查看完整回答
反对 回复 2024-01-05
  • 1 回答
  • 0 关注
  • 85 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信