Should #JoinTable be specified in both sides of a #ManyToMany relationship? - java

I've an entity Course and an entity User. There's a many-to-many relation ship between course and user, since a course can have many users and a user can be enrolled in many courses. In both entities I've put the #ManyToMany annotation on the specific field, that is, in Course I have:
#ManyToMany
private List<RegisteredUser> members;
and in User I have:
#ManyToMany
private List<Course> coursesTaken;
Now, I know that this kind of many-to-many relationships are usually represented by a third table. I also know that there's the annotation #JoinTable which allows us to do that. What I don't know is if I should add this annotation #JoinTable over both fields in the two different entities or not. By the way, if I need to add to both, the names need to match right?

It's actually a good question, and it helps to understand the concept of an "owning" entity because neither side needs a #JoinTable annotation. If you want to prevent both sides from having join tables, a good idea, then you need to have a mappedBy= element on one side. The #JoinTable annotation is used to either specify the table name, or the columns that map the association.
First look at the Javadoc for #JoinTable:
Specifies the mapping of associations. It is applied to the owning side of an association.
Whether or not there is a join table is controlled by the mappedBy="name" element of the #ManyToMany annotation. The Javadoc for mappedBy for the ManyToMany annotation says:
The field that owns the relationship. Required unless the relationship is unidirectional.
For your (bidirectional) example in Hibernate (5.0.9.Final), if there were only two #ManyToMany annotations and no mappedBy= element, the default will have two Entity tables and two Join Tables:
Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Course_Member (Course_id bigint not null, members_id bigint not null, primary key (Course_id, members_id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (Member_id bigint not null, courses_id bigint not null, primary key (Member_id, courses_id))
While this is saying that each Entity "owns" its ManyToMany relationship, the extra join table is redundant in the typical use case. However, if I decide to have the Member entity "own" the relationship, then I add the mappedBy= element to the Course entity to specify that it doesn't own the relationship:
#ManyToMany(mappedBy="courses")
Set<Member> members;
Adding #JoinTable(name="Member_Course") to the Member entity doesn't change anything: it's only naming the table the same as it would have been named anyway.
Since the Course entity no longer owns its ManyToMany relationship, the extra JoinTable will not be created:
Hibernate: create table Course (id bigint not null, primary key (id))
Hibernate: create table Member (id bigint not null, primary key (id))
Hibernate: create table Member_Course (members_id bigint not null, courses_id bigint not null, primary key (members_id, courses_id))
This is important to the developer because he or she must understand that no relationship is persisted unless it's added to the owning entity, in this case the Member entity. However, since this a bidirectional relationship, the developer should be adding both a Course to Member.courses and a Member to Course.members anyway.
So, if you have a bidirectional ManyToMany relationship, which means you have ManyToMany on both entities involved, then you should add a mappedBy="name" on one of them to avoid having a redundant join table. Since it's bidirectional, I don't think it matters which side you make the owning entity. As always, it's always a good idea to enable the sql logs and see what's going on in the database:
References:
What is the difference between Unidirectional and Bidirectional associations?.
What does relationship owner means in bidirectional relationship?.
What is the “owning side” in an ORM mapping?.
Most efficient way to prevent an infinite recursion in toString()?.

You actually CAN use #JoinTable on both sides and often it makes perfect sense! I am talking out of experience after I had been looking for this solution for weeks.
Even though all throughout the internet, blogs and articles tell a different story - and the Javadoc of JPA is easily misunderstood (or wrong) in this way. I tried it after seeing this uncommented example in a book for professionals - and it worked.
How to do it:
Singer-Instrument-Association:
Singer side:
#ManyToMany
#JoinTable(name = "singer_instrument", joinColumns =
#JoinColumn(name = "SINGER_ID"), inverseJoinColumns = #JoinColumn(name = "INSTRUMENT_ID"))
public Set<Instrument> instruments;
And exactly the same on the other side!
Instrument side:
#ManyToMany
#JoinTable(name = "singer_instrument",
joinColumns = #JoinColumn(name = "INSTRUMENT_ID"),
inverseJoinColumns = #JoinColumn(name = "SINGER_ID"))
public Set<Singer> singers;
So, if you address the same join table, "singer_instrument", with the same name, it work's.
If you address one join table "singer_instrument" and one join table "instrument-singer" though, it will indead result in two different join tables in the database.
This makes a lot of sense, because a many-to-many relationship has no owning side - seen from the database perspective. Owning side means the side, that owns the foreign key of the relationship. But neither the table "singer" nor "instrument" have a foreign key referring to each other. The foreign keys are inside the neccessary join table between them.
The advantage of #JoinTable on both sides of the relation:
Let's say, a singer starts to learn a new instrument: You can add the instrument to singer (and vise versa, as it is bidirectional) and update/merge the singer. The update will update only the singer and the join table. It won't touch the instrument-table.
Now the other case - a guitar-course has ended, so you want to remove the connection between the guitar and the former course-participants/singers: After removing the instrument "guitar" from the singers (and vise versa!), you update/merge the instrument. The update will update only the instrument and the join table. It won't touch the singer-table.
If you had #JoinTable only on one side, you would always have to update/save/delete this side to safely handle the entries in the join table (the relationships between singers and instruments). In this case, you would have to update each singer, who ended the guitar course. That is not reflecting the type of relationship properly and can cause performance issues and conflicts during data transaction.

Nope. Both sides get #ManyToMany, but only one has the #JoinTable
More ManyToMany info here

For #ManyToMany to work on an an existing schema (not made by Hibernate) you will have to use the #JoinTable annotation on both classes to specify the table and which columns map to Java member variables in the appropriate class. I think this example may help you with what properties should be passed to the annotation:
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

Related

Issue about JPA One to One Lazy Loading

I know that lazy loading can't be done if you have a two-way relationship in a one-to-one relationship.
So I read about How can I make a JPA OneToOne relation lazy.
The article says that you can't create a proxy in a one-to-one relationship, but I can't quite understand it.
I think There is no difference between a many-to-one relationship and a one-to-one relationship. He says that in a many-to-one relationship you can create a proxy because you can get the value from FK.
However, one-to-one relationships can also get values ​​with FK, there is no explanation as to why exactly one-to-one relationships can't only get values.
Can someone please explain clearly?
// one to one
class Address {
..
#OnetoOne(fetch = LAZY)
#JoinColumn(~)
private Order order;
}
class Order {
..
#OnetoOne(mappedBy = order, fetch = LAZY)
private Address address; // LAZY not wokring
}
// ManyToOne
public class Member {
#ManyToOne(fetch = LAZY)
#JoinColmn(~)
public Team team; // LAZY working!
}
I think these two are different. I know the PK values ​​equally, so why doesn't only the one-to-one relationship do lazy loading?
Address and Member are the same. They own the column representing the association. Let's say, for example, the column team_id in the table Member and the column order_id in the table Address.
When Hibernate ORM reads a row from the table Member, by checking the value of the team_id column, it knows if that value is null or if there is an association. Therefore, when the entity is created it can decide if the value mapping the association (Mapping#team)is null or a proxy. In this case lazy fetching is possible.
The table Order doesn't have a column representing the association, that's because the association is mapped by the column order_id in Address. When Hibernate ORM needs to convert a row in the table Order to an entity, it also needs to check the table Address to see if an association exists. And depending on the result, it will set the value Order#address to null or the associated address. In this case, it cannot know if the value of the association is null or not without checking the table Address: lazy fetching is not possible.
Note that it might still be possible to lazy fetch the association when the mapping states that the association is never null: #OneToOne(mappedBy="order", optional=false).
Basically, the problem is that if a value can be mapped to null, Hibernate ORM needs to check first if this value is actually null because, otherwise, code like if (order.address == null) { is not going to work if a proxy is created.
Empty *-to-many associations are different in this regard because Hibernate ORM maps them with an empty collection (and never with null) and this means that it can always return a proxy.

What is better, using #manytomany or create entity for intermediate table?

It is true that using the relationship manytomany is easier than presenting the intermediate table by an entity but I don't like this annotation for many raisons:
It's impossible to add a new columns in my intermediate table
matching the intermediate table by entity looks more close to the reality than using #ManyToMany
What do you think ?
It's a case to case basis.
1) There are occasions when a relationship actually has "state" associated with it. In the case of many-to-many relationship, if you want to add a state/property in your relationship, then you will need turn your relationship into a separate entity. Hence, this will result in new column(s) in your join table besides the foreign keys.
This requires updating your mappings. Here is a good example:
http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/
Technically, we are not using the #ManyToMany mapping here but logically it is many-to-many relationship. The new entity (relationship entity) will have a many-to-one relationship to each of the existing entity types, and each of the entity types will have a one-to-many relationship back to the new entity representing the relationship
2) If maintaining a relationship "state" as mentioned in point #1 is not necessary, then a simple use of #ManyToMany association mapping should suffice. It eliminates the need to maintain/manage a separate entity just to represent the relationship. Otherwise, a separate entity will just complicate your object model.

Hibernate One To Many Mapping With Fixed Values

I don't have a lot of experience with database design and i try to understand the general logic behind it with using an ORM like hibernate. I have two tables user and languages. User could know one or more languages so there is a one to many relation between two tables. But i have a fixed length of languages English , Spanish and French for example. As i understand with each new user instance persisted there will be duplicate entries in the language table with a foreign key of that person. Is there a way to prevent this duplicate entries ?
Your understanding is a little confused. You can map a OneToMany relationship using a Foreign Key, and there are good database reasons to do so, though generally a JPA provider recommends against it. However, you are describing a ManyToMany relationship. A User will (or could) have many Languages. A Language will have many Users. When you create a many to many relationship with annotations:
#Entity
public class Person {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#ManyToMany
private List<Language> languages;
and
#Entity
public class Language {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
The JPA provider will create an association or Join Table, with an id from each Entity in it:
create table Person_Language (Person_id bigint not null, languages_id bigint not null)
When you create a language, an entry will be put in the language table. When you create a user, an entry will be put into the user table. When you add a language to a person's languages then an entry will be put into the Join Table.
insert into Person_Language (Person_id, languages_id) values (?, ?)
There will be only unique combinations of Person_id and languages_id in the join table, and so the database will be well normalized. You would not be able to assign a language to multiple users using a Foreign Key in the Language entity for the reason you pointed out: there would be only one foreign key column for any given language.

ManyToOne relation without list/set using JPA

I have new JPA project using hibernate and I run into a difficulty while reading its code. I saw:
#Entity
public class Product {
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Manufacturer manufacturer;
...
}
and another Entity
#Entity
public class Manufacturer{
private ManufacturerId manufacturerId;
private String name;
private Manufacturer() {
}
...
Why there is not List/Set with Product(s) in the Manufacturer Entity class? ManyToOne relationship is Bidirectional? Why is this possible? How Manufacturer knows about its products, how this will be persisted on the DB table?
The many-side of an one to many association is optional. You can implement it if intended or skip it if not needed or even risky. A manufacturer could have many thousand of products. Than it makes no sense to fetch all of them at once. It's better to load via a query and use paging. Of course you could add the collection of products to your Manufacturer if you think this helps you.
Why there is not List/Set with Product(s) in the Manufacturer Entity class?
Either because not needed or as considered risky.
ManyToOne relationship is Bidirectional?
Of course yes. Even if the relationship is not impletemented it still exists.
Why is this possible? How this will be persisted on the DB table?
A OneToMany relationship is always implemented by an id on the one side. (ManufacturerId in Products in this case. Nothing else is needed. Even if you implement the products collection. This will not impact the way it's persisted.
How Manufacturer knows about its products?
It doesn't. But of course it's possible to query the database.
If you look at it at DB level, table Product will have something like manufacturer_id which is a foreign key to Manufacturer table. The table structure remains the same in both unidirectional and bidirectional mapping case.
Manufacturer will know its product by querying table Product with manufacturer_id = <its id>. On JPA level, in case of unidirectional mapping you could query it by from Product p where p.manufacturer.id = :man_id. In case of bidirectional mapping you could just do manufacturer.getProducts(), but it would translate to the same SQL.

Understanding many to many mapping in hibernate

I am referring to the example at - http://viralpatel.net/blogs/hibernate-many-to-many-annotation-mapping-tutorial/ Employees can attend many Meetings and Meetings
can be attended by many Employees.
Let the owning class be Employee. The many to many mapping code inside Employee will be -
#ManyToMany(cascade = {CascadeType.ALL})
#JoinTable(name="EMPLOYEE_MEETING",
joinColumns={#JoinColumn(name="EMPLOYEE_ID")},
inverseJoinColumns={#JoinColumn(name="MEETING_ID")})
private Set<Meeting> meetings = new HashSet<Meeting>();
I looked at several other examples and documentation, but I am still not sure how this works. I don't think I fully understand what joinColumns and inverseJoinColumns do.
joinColumns={#JoinColumn(name="EMPLOYEE_ID")} - Does this line only tell hibernate
that Employee id of employee joins employee id of the mapping table ?
inverseJoinColumns={#JoinColumn(name="MEETING_ID")}) ? Does this line only tell hibernate to join Employee id to the Meeting_Id column in the link table ?
What the actual annotation is saying is that Hibernate should create an intermediate join table. One column of this table should map to the column on your Employee table called Employee_ID (that's your joinColumns parameter), and the other column should map to the column on your Meeting table called Meeting_ID (that's your inverseJoinColumns parameter).
The joinColumns parameter should specify the PK of the owning entity (in this case your Employee object), while the inverseJoinColumn specifies the PK of the non-owning entity.
Note that you don't always have to just specify your join columns as PK's, but it makes sense because uniqueness is enforced and its kind of the point of a PK. You can also have multiple join columns forming a composite key on either side of the relationship within the join table.
You seem to have a handle on how the relationships are set up but I'll say this for the benefit of people coming here from Google. In a bidirectional relationship you have to specify which side of the relationship 'owns' the relationship. While you can have a bidirectional cascade as well, you usually only access the non-owning object through the owning one. So you don't generally access a Meeting object directly, you access it through a Employee object. This becomes more important in a uni-directional relationship but it can cause a few problems in a bidirectional relationship in determining what you want to cascade.
For example, if you decide that if you delete a Meeting, you don't want the deletion to cascade to the join table, you need to change your CascadeType in your Meeting class.
If you want me to expand on my explanation please let me know.

Categories