我将尽力说明我即将实现的目标...
假设我有一个用户表:
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());
它似乎也可以工作。同样,不确定这是否是正确的方法,但至少可以做到。