views:

197

answers:

2

Hello,
I am not sure what I am missing to make a bidirectional onetomany relationship (hibernate engine). A scaled down version of the domain model:

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

class Relationship {
@ManyToOne
  public Person personFrom;

  @ManyToOne
  public Person personTo;
}

Some of the observations:
1. with the above mapping, there is no join table created.
2. When I remove the mappedBy (@OneToMany(cascade = CascadeType.PERSIST) ), the join table is created and i could persist Relationship through Person. "personFrom" field is empty, but I think that is normal as the relation is maintained through the join table.

I also tried by specifying join column at Relationship, didn't make any difference. Any help, highly appreciated. thanks.

Edit:1

As per Dan's comment, if it matters to see the full content of the domain class, I have expanded them below.

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;
}

Role also related to Person, but I think keeping the personFrom, personTo helps to optimize my queries.

Role extends Model {

    @ManyToOne
    public RoleType roleType;

    @ManyToOne
    public Person createdBy; 
}
A: 

Did you specify the foreign key column name as the name of your join column? Assuming the foreign key column is named PERSON_ID, the code should look something like this:

class Relationship {
    @ManyToOne
    @JoinColumn(name="PERSON_ID")
    public Person personFrom;
    ...
}
reverendgreen
It didn't make any difference.. all the colum except person_id is filled up correctly.. but, there are no join table.
bsreekanth
+1  A: 

with the above mapping, there is no join table created.

A join table is not required for a OneToMany, you'll get foreign key column in the Many side. And this is what I get when using your code:

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

Which is the expected result (at least for me). Maybe what you actually want is a ManyToMany.

When I remove the mappedBy (@OneToMany(cascade = CascadeType.PERSIST) ), the join table is created and i could persist Relationship through Person. "personFrom" field is empty, but I think that is normal as the relation is maintained through the join table.

I wrote a small unit test using the provided code (with Hibernate's API but this doesn't change anything) and I don't get what the problem is (the session is created before the test method and the method runs inside a transaction):

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

The above code results in two insert in the Person table and one insert in the Relationship table (valuing the 3 columns).

As I said, I don't get the problem. Maybe you should explain what the expected result is (both the relational model and the queries).

Update: To be totally clear, when working with bi-directional associations, you have to set both sides of the link and a common pattern is to use defensive link management methods to correctly set both sides of the association. Something like this:

public void addToRelationships(Relationship relationship) {
    if (this.relationships == null) {
       this.relationships = new ArrayList<Relationship>();
    }
    this.relationships.add(relationship);
    relationship.setPersonFrom(this);
}

This is detailed in the section 1.2.6. Working bi-directional links of the Hibernate documentation.

Pascal Thivent
I can't thank you enough, as you are saving me the second time.. I had the impression that the owning entity (Person) manages the mapped relation, so I wan't setting it explicitly. line: r.setPersonFrom(p1); Your test case made it clear how to persist the entity correctly thanks. I have a similar qn about unidirectional one-to-many, which I will ask in a seperate qn.. thanks again for ur time, and help.
bsreekanth
@bsreekanth: You're welcome :)
Pascal Thivent
Pascal Thivent .. I read more about JPA oneToOne mapping, and got cleared from the doubts.. I have another doubt, related to yaml syntax for bidirectional mapping.. please see it here..http://stackoverflow.com/questions/3349545/bidirectional-relationship-in-yaml your expertise may help me. thanks.
bsreekanth