54

I have three classes one of the names is User and this user has other classes instances. Like this;

public class User{
    @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
    public List<APost> aPosts;

    @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL)
    public List<BPost> bPosts;
}




   public class BPost extends Post {
    
    @ManyToOne(fetch=FetchType.LAZY)    
    public User user;
 }
    
    public class APost extends Post {
    
     @ManyToOne(fetch=FetchType.LAZY) 
     public User user;
 }

it's working like this but generates empty tables in DB. Which have to contain foreign keys. When I tried to use mappedBy and JoinColumn annotations I got failed. How can I resolve this?

Extra information:

When I changed with;

 @ManyToOne(fetch=FetchType.LAZY)
 @JoinColumn(name="id")
 public User user;

and

 @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="id")
 public List<APost> aPosts;

I'm getting

A JPA error occurred (Unable to build EntityManagerFactory): Repeated column in mapping for entity: models.post.APost column: id (should be mapped with insert="false" update="false")

Final Edit: Finally, I was totally wrong about JPA annotations. :( When I change

@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="id")

to

@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="user")

and

@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="user_id")

everything works ok. :)

Perkone
  • 57
  • 3
  • 8
Ömer Faruk AK
  • 2,409
  • 5
  • 26
  • 47
  • How exactly did you fail? What are you seeking help with, precisely? And if the first solution worked for you why not stick with it? – Perception Dec 11 '12 at 00:49
  • because jpa creates User_APost and User_BPost tables but it's not adding any row to this tables. And it feels me wrong. – Ömer Faruk AK Dec 11 '12 at 07:07

2 Answers2

66

I am not really sure about your question (the meaning of "empty table" etc, or how mappedBy and JoinColumn were not working).

I think you were trying to do a bi-directional relationships.

First, you need to decide which side "owns" the relationship. Hibernate is going to setup the relationship base on that side. For example, assume I make the Post side own the relationship (I am simplifying your example, just to keep things in point), the mapping will look like:

(Wish the syntax is correct. I am writing them just by memory. However the idea should be fine)

public class User{
    @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="user")
    private List<Post> posts;
}


public class Post {
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="user_id")
    private User user;
}

By doing so, the table for Post will have a column user_id which store the relationship. Hibernate is getting the relationship by the user in Post (Instead of posts in User. You will notice the difference if you have Post's user but missing User's posts).

You have mentioned mappedBy and JoinColumn is not working. However, I believe this is in fact the correct way. Please tell if this approach is not working for you, and give us a bit more info on the problem. I believe the problem is due to something else.


Edit:

Just a bit extra information on the use of mappedBy as it is usually confusing at first. In mappedBy, we put the "property name" in the opposite side of the bidirectional relationship, not table column name.

Adrian Shum
  • 38,812
  • 10
  • 83
  • 131
  • i have add extra information to post adrian. It contains errors when i changed my annotations with your suggestions. – Ömer Faruk AK Dec 11 '12 at 07:59
  • sorry bro, i couldn't understand your answer at first. :) – Ömer Faruk AK Dec 11 '12 at 14:35
  • Hi @Adrian, I am not sure how this answered his question. However, I have the same problem. i.e In your solution User has only list of "Post" but in his case he has class "APost" and "bPost" and both of this post classes refer to User. So can please modify your answer by showing to different classes referencing User and User having ManyToOne r/s with those classes? Thanks. – WowBow Sep 23 '15 at 23:47
  • 2
    @WowBow it is simply because his problem is solely related to the way to do bi-directional mapping, and have nothing to do with multiple one-to-many relationship. Won't update the answer as I want to keep the answer away from unnecessary distraction unrelated to problem. If you have other problem, feel free to raise it as another question. – Adrian Shum Sep 24 '15 at 00:05
  • 2
    @WowBow if your question is about how to have 2 sub-class, and you want to have method in `User` to get corresponding `aPosts` and `bPosts`, there are lots of ways. My personal preference is to make it simple: have `User` having one-to-many relationship to `Post`, and provide getters for `getAPosts()` and `getBPosts()` simply by run-time filtering the internal `Collection`. You can also do it like OP, while treating APost and BPost as totally different entity and maintain its own `user` relationship (There is nothing more than defining 2 OneToMany relationship using described way). – Adrian Shum Sep 24 '15 at 01:16
  • Thank you sir. That's what I was looking for ;) – WowBow Sep 24 '15 at 17:51
  • @JoinColumn("user_id") > Not working >> @JoinColumn(name="user_id") – Khaled Lela Nov 28 '15 at 21:04
  • @KhaledLela thanks for pointing that out. Fixing it. I think should be easy to figure out though :) – Adrian Shum Nov 30 '15 at 01:27
33

You should never use the unidirectional @OneToMany annotation because:

  1. It generates inefficient SQL statements
  2. It creates an extra table which increases the memory footprint of your DB indexes

Now, in your first example, both sides are owning the association, and this is bad.

While the @JoinColumn would let the @OneToMany side in charge of the association, it's definitely not the best choice. Therefore, always use the mappedBy attribute on the @OneToMany side.

public class User{
    @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="user")
    public List<APost> aPosts;

    @OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="user")
    public List<BPost> bPosts;
}

public class BPost extends Post {

    @ManyToOne(fetch=FetchType.LAZY)    
    public User user;
}

public class APost extends Post {

     @ManyToOne(fetch=FetchType.LAZY) 
     public User user;
}
Vlad Mihalcea
  • 142,745
  • 71
  • 566
  • 911
  • I have been trying this. However, I keep getting a stack overflow due to the obvious circular reference (User has list of APost, which has the user, which has a list of APost, ... Stack Overflow error). Any pointers on that? – Roger Worrell Sep 12 '17 at 18:53
  • The only way to get a StackOverflow is by calling from the child a method from the parent which in turn calls the same method in the child.However, that's not due to the mapping I proposed you. – Vlad Mihalcea Sep 12 '17 at 19:07
  • Thanks Vlad. I just found https://stackoverflow.com/questions/17445657/hibernate-onetomany-java-lang-stackoverflowerror which seems to indicate it's the serialization of the object to JSON that is causing the problem. – Roger Worrell Sep 12 '17 at 20:35