Ok so this is probably a trivial question but I\'m having trouble visualizing and understanding the differences and when to use each. I\'m also a little unclear as to how co
This is a very common question, so this answer is based on this article I wrote on my blog.
The one-to-many table relationship looks like this:
In a relational database system, a one-to-many table relationship associates two tables based on a Foreign Key
column in the child table referencing the Primary Key
of one record in the parent table.
In the table diagram above, the post_id
column in the post_comment
table has a Foreign Key
relationship with the post
table id Primary Key
column:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
In JPA, the best way to map the one-to-many table relationship is to use the @ManyToOne
annotation.
In our case, the PostComment
child entity maps the post_id
Foreign Key column using the @ManyToOne
annotation:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
@OneToMany
annotationJust because you have the option of using the @OneToMany
annotation, it doesn't mean it should be the default option for all the one-to-many database relationships.
The problem with JPA collections is that we can only use them when their element count is rather low.
The best way to map a @OneToMany
association is to rely on the @ManyToOne
side to propagate all entity state changes:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
The parent Post
entity features two utility methods (e.g. addComment
and removeComment
) which are used to synchronize both sides of the bidirectional association.
You should provide these methods whenever you are working with a bidirectional association as, otherwise, you risk very subtle state propagation issues.
The unidirectional @OneToMany
association is to be avoided as it's less efficient than using @ManyToOne
or the bidirectional @OneToMany
association.
For more details about the best way to map the
@OneToMany
relationship with JPA and Hibernate, check out this article.
The one-to-one table relationship looks as follows:
In a relational database system, a one-to-one table relationship links two tables based on a Primary Key
column in the child which is also a Foreign Key
referencing the Primary Key
of the parent table row.
Therefore, we can say that the child table shares the Primary Key
with the parent table.
In the table diagram above, the id
column in the post_details
table has also a Foreign Key
relationship with the post
table id
Primary Key
column:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
@OneToOne
with @MapsId
annotationsThe best way to map a @OneToOne
relationship is to use @MapsId
. This way, you don't even need a bidirectional association since you can always fetch the PostDetails
entity by using the Post
entity identifier.
The mapping looks like this:
@Entity(name = "PostDetails")
@Table(name = "post_details")
public class PostDetails {
@Id
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
}
This way, the id
property serves as both Primary Key and Foreign Key. You'll notice that the @Id
column no longer uses a @GeneratedValue
annotation since the identifier is populated with the identifier of the post
association.
For more details about the best way to map the
@OneToOne
relationship with JPA and Hibernate, check out this article.
The many-to-many table relationship looks as follows:
In a relational database system, a many-to-many table relationship links two parent tables via a child table which contains two Foreign Key
columns referencing the Primary Key
columns of the two parent tables.
In the table diagram above, the post_id
column in the post_tag
table has also a Foreign Key
relationship with the post
table id Primary Key
column:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
And, the tag_id
column in the post_tag
table has a Foreign Key
relationship with the tag
table id Primary Key
column:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
@ManyToMany
mappingThis is how you can map the many-to-many
table relationship with JPA and Hibernate:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
//Getters and setters ommitted for brevity
public void addTag(Tag tag) {
tags.add(tag);
tag.getPosts().add(this);
}
public void removeTag(Tag tag) {
tags.remove(tag);
tag.getPosts().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Post)) return false;
return id != null && id.equals(((Post) o).getId());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
}
@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String name;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
//Getters and setters ommitted for brevity
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
tags
association in the Post
entity only defines the PERSIST
and MERGE
cascade types. As explained in this article, the REMOVE
entity state transition doesn't make any sense for a @ManyToMany
JPA association since it could trigger a chain deletion that would ultimately wipe both sides of the association.Post
entity uses the entity identifier for equality since it lacks any unique business key. As explained in this article, you can use the entity identifier for equality as long as you make sure that it stays consistent across all entity state transitions.Tag
entity has a unique business key which is marked with the Hibernate-specific @NaturalId
annotation. When that's the case, the unique business key is the best candidate for equality checks.mappedBy
attribute of the posts
association in the Tag
entity marks that, in this bidirectional relationship, the Post
entity owns the association. This is needed since only one side can own a relationship, and changes are only propagated to the database from this particular side.Set
is to be preferred, as using a List
with @ManyToMany
is less efficient.For more details about the best way to map the
@ManyToMany
relationship with JPA and Hibernate, check out this article.
this would probably call for a many-to-many relation ship as follows
public class Person{
private Long personId;
@manytomany
private Set skills;
//Getters and setters
}
public class Skill{
private Long skillId;
private String skillName;
@manyToMany(MappedBy="skills,targetClass="Person")
private Set persons; // (people would not be a good convenion)
//Getters and setters
}
you may need to define a joinTable + JoinColumn but it will possible work also without...
One-to-Many: One Person Has Many Skills, a Skill is not reused between Person(s)
Many-to-Many: One Person Has Many Skills, a Skill is reused between Person(s)
In a One-To-Many relationship, one object is the "parent" and one is the "child". The parent controls the existence of the child. In a Many-To-Many, the existence of either type is dependent on something outside the both of them (in the larger application context).
Your subject matter (domain) should dictate whether or not the relationship is One-To-Many or Many-To-Many -- however, I find that making the relationship unidirectional or bidirectional is an engineering decision that trades off memory, processing, performance, etc.
What can be confusing is that a Many-To-Many Bidirectional relationship does not need to be symmetric! That is, a bunch of People could point to a skill, but the skill need not relate back to just those people. Typically it would, but such symmetry is not a requirement. Take love, for example -- it is bi-directional ("I-Love", "Loves-Me"), but often asymmetric ("I love her, but she doesn't love me")!
All of these are well supported by Hibernate and JPA. Just remember that Hibernate or any other ORM doesn't give a hoot about maintaining symmetry when managing bi-directional many-to-many relationships...thats all up to the application.
I would explain that way:
OneToOne - OneToOne relationship
@OneToOne
Person person;
@OneToOne
Nose nose;
OneToMany - ManyToOne relationship
@OneToMany
Shepherd> shepherd;
@ManyToOne
List<Sheep> sheeps;
ManyToMany - ManyToMany relationship
@ManyToMany
List<Traveler> travelers;
@ManyToMany
List<Destination> destinations;
Take a look at this article: Mapping Object Relationships
There are two categories of object relationships that you need to be concerned with when mapping. The first category is based on multiplicity and it includes three types:
*One-to-one relationships. This is a relationship where the maximums of each of its multiplicities is one, an example of which is holds relationship between Employee and Position in Figure 11. An employee holds one and only one position and a position may be held by one employee (some positions go unfilled).
*One-to-many relationships. Also known as a many-to-one relationship, this occurs when the maximum of one multiplicity is one and the other is greater than one. An example is the works in relationship between Employee and Division. An employee works in one division and any given division has one or more employees working in it.
*Many-to-many relationships. This is a relationship where the maximum of both multiplicities is greater than one, an example of which is the assigned relationship between Employee and Task. An employee is assigned one or more tasks and each task is assigned to zero or more employees.
The second category is based on directionality and it contains two types, uni-directional relationships and bi-directional relationships.
*Uni-directional relationships. A uni-directional relationship when an object knows about the object(s) it is related to but the other object(s) do not know of the original object. An example of which is the holds relationship between Employee and Position in Figure 11, indicated by the line with an open arrowhead on it. Employee objects know about the position that they hold, but Position objects do not know which employee holds it (there was no requirement to do so). As you will soon see, uni-directional relationships are easier to implement than bi-directional relationships.
*Bi-directional relationships. A bi-directional relationship exists when the objects on both end of the relationship know of each other, an example of which is the works in relationship between Employee and Division. Employee objects know what division they work in and Division objects know what employees work in them.
First of all, read all the fine print. Note that NHibernate (thus, I assume, Hibernate as well) relational mapping has a funny correspondance with DB and object graph mapping. For example, one-to-one relationships are often implemented as a many-to-one relationship.
Second, before we can tell you how you should write your O/R map, we have to see your DB as well. In particular, can a single Skill be possesses by multiple people? If so, you have a many-to-many relationship; otherwise, it's many-to-one.
Third, I prefer not to implement many-to-many relationships directly, but instead model the "join table" in your domain model--i.e., treat it as an entity, like this:
class PersonSkill
{
Person person;
Skill skill;
}
Then do you see what you have? You have two one-to-many relationships. (In this case, Person may have a collection of PersonSkills, but would not have a collection of Skills.) However, some will prefer to use many-to-many relationship (between Person and Skill); this is controversial.
Fourth, if you do have bidirectional relationships (e.g., not only does Person have a collection of Skills, but also, Skill has a collection of Persons), NHibernate does not enforce bidirectionality in your BL for you; it only understands bidirectionality of the relationships for persistence purposes.
Fifth, many-to-one is much easier to use correctly in NHibernate (and I assume Hibernate) than one-to-many (collection mapping).
Good luck!