JPA(Hibernate)的一对多关系

3

我不确定我缺少什么来建立一个双向的一对多关系(Hibernate引擎)。以下是一个精简版的领域模型:

class Person {
@OneToMany(mappedBy="personFrom", cascade = CascadeType.PERSIST)
    public List<Relationship> relationships;
}

class Relationship {
@ManyToOne
  public Person personFrom;

  @ManyToOne
  public Person personTo;
}

一些观察结果:
1. 使用以上映射,不会创建联接表。
2. 当我移除mappedBy(@OneToMany(cascade = CascadeType.PERSIST))时,联接表被创建并且我可以通过Person持久化Relationship。"personFrom"字段为空,但我认为这是正常的,因为关系是通过联接表维护的。

我也尝试在Relationship中指定联接列,但没有任何区别。非常感谢任何帮助。 谢谢。

编辑:1

根据Dan的评论,如果需要查看域类的完整内容,我已将其展开如下。

class Relationship extends Model{

  @ManyToOne
  public RelationshipType relationshipType;

  @ManyToOne
  public Person personFrom;

  @ManyToOne
  public Person personTo;

  @ManyToOne
  public Person createdBy;

  @ManyToOne
  public Role roleFrom;

  @ManyToOne
  public Role roleTo;

    @Override
    public String toString() {
        return relationshipType.toString();
    }
}

class Person extends Model {

    public Date dateCreated;

    @Lob
    public String description;

    @OneToMany(cascade = CascadeType.ALL)
    public List<Role> roles;

    @OneToMany(mappedBy="personFrom", cascade = CascadeType.PERSIST)
    public List<Relationship> relationships;
}

角色也与人相关,但我认为保留personFrom和personTo有助于优化我的查询。

Role extends Model {

    @ManyToOne
    public RoleType roleType;

    @ManyToOne
    public Person createdBy; 
}

2
Relationship 只有如图所示的两个字段吗?这将使其符合 @JoinTable 简化的条件。 - Dan LaRocque
谢谢Dan,我通过你的评论了解到了@JoinTable,请查看领域模型的更新。 - bsr
2个回答

2
使用上述映射,不需要创建联接表。
对于OneToMany,不需要创建联接表,你将在多方获取外键列。当使用您的代码时,就是这样得到的结果。
create table Person (
    id bigint not null,
    primary key (id)
)

create table Relationship (
    id bigint not null,
    personFrom_id bigint,
    personTo_id bigint,
    primary key (id)
)

alter table Relationship 
    add constraint FK499B69164A731563 
    foreign key (personTo_id) 
    references Person

alter table Relationship 
    add constraint FK499B691698EA8314 
    foreign key (personFrom_id) 
    references Person

这是我期望的结果(至少对我来说)。也许你实际想要的是ManyToMany

当我删除mappedBy (@OneToMany(cascade = CascadeType.PERSIST))时,会创建连接表,并且我可以通过Person持久化Relationship。"personFrom"字段为空,但我认为这是正常的,因为关系是通过连接表维护的。

我使用提供的代码编写了一个小的单元测试(使用Hibernate的API,但这并不影响任何内容),我不知道问题出在哪里(测试方法之前创建了会话,并在事务中运行测试方法):

Person p1 = new Person();
Person p2 = new Person();
Relationship r = new Relationship();

// create the personFrom bi-directional association
r.setPersonFrom(p1);
List<Relationship> relationships = new ArrayList<Relationship>();
relationships.add(r);
p1.setRelationships(relationships); // these four lines should be moved to some 
                                    // link management method (see update below).

// create the personTo uni-directional association
r.setPersonTo(p2);

session.persist(p2);
session.persist(p1);

assertNotNull(p2.getId());
assertNotNull(p1.getId());
assertNotNull(r.getId());

上述代码导致 Person 表中插入了两个记录,同时在 Relationship 表中插入了一个记录(三个列的值)。
我不太理解问题所在。也许您应该解释一下期望得到的结果(包括关系模型和查询)。
更新:为了完全清楚,在处理双向关联时,您必须设置链接的两个方向,并且常见的模式是使用防御性链接管理方法来正确设置关联的两个方向。类似这样的东西:
public void addToRelationships(Relationship relationship) {
    if (this.relationships == null) {
       this.relationships = new ArrayList<Relationship>();
    }
    this.relationships.add(relationship);
    relationship.setPersonFrom(this);
}

这在Hibernate文档的1.2.6章节中详细介绍了双向链接的操作


我无法感谢你的足够,因为你已经第二次帮助了我。我一直以为拥有实体(人)管理映射关系,所以我没有明确设置它。代码行:r.setPersonFrom(p1);你的测试用例清楚地说明了如何正确持久化实体,非常感谢。我有一个类似的问题关于单向一对多,我会在另一个问题中提问。再次感谢你的时间和帮助。 - bsr
Pascal Thivent..我阅读了更多关于JPA oneToOne映射的内容,并消除了疑虑。我有另一个疑问,与yaml双向映射的语法有关。请在这里查看http://stackoverflow.com/questions/3349545/bidirectional-relationship-in-yaml,您的专业知识可能会帮助我。谢谢。 - bsr

0

你是否将外键列的名称指定为连接列的名称?假设外键列的名称为PERSON_ID,则代码应该如下所示:

class Relationship {
    @ManyToOne
    @JoinColumn(name="PERSON_ID")
    public Person personFrom;
    ...
}

除了person_id列之外,所有的列都填写正确,但是没有连接表,这并没有产生任何影响。 - bsr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接