我需要建立友谊关系。我有一个友谊类,有两个主键,每个主键都是一个成员类。我收到以下例外:
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;
答案 0 :(得分:1)
您必须在Member
类中添加单独的ID字段,以便映射@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;
}
答案 1 :(得分:1)
如果使用不带标识符类型的多个id属性,那么Member
和Credential
应该实现Serializable
,你的映射是好的,这似乎是Hibernate中的一个错误。< / p>
解决方案1
我设法通过在referencedColumnName
中friend
和requester
Friendship
中声明@Id
@ManyToOne
@JoinColumn(referencedColumnName = "username")
Member requester;
@Id
@ManyToOne
@JoinColumn(referencedColumnName = "username")
Member friend;
来完成这项工作:
Credential
Member
Friendship
这样我们就可以明确地告诉Hibernate复合id引用了哪些列,这样它就不必自己弄清楚了。
解决方案2
解决方案1让我想到了Hibernate中可能导致错误的原因。它似乎受到Hibernate处理实体映射的顺序的某种影响。如果您显式声明了引用的列,那么一切正常,否则看起来Hibernate在构建复合键时不知道有关引用列的所有细节。
因此,我将添加带注释的类的顺序更改为会话工厂配置:
Serializable
然后一切都与原始映射一起使用(在Member
和Credential
中实施Configuration
之后。
我已按编程方式将这些类添加到persistence.xml
类,但我认为通过在hibernate.cfg.xml
或<class>Credential</class>
<class>Member</class>
<class>Friendship</class>
中指定此顺序可以实现相同的效果:
@IdClass
尽管如此,此解决方案仅用于演示目的(您或其他人可以在以后重新排序类而不考虑此问题),因此我建议使用解决方案1.
注意强>
您更了解您的用例,但我个人认为您应该使用@EmbeddedId
或loader.execute();
,因为它们在JPA中已标准化;不带标识符类型的多个id属性是Hibernate特有的功能。除了能够更容易地构造用于搜索和查询相应实体的主键对象之外,专用PK对象通常更轻,并且在序列化时提供更好的性能,尤其是在启用了二级缓存时。
答案 2 :(得分:0)
在Friendship
课程中,请尝试指定@JoinColumn:
@Entity
public class Friendship implements Serializable {
@Id
@ManyToOne
@JoinColumn(name = "username")
Member requester;
...
}
答案 3 :(得分:0)
您错过了Friendship
课程中主键的建模。
例如:
@Embeddable
public class FriendshipPK implements Serializable
{
@Column(name = "requester_id")
protected String requesterId;
@Column(name = "friend_id")
protected String friendId;
}
现在可以按如下方式修改友谊课程:
@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;
}
我稍微更新了会员班:
@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;
}
我从Member类中删除了级联,并首先创建了凭证对象。但是你可以改变这一点。