@OneToMany Relationship with Java, Hibernate and Annotations

Nico Heid's picture

NOTE: There is a follow up here: Using an EntityManagerFactory with JPA, Hibernate and Wicket which fixes some design flaws.

If you followed the first article on hibernate: Java persistence with Hibernate and Annotations you might be interested in how to create relationship between classes (or sql tables).

Let's pretend we're writing a little blog software. We only need articles and comments. To keep it simple, one article can have many comments. Comments can have no comments.

The database, once filled, should look like this:

mysql> select * from BlogPost;
+-------------+-----------------------------+---------------------+--------------------------+-----------+----------------+
| BLOGPOST_ID | content | date | path | published | title |
+-------------+-----------------------------+---------------------+--------------------------+-----------+----------------+
| 1 | the content blows your mind | 2010-01-12 14:25:24 | /2010/01/a_monster_title | Y | a moster title |
+-------------+-----------------------------+---------------------+--------------------------+-----------+----------------+

mysql> select * from Comment;
+------------+---------------------+----------------------+
| COMMENT_ID | comment | blogPost_BLOGPOST_ID |
+------------+---------------------+----------------------+
| 1 | and a lousy comment | 1 |
+------------+---------------------+----------------------+

Simple enough. Because we're lazy and we've learned a bit since the last article, we let hibernate create the tables for us. Therefor add hibernate.hbm2ddl.auto=create-drop to your hibernate.properties file.

Now we take a look at the two Java classes we need. First the BlogPost:

  1. import java.io.Serializable;
  2. import java.util.Date;
  3. import java.util.Set;
  4.  
  5. import javax.persistence.CascadeType;
  6. import javax.persistence.Column;
  7. import javax.persistence.Entity;
  8. import javax.persistence.FetchType;
  9. import javax.persistence.GeneratedValue;
  10. import javax.persistence.GenerationType;
  11. import javax.persistence.Id;
  12. import javax.persistence.OneToMany;
  13. import javax.persistence.Table;
  14.  
  15. import org.hibernate.annotations.Type;
  16.  
  17. import com.unitedcoders.yawblog.persistence.dao.PersistenceUtil;
  18.  
  19. @Entity
  20. @Table(name = "BlogPost")
  21. public class BlogPost extends PersistenceUtil implements Serializable {
  22.  
  23.         private Long id;
  24.         private String title;
  25.         private String content;
  26.         private Boolean published;
  27.         private Date date;
  28.         private String path;
  29.         private Set<Comment> comments;
  30.        
  31.         @Id
  32.         @GeneratedValue(strategy=GenerationType.AUTO)
  33.         @Column(name="BLOGPOST_ID")
  34.         public Long getId() {
  35.                 return id;
  36.         }
  37.  
  38.         public void setId(Long id) {
  39.                 this.id = id;
  40.         }
  41.  
  42.         @Column
  43.         public String getTitle() {
  44.                 return title;
  45.         }
  46.  
  47.         public void setTitle(String title) {
  48.                 this.title = title;
  49.         }
  50.        
  51.         @Column
  52.         public String getContent() {
  53.                 return content;
  54.         }
  55.  
  56.         public void setContent(String content) {
  57.                 this.content = content;
  58.         }
  59.  
  60.         @Column
  61.         @Type(type="yes_no")
  62.         public Boolean getPublished() {
  63.                 return published;
  64.         }
  65.  
  66.         public void setPublished(Boolean published) {
  67.                 this.published = published;
  68.         }
  69.  
  70.         @Column
  71.         public Date getDate() {
  72.                 return date;
  73.         }
  74.  
  75.         public void setDate(Date date) {
  76.                 this.date = date;
  77.         }
  78.  
  79.         @Column
  80.         public String getPath() {
  81.                 return path;
  82.         }
  83.        
  84.         @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="blogPost")
  85.         public Set<Comment> getComments() {
  86.                 return comments;
  87.         }
  88.  
  89.         public void setComments(Set<Comment> comments) {
  90.                 this.comments = comments;
  91.         }
  92.  
  93.         public void setPath(String path) {
  94.                 this.path = path;
  95.         }
  96.  
  97. }

And the comments:

  1. import java.io.Serializable;
  2.  
  3. import javax.persistence.AssociationOverride;
  4. import javax.persistence.CascadeType;
  5. import javax.persistence.Column;
  6. import javax.persistence.Entity;
  7. import javax.persistence.FetchType;
  8. import javax.persistence.GeneratedValue;
  9. import javax.persistence.GenerationType;
  10. import javax.persistence.Id;
  11. import javax.persistence.JoinColumn;
  12. import javax.persistence.ManyToOne;
  13. import javax.persistence.Table;
  14.  
  15. @Entity
  16. @Table(name = "Comment")
  17. public class Comment extends Serializable {
  18.  
  19.         private Long id;
  20.         private String comment;
  21.         private BlogPost blogPost;
  22.        
  23.        
  24.         @ManyToOne(fetch = FetchType.EAGER)
  25.         public BlogPost getBlogPost() {
  26.                 return blogPost;
  27.         }
  28.  
  29.         public void setBlogPost(BlogPost blogPost) {
  30.                 this.blogPost = blogPost;
  31.         }
  32.  
  33.         @Id
  34.         @GeneratedValue(strategy=GenerationType.AUTO)
  35.         @Column(name="COMMENT_ID")
  36.         public Long getId() {
  37.                 return id;
  38.         }
  39.  
  40.         public void setId(Long id) {
  41.                 this.id = id;
  42.         }
  43.  
  44.         @Column
  45.         public String getComment() {
  46.                 return comment;
  47.         }
  48.  
  49.         public void setComment(String comment) {
  50.                 this.comment = comment;
  51.         }
  52.  
  53. }

As you can see, the mapping magic is just a few annotation. In BlogPost you define the Comments as @OneToMany. Important is the mappedBy, this has to point to the member in the target class (Comment). In the Comment class itself you have to define the @ManyToOne relationship. Because the annotations are directly above the class names, hibernate knows which tables it has to map to each other.

And because we want to try it, here's some sample code for filling the DB

  1.                 // s2 is the hibernate session, however you want to create it
  2.                 BlogPost bp = new BlogPost();
  3.                 bp.setTitle("a moster title");
  4.                 bp.setContent("the content blows your mind");
  5.                 bp.setDate(new Date());
  6.                 bp.setPath("/2010/01/a_monster_title");
  7.                 bp.setPublished(true);
  8.                
  9.                
  10.                 Set set = new HashSet();
  11.                 Comment c = new Comment();
  12.                 c.setComment("and a lousy comment");
  13.                 set.add(c);
  14.                 bp.setComments(set);
  15.                 c.setBlogPost(bp);
  16.                 s2.save(bp);
  17.                 s2.close();

That's it.

The full source and overhead can be found on github: http://github.com/nheid/yawblog
Check it out with: git clone git://github.com/nheid/yawblog.git v0.1.1

Comments

Guderin's picture

Hi Nico!

Thanks for this article. I'm newbie in the world of ORM for Java. During my learning I've seen few times that programmers use both Hibernate and JPA in same class. I can see you've done to. Can you explain me why is that so? Why using javax.persistence and org.hibernate in one class? Sorry if my question is stupid :)

Nico Heid's picture

First of all, using org.hibernate was a mistake. I didn't organize my imports after finishing the class, so there were some leftovers from my trial and error runs.

The JPA is an interface definition. Hibernate implements the Interface but also adds some specific extras.

Therefor there are usually two ways to approach the mapping.

Either you strictly use JPA, which gives you a little less functionality but you can always change your entity manager. E.g. using TopLink instead of Hibernate.
Alternatively you commit yourself to Hibernate, but then you have to change some of your code when you, for whatever reason, move to another OR Mapper.

Hope that helps

Guderin's picture

Yes, helped me a lot. Thank you very much.

Nico Heid's picture

First of all, using org.hibernate was a mistake. I didn't organize my imports after finishing the class, so there were some leftovers from my trial and error runs.

The JPA is an interface definition. Hibernate implements the Interface but also adds some specific extras.

Therefor there are usually two ways to approach the mapping.

Either you strictly use JPA, which gives you a little less functionality but you can always change your entity manager. E.g. using TopLink instead of Hibernate.
Alternatively you commit yourself to Hibernate, but then you have to change some of your code when you, for whatever reason, move to another OR Mapper.

Hope that helps

Dave's picture

Thanks for the article.

One minor quibble...in your BlogPost entity you've declared your entity fields with "public" access, which is considered very bad practice. It opens up the entity state fields to access by any unprotected class and could defeat the implementation of your JPA provider. In fact, I believe that public access if forbidden if you annotate your fields (rather than the property access you've chosen by annotating your getters).

Dave

Nico Heid's picture

That was very careless of me. Of course they should be private, otherwise why bother with all the getters and setters. Will update the example.
Thanks for the feedback.

SMiGL's picture

Helpful post, Thanks

Using an EntityManagerFactory with JPA, Hibernate and Wicket's picture

[...] If you read the previous article "@OneToMany Relationship with Java, Hibernate and Annotations" you might have noticed some imperfections in the persistence layer. On the code side we have been [...]

faiz's picture

how to write the code for creating the map with table or field using hibernate + jpa