Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate (JPA) multiple @OneToMany for same model

I have two models.

@Entity
public class Student
{
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    protected long id;

    @?
    protected Address homeAddress;

    @?
    protected Address schoolAddress;
}

@Entity
public class Address
{
   @Id
   @GeneratedValue(strategy=GenerationType.AUTO)
   protected long id;

   @?
   protected List<Student> students;
}

What JPA/hibernate annotations do I need to put above homeAddress, schoolAddress and students to make the association work?

Of course I've tried many things and nothing worked. For example, setting

    @ManyToOne
    @JoinColumn (name="student_homeAddress_id", updatable = false, insertable = false)
    protected Address homeAddress;

    @ManyToOne
    @JoinColumn (name="student_schoolAddress_id", updatable = false, insertable = false)
    protected Address schoolAddress;

and

    @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.EAGER)
    @JoinColumns({
    @JoinColumn(name = "student_homeAddress_id", referencedColumnName = "student_homeAddress_id"),
    @JoinColumn(name = "student_schoolAddress_id", referencedColumnName = "student_schoolAddress_id")})
    @IndexColumn(name="students_index")
    protected List<Student> students;

yealds Unable to find column with logical name: student_homeAddress_id in org.hibernate.mapping.Table(Address) and its related supertables and secondary tables. Also tried using mappedBy but that takes a single argument (can't do mappedBy="student_homeAddress_id, student_schoolAddress_id".

Also looked into moving the JoinColumns to the Student tablet but I am not sure what the annotations should look like for OneToMany and ManyToOne as I have multiple Addresses there which JoinColumns doesn't make much sense.

The thing that did work but was not creating the associations was having:

    @OneToMany(cascade={CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH}, fetch = FetchType.EAGER)
    @JoinColumn(name="address_id")
    @IndexColumn(name="students_index")
    protected List<Student> students;

Using this, when storing in the DB the models, the student_homeAddress_id and student_schoolAddress_id were always null even after storing both ends (the Student and Address model).

My thought is that on the Address table there will be 3 extra columns: student_homeAddress_id (the id of the Student in the Student table for the homeAddress), student_schoolAddress_id (the id of the Student in the Student table for the schoolAddress) and students_index (the 0-based location on the students list). That should suffice, correct?

Any ideas?

Thanks a lot!

like image 624
iliask Avatar asked Oct 12 '12 14:10

iliask


2 Answers

We tried mael's suggestion but we couldn't make it work.

We ended up following this which worked.

In other words, we have OneToMany relations:

On Student:

protected List<AddressStudentAssociation> addresses;

On Address:

protected List<AddressStudentAssociation> students;

And on AddressStudentAssociation:

@ManyToOne
@PrimaryKeyJoinColumn(name="STUDENTID", referencedColumnName="id")
private Student student;
@ManyToOne
@PrimaryKeyJoinColumn(name="ADDRESSID", referencedColumnName="id")
private Address address;

plus an argument to separate the one address from the other (isHome).

Finally, inside Student we have public Address getHomeAddress() which traverses the addresses list and returns the correct address. We also had to play with the annotations to make it work. Not optimal in general but it works and we already spent too much time trying to make things work. :|

like image 67
iliask Avatar answered Oct 13 '22 00:10

iliask


If you have an entity which you want to relate @OneToMany in more than one field you should use @JoinTable so Hibernate can generate 2 tables for the relationship.

From the @JoinTable javadoc:

A join table is typically used in the mapping of many-to-many and unidirectional one-to-many associations. It may also be used to map bidirectional many-to-one/one-to-many associations, unidirectional many-to-one relationships, and one-to-one associations (both bidirectional and unidirectional).

When a join table is used in mapping a relationship with an embeddable class on the owning side of the relationship, the containing entity rather than the embeddable class is considered the owner of the relationship.

If the JoinTable annotation is missing, the default values of the annotation elements apply. The name of the join table is assumed to be the table names of the associated primary tables concatenated together (owning side first) using an underscore.

Take this example:

@Entity
@Indexed(index = "causa_penal")
@Table(name = "causas_penales")
public class CausaPenal implements Serializable {



     @Id
     @GeneratedValue(strategy = GenerationType.TABLE)
     @Column(name = "id")
     @DocumentId
     private Integer id;

     @Version
     @Column(name = "opt_lock")
     private Integer version;

     @ManyToMany(cascade = { CascadeType.ALL })
     @JoinTable(name = "causapenal_imputados")
     @IndexedEmbedded(depth = 1)
     private List<ParteMaterial> imputados;

     @ManyToMany(cascade = CascadeType.ALL)
     @JoinTable(name = "causapenal_victimas")
     @IndexedEmbedded(depth = 1)
     private List<ParteMaterial> victimas;

    //Getters and Setters
}

As you can see, I have CausaPenal pointing to ParteMaterial twice. But I need those list to be independent of each other. So with @JoinTable I tell Hibernate that this relationship must be mapped with 4 tables: "causa_penal" for the CausaPenal entity, "causa_penal_imputados" for the relationship of CausaPenal and the imputados field which is mapped to a ParteMaterial Entity and the same for victimas and finally, the table for ParteMaterial Entity.

like image 4
ElderMael Avatar answered Oct 13 '22 02:10

ElderMael