@OneToMany Relationship with Java, Hibernate and Annotations
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:
- import java.io.Serializable;
- import java.util.Date;
- import java.util.Set;
- import javax.persistence.CascadeType;
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.FetchType;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.OneToMany;
- import javax.persistence.Table;
- import org.hibernate.annotations.Type;
- import com.unitedcoders.yawblog.persistence.dao.PersistenceUtil;
- @Entity
- @Table(name = "BlogPost")
- public class BlogPost extends PersistenceUtil implements Serializable {
- private Long id;
- private String title;
- private String content;
- private Boolean published;
- private Date date;
- private String path;
- private Set<Comment> comments;
- @Id
- @GeneratedValue(strategy=GenerationType.AUTO)
- @Column(name="BLOGPOST_ID")
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- @Column
- public String getTitle() {
- return title;
- }
- public void setTitle(String title) {
- this.title = title;
- }
- @Column
- public String getContent() {
- return content;
- }
- public void setContent(String content) {
- this.content = content;
- }
- @Column
- @Type(type="yes_no")
- public Boolean getPublished() {
- return published;
- }
- public void setPublished(Boolean published) {
- this.published = published;
- }
- @Column
- public Date getDate() {
- return date;
- }
- public void setDate(Date date) {
- this.date = date;
- }
- @Column
- public String getPath() {
- return path;
- }
- @OneToMany(fetch=FetchType.EAGER, cascade = CascadeType.ALL, mappedBy="blogPost")
- public Set<Comment> getComments() {
- return comments;
- }
- public void setComments(Set<Comment> comments) {
- this.comments = comments;
- }
- public void setPath(String path) {
- this.path = path;
- }
- }
And the comments:
- import java.io.Serializable;
- import javax.persistence.AssociationOverride;
- import javax.persistence.CascadeType;
- import javax.persistence.Column;
- import javax.persistence.Entity;
- import javax.persistence.FetchType;
- import javax.persistence.GeneratedValue;
- import javax.persistence.GenerationType;
- import javax.persistence.Id;
- import javax.persistence.JoinColumn;
- import javax.persistence.ManyToOne;
- import javax.persistence.Table;
- @Entity
- @Table(name = "Comment")
- public class Comment extends Serializable {
- private Long id;
- private String comment;
- private BlogPost blogPost;
- @ManyToOne(fetch = FetchType.EAGER)
- public BlogPost getBlogPost() {
- return blogPost;
- }
- public void setBlogPost(BlogPost blogPost) {
- this.blogPost = blogPost;
- }
- @Id
- @GeneratedValue(strategy=GenerationType.AUTO)
- @Column(name="COMMENT_ID")
- public Long getId() {
- return id;
- }
- public void setId(Long id) {
- this.id = id;
- }
- @Column
- public String getComment() {
- return comment;
- }
- public void setComment(String comment) {
- this.comment = comment;
- }
- }
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
- // s2 is the hibernate session, however you want to create it
- BlogPost bp = new BlogPost();
- bp.setTitle("a moster title");
- bp.setContent("the content blows your mind");
- bp.setDate(new Date());
- bp.setPath("/2010/01/a_monster_title");
- bp.setPublished(true);
- Set set = new HashSet();
- Comment c = new Comment();
- c.setComment("and a lousy comment");
- set.add(c);
- bp.setComments(set);
- c.setBlogPost(bp);
- s2.save(bp);
- 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
- Login to post comments
Comments
Guderin (not verified) - Wed, 01/13/2010 - 16:32
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 - Thu, 01/14/2010 - 10:02
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 (not verified) - Thu, 01/14/2010 - 10:55
Yes, helped me a lot. Thank you very much.
Nico Heid - Thu, 01/14/2010 - 10:02
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 (not verified) - Wed, 01/13/2010 - 18:08
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 - Thu, 01/14/2010 - 09:50
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 (not verified) - Thu, 01/14/2010 - 17:12
Helpful post, Thanks
Using an EntityManagerFactory with JPA, Hibernate and Wicket (not verified) - Sun, 02/21/2010 - 19:46
[...] 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 (not verified) - Sun, 02/26/2012 - 04:16
how to write the code for creating the map with table or field using hibernate + jpa