我将尽力说明我即将实现的目标...
假设我有一个用户表:

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, ...
}


我有一个可扩展JpaRepository等的UserRepository接口,现在,它可以完美运行,并且我可以检索所有连接,例如FRIEND,FAMILY,MOST_HATED_PERSONS,BLOCKED,DEMON等。

我也尝试在图片中集成ConnectionType ...

@Entity
@Table(name = "CONNECTION_TYPE")
public class Connection implements Serializable {
  public static enum Types {
    FRIEND, FAMILY, BLOCKED, ...
  }

  @Id
  @NotNull
  @Column(name = "CONNECTION_TYPE_ID")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer connectionTypeId;

  @Column(name = "CONNECTION_TYPE_NAME")
  private ConnectionType connectionType;

  // ctor, getters, setter, etc
}


现在,我的问题是,如何才能基于Connection.Types仅获得给定用户的特定连接?例如,我只想找到朋友,或者只想找到家人,我想你明白我的意思。这个三向交叉表让我头疼。

@说明:
我想要的是在我的User实体上定义的@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个关联类型,而a可以使用connection_type表将交点实体上的“类型”链接到任意数量的连接)。

我希望它能清除一切。上面只是一个示例,我从未想象过我们会挂断一个枚举,因为我想让它看起来很简单,也许我可能使它变得太简单了:)。

最佳答案

我设法解决了这个问题,但是我不确定这是否是正确的方法。无论如何,这是我的解决方案。考虑以下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;

  ...
}


这里没有什么特别有趣的,我省略了构造函数,getters,setters等,并且从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;

  ...
}


如果至少对于我来说没有其他人的话,这一点会更有趣。这将是我的交叉表实体。对于具有ManyToOne的使用的ConnectionType,存在单向映射,因为一个Connection可以仅具有一个ConnectionType,而相同的ConnectionType可以用于任意数量的Connection。
我确定其他2个用户映射已经搞砸了,但在此之前是User实体:

@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());


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

10-04 18:02