如何在休眠中建立友谊关系?

How to model a friendship relationship in hibernate?

我需要一段友情关系。我有一个友谊 class,有两个主键,每个主键都是一个成员 class。我收到以下异常:

org.hibernate.MappingException: Foreign key (FK_8ynretl1yt1xe3gcvfytrvpq:Friendship [])) must have same number of columns as the referenced primary key (Member [username])

友谊

@Entity
public class Friendship implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = -1234656876554786549L;
    @Id
    @ManyToOne
    Member requester;
    @Id
    @ManyToOne
    Member friend;
    @Temporal(javax.persistence.TemporalType.DATE)
Date date;

成员

@Entity
public class Member {
    @Id
    @MapsId
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "username")
    Credential credential;
    @Column(nullable = false)
    String fname;
    @Column(nullable = false)
    String lname;
    @Column(nullable = false)
    short gender;

凭据

@Entity
public class Credential {
    @Id
    @Column(nullable = false, unique = true)
    private String username;
    @Column(nullable = false)
    private String password;
    @Column(nullable = false)
    private String authority;
    @Column(nullable = false)
    private boolean enabled;

您必须将单独的 ID 字段添加到 Member class,以便 @MapsID 注释进行映射。像这样:

@Entity
public class Member implements Serializable {
    @Id
    private String username;

    @MapsId
    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "username")
    Credential credential;
    @Column(nullable = false)
    String fname;
    @Column(nullable = false)
    String lname;
    @Column(nullable = false)
    short gender;

}

Friendship class 中也尝试指定 @JoinColumn

@Entity
public class Friendship implements Serializable {

    @Id
    @ManyToOne
    @JoinColumn(name = "username")
    Member requester;

    ...
}

您缺少 Friendship class 中主键的建模。 例如:

@Embeddable
public class FriendshipPK implements Serializable
{
    @Column(name = "requester_id")
    protected String requesterId;

    @Column(name = "friend_id")
    protected String friendId;
}

友情class现在可以修改如下:

@Entity
public class Friendship implements Serializable
{
    @EmbeddedId
    protected FriendshipPK friendshipId = new FriendshipPK();

    @ManyToOne
    @MapsId("requesterId")
    Member requester;

    @ManyToOne
    @MapsId("friendId")
    Member friend;

    @Temporal(javax.persistence.TemporalType.DATE)
    Date date;
}

我稍微更新了会员class:

@Entity
public class Member implements Serializable
{
    @Id
    protected String memberId;

    @MapsId
    @OneToOne(optional = false)
    @JoinColumn(name = "username")
    Credential credential;
    @Column(nullable = false)
    String fname;
    @Column(nullable = false)
    String lname;
    @Column(nullable = false)
    short gender;
}

我从成员 class 中删除了级联,并首先创建了凭据对象。但您可以根据需要更改它。

撇开 MemberCredential 应该实现 Serializable 如果使用多个没有标识符类型的 id 属性,你的映射很好,这似乎是 Hibernate 中的一个错误.

解决方案 1

我设法通过在 friend 中声明 referencedColumnName 和在 Friendship 中声明 requester 关联来完成这项工作:

@Id
@ManyToOne
@JoinColumn(referencedColumnName = "username")
Member requester;

@Id
@ManyToOne
@JoinColumn(referencedColumnName = "username")
Member friend;

这样我们明确地告诉 Hibernate 复合 id 引用了哪些列,这样它就不必自己弄清楚了。

解决方案 2

解决方案1让我想到了Hibernate中的错误可能是什么原因。它似乎在某种程度上受到 Hibernate 处理实体映射的顺序的影响。如果您显式声明引用列,则一切正常,否则 Hibernate 在构建复合键时似乎不知道有关引用列的所有详细信息。

所以我将添加注释 classes 到会话工厂配置的顺序更改为:

Credential
Member
Friendship

然后一切都与您的原始映射一起工作(在 MemberCredential 中实施 Serializable 之后)。

我按此顺序以编程方式将 classes 添加到 Configuration class,但我认为通过在 persistence.xml 中指定此顺序可以实现相同的效果] 或 hibernate.cfg.xml:

<class>Credential</class>
<class>Member</class>
<class>Friendship</class>

尽管如此,此解决方案仅用于演示目的(您或其他人稍后可以重新排序 classes 而无需牢记此问题),因此我建议使用解决方案 1。

备注

您更了解您的用例,但我个人认为您应该使用 @IdClass@EmbeddedId,因为它们在 JPA 中已标准化;没有标识符类型的多个 id 属性是 Hibernate 特定的功能。除了能够更轻松地构建用于搜索和查询相应实体的主键对象外,专用 PK 对象通常更轻巧,并且在序列化时提供更好的性能,尤其是在启用二级缓存的情况下。