简体   繁体   English

spring-data-jpa 三通交叉表

[英]spring-data-jpa 3-way-intersection table

I'll try to illustrate what I'm trying to achieve shortly... Let's suppose I have a users table:我将尝试说明我很快要实现的目标......假设我有一个用户表:

USER_INFO
  USER_ID [PK]
  USER_NAME
  PASSWORD

an intersection table to define connections for each user (N:M - ManyToMany)为每个用户定义连接的交集表 (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)]

The CONNECTION_TYPE is simple as: CONNECTION_TYPE 很简单:

CONNECTION_TYPE
  CONNECTION_TYPE_ID [PK]
  CONNECTION_TYPE_NAME [CHECK allowed values are: FRIEND, FAMILY, ...]

On Spring side I defined my User entity as:在 Spring 方面,我将我的用户实体定义为:

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

I have a UserRepository interface that extends JpaRepository etc etc. Now, this works perfectly and I can retrieve all connections be it FRIEND, FAMILY, MOST_HATED_PERSONS, BLOCKED, DEMON, etc...我有一个扩展 JpaRepository 等的 UserRepository 接口。现在,它完美运行,我可以检索所有连接,无论是 FRIEND、FAMILY、MOST_HATED_PERSONS、BLOCKED、DEMON 等...

I tried to integrate the ConnectionType too in the picture however...我也尝试在图片中集成 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
}

Now, my question is, how can I get only specific connections for a given user, based on Connection.Types?现在,我的问题是,如何根据 Connection.Types 仅获取给定用户的特定连接? For example I want to find only FRIENDs, or only FAMILY I think you get my point.例如,我只想找到 FRIENDs 或 FAMILY,我想你明白我的意思。 This 3 way intersection table gives me one of a headache.这个 3 路交叉表让我很头疼。

@Clarification: What I want is a @ManyToMany relation defined on my User entity that happen to have extra column. @Clarification:我想要的是在我的用户实体上定义的@ManyToMany 关系,它恰好有额外的列。 I know in that case there are proposed solutions like LINK .我知道在这种情况下,有建议的解决方案,例如LINK In my case this extra column is a foreign key to a third table (USER_INFO(Holds the users), CONNECTION_INFO(Holds the connections between users N:M + an info on the type of connection), CONNECTION_TYPE. If I can model it with spring-data-jpa from what I understand I only need a magic named method under UserRepository, something like (totally incorrect):在我的情况下,这个额外的列是第三个表的外键(USER_INFO(保存用户),CONNECTION_INFO(保存用户之间的连接N:M +有关连接类型的信息),CONNECTION_TYPE。如果我可以 model 它与spring-data-jpa 据我了解,我只需要在 UserRepository 下使用一个名为魔法的方法,类似于(完全不正确):

public interface UserRepository extends JpaRepository<User, Integer> {
    List<User> findUserFriendsByConnectionType(User userWhoseFriendsWeAreSearching, String connectionTypeFromTheThirdTable);
}

That's all I want.这就是我想要的。 I know it's simple with a normal extra column by creating an entity for the intersection table too and break the ManyToMany to OneToMany and ManyToOne, it just happens I have a third table and a possibly ManyToOne (1 connection can have 1 associated type, while a type can be linked to any number of connections) on the intersection entity with the connection_type table.我知道通过为交集表创建一个实体并将ManyToMany分解为OneToMany和ManyToOne,使用普通的额外列很简单,只是碰巧我有第三个表和一个可能的ManyToOne(1个连接可以有1个关联类型,而一个type 可以链接到任意数量的连接)在与 connection_type 表的交集实体上。

I hope it clears everything up.我希望它能清除一切。 The above are just a sample I never imagined we'd hang up on an enum because I wanted to make it look simple I possibly made it way too simple perhaps:).以上只是我从未想过我们会挂在枚举上的示例,因为我想让它看起来简单,我可能让它太简单了:)。

I managed to solve the problem but I'm not sure if this is the right way to do it.我设法解决了这个问题,但我不确定这是否是正确的方法。 Anyway here's my solution.无论如何,这是我的解决方案。 Consider the following 3 tables:考虑以下 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)

With the above 3 tables, I want to provide a functionality to get connections for any given user based on the connection's type.对于上面的 3 个表,我想提供一个功能来根据连接的类型为任何给定的用户获取连接。 For this I created 3 entities as follows:为此,我创建了 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;

  ...
}

Nothing particularly interesting in here, I omitted the constructor, getters, setters etc and from the ConnectionType I don't want to have a mapping for all connections for this type so that direction is not present.这里没有什么特别有趣的,我省略了构造函数、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;

  ...
}

This one is more interesting if for noone else at least for me.如果没有其他人至少对我来说,这个更有趣。 This would be my intersection table entity.这将是我的交集表实体。 There's the uni-directional mapping for the used ConnectionType with ManyToOne as one Connection can have exactly one ConnectionType while the same ConnectionType can be reused for an arbitrary number of Connections.使用的 ConnectionType 与 ManyToOne 存在单向映射,因为一个 Connection 可以只有一个 ConnectionType,而相同的 ConnectionType 可以用于任意数量的 Connection。 The other 2 User mappings I'm sure I've messed up but before that here's the User entity:其他 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;
}

Now here I'm even more sure I completely messed up, but I'll show the actual error.现在在这里我更加确定我完全搞砸了,但我会显示实际的错误。 My repository is simple as a brick:我的存储库很简单:

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}

And I have a UserService with a simplified findAllConnectionsForUserById function:我有一个带有简化 findAllConnectionsForUserById function 的 UserService:

@Service
public interface UserService {
    List<User> findAllConnectionsForUserById(Integer userId);
}

The method implementation is simple enough:方法实现很简单:

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

This way it seem to work fine for the simple case and in case I take the ConnectionType too:这种方式似乎适用于简单的情况,如果我也采用 ConnectionType:

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

it seem to work as well.它似乎也有效。 Again, not sure if this is the right way but at least it does the job.同样,不确定这是否是正确的方法,但至少它可以完成工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM