Understanding many to many mapping in hibernate - java

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.

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.

When we using relationship One To Many two side and when we using it one side

I have read some code describing relationship of two entities on both sides which look like following:
public class Department {
#OneToMany(mappedBy = "department",fetch = FetchType.EAGER , cascade = CascadeType.ALL)
private List<Course> courses = new ArrayList<>();
}
public class Course {
#ManyToOne
private Department department;
}
There are two scenarios: When I use relationship annotation on both sides("on both tables Department and Course ") with One-To-Many relationship and When i only use on one side("only table Derpartment). Scenario is similar for Many-To-Many relationship as well.
My question: Should "fetch = FetchType.EAGER , cascade = CascadeType.ALL" be defined only on one side or both sides in the above mentioned scenarios ?
fetch and cascade options can be defined on both sides. If its defined only on one side that it won't have any impact when the other side object is fetched. e.g. If eager fetch is set for courses in Department class but not in Course class then, if a select query is made on department then, it will fetch all its courses along with it But if a select query is made on course then, it won't fetch its associated department unless explicitly called out in query.
Same goes for cascade option. Thus, its definition on either side depends on the kind of queries which are required to be made. If there are going to be a lot of queries on department which needs all the courses information every time but its not the same for fetching a course then, fetch option should be defined only in Department class for courses.
Bi-directional association is good but with additional update in your code for efficient queries i.e. use JoinColumn with #ManyToOne association so that additional association mapping information between two entities doesn't have to be maintained on code side.

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

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

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.

Additional queries in JPA

I have two classes InvitedPerson and Flight with a one to one relationship with each other. Here is how they are annotated.
public class InvitedTech{
...
#OneToOne(mappedBy="invitedTech", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Flight flight;
#OneToOne(mappedBy="invitedTech", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Hotel hotel;
...
}
public class Flight{
...
#OneToOne
#JoinColumn(name="invitedTechId", nullable=false)
public InvitedTech invitedTech;
...
}
As you can see Flight is the owner of the relationship and InvitedTech is the other side of this bidirectional relationship. InvitedTech also has a OneToOne relationship with Hotel Now, when I write a simple query to fetch all flights, it triggers three queries in total. 1st which gets me the results, but fires 2 additional queries.
List<Flight> flg = JPA.em().createQuery("SELECT flg from Flight flg").getResultList();
Query that gets all flights (This is the only one that I need)
Query with a join between InvitedTech and Flight
Query with a join between invitedTech and Hotel
Why are query 2&3 being executed even though I have set FetchType=Lazy. I am not accessing Hotel Information. And Flight should not be queries again as the first query returns the data.
After some playing around when I remove mappedBy attribute from both the annotations, those 2 addition queries don't get executed(i.e only 1st gets executed).
Why does the mappedBy attribute cause additional queries to be executed even though FetchType=Lazy. Is there a way to stop this?
I believe this is due to one of Hibernate's idiosyncrasies:
non-optional one-to-one relationships are eagerly loaded regardless of whether they are mapped as Lazy.
The reasoning behind this is that as the engine has to look in the association table anyway - to determine whether it should set the association as a proxy or as null - then it may as well load the associated entity anyway.
I have experienced this myself and as far as I know the only way round it is to mark the relationship with optional=false which tells Hibernate it can always set a proxy.
If the relationship is optional then the only other option seems to be byte code instrumentation.
See also:
https://community.jboss.org/wiki/SomeExplanationsOnLazyLoadingone-to-one
Making a OneToOne-relation lazy
You have not set the association from Flight to InvitedTech lazy. So it loads the InvitedTech associated with the flight.
Since it can't know from the InvitedTech if there exist a Hotel and a Flight associated with the InvitedTech, it can't decide if these fields should be null or should be proxies. So it's forced to execute additional queries to know if a hotel/flight exists for the InvitedTech.

Categories