JPA (hibernate) onetomany relation - java

I am not sure what I am missing to make a bidirectional onetomany relationship (hibernate engine). A scaled down version of the domain model:
class Person {
#OneToMany(mappedBy="personFrom", cascade = CascadeType.PERSIST)
public List<Relationship> relationships;
}
class Relationship {
#ManyToOne
public Person personFrom;
#ManyToOne
public Person personTo;
}
Some of the observations:
1. with the above mapping, there is no join table created.
2. When I remove the mappedBy (#OneToMany(cascade = CascadeType.PERSIST) ), the join table is created and i could persist Relationship through Person. "personFrom" field is empty, but I think that is normal as the relation is maintained through the join table.
I also tried by specifying join column at Relationship, didn't make any difference. Any help, highly appreciated.
thanks.
Edit:1
As per Dan's comment, if it matters to see the full content of the domain class, I have expanded them below.
class Relationship extends Model{
#ManyToOne
public RelationshipType relationshipType;
#ManyToOne
public Person personFrom;
#ManyToOne
public Person personTo;
#ManyToOne
public Person createdBy;
#ManyToOne
public Role roleFrom;
#ManyToOne
public Role roleTo;
#Override
public String toString() {
return relationshipType.toString();
}
}
class Person extends Model {
public Date dateCreated;
#Lob
public String description;
#OneToMany(cascade = CascadeType.ALL)
public List<Role> roles;
#OneToMany(mappedBy="personFrom", cascade = CascadeType.PERSIST)
public List<Relationship> relationships;
}
Role also related to Person, but I think keeping the personFrom, personTo helps to optimize my queries.
Role extends Model {
#ManyToOne
public RoleType roleType;
#ManyToOne
public Person createdBy;
}

with the above mapping, there is no join table created.
A join table is not required for a OneToMany, you'll get foreign key column in the Many side. And this is what I get when using your code:
create table Person (
id bigint not null,
primary key (id)
)
create table Relationship (
id bigint not null,
personFrom_id bigint,
personTo_id bigint,
primary key (id)
)
alter table Relationship
add constraint FK499B69164A731563
foreign key (personTo_id)
references Person
alter table Relationship
add constraint FK499B691698EA8314
foreign key (personFrom_id)
references Person
Which is the expected result (at least for me). Maybe what you actually want is a ManyToMany.
When I remove the mappedBy (#OneToMany(cascade = CascadeType.PERSIST) ), the join table is created and i could persist Relationship through Person. "personFrom" field is empty, but I think that is normal as the relation is maintained through the join table.
I wrote a small unit test using the provided code (with Hibernate's API but this doesn't change anything) and I don't get what the problem is (the session is created before the test method and the method runs inside a transaction):
Person p1 = new Person();
Person p2 = new Person();
Relationship r = new Relationship();
// create the personFrom bi-directional association
r.setPersonFrom(p1);
List<Relationship> relationships = new ArrayList<Relationship>();
relationships.add(r);
p1.setRelationships(relationships); // these four lines should be moved to some
// link management method (see update below).
// create the personTo uni-directional association
r.setPersonTo(p2);
session.persist(p2);
session.persist(p1);
assertNotNull(p2.getId());
assertNotNull(p1.getId());
assertNotNull(r.getId());
The above code results in two insert in the Person table and one insert in the Relationship table (valuing the 3 columns).
As I said, I don't get the problem. Maybe you should explain what the expected result is (both the relational model and the queries).
Update: To be totally clear, when working with bi-directional associations, you have to set both sides of the link and a common pattern is to use defensive link management methods to correctly set both sides of the association. Something like this:
public void addToRelationships(Relationship relationship) {
if (this.relationships == null) {
this.relationships = new ArrayList<Relationship>();
}
this.relationships.add(relationship);
relationship.setPersonFrom(this);
}
This is detailed in the section 1.2.6. Working bi-directional links of the Hibernate documentation.

Did you specify the foreign key column name as the name of your join column? Assuming the foreign key column is named PERSON_ID, the code should look something like this:
class Relationship {
#ManyToOne
#JoinColumn(name="PERSON_ID")
public Person personFrom;
...
}

Related

Do I have to have to let Hibernate create table1_table2 schemas or can this be disabled? [duplicate]

What is the difference between:
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
#JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
private List<Branch> branches;
...
}
and
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "companyIdRef")
private List<Branch> branches;
...
}
The annotation #JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the "other" entity. This also means that you can access the other table from the class which you've annotated with "mappedBy" (fully bidirectional relationship).
In particular, for the code in the question the correct annotations would look like this:
#Entity
public class Company {
#OneToMany(mappedBy = "company",
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Branch> branches;
}
#Entity
public class Branch {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "companyId")
private Company company;
}
#JoinColumn could be used on both sides of the relationship. The question was about using #JoinColumn on the #OneToMany side (rare case). And the point here is in physical information duplication (column name) along with not optimized SQL query that will produce some additional UPDATE statements.
According to documentation:
Since many to one are (almost) always the owner side of a bidirectional relationship in the JPA spec, the one to many association is annotated by #OneToMany(mappedBy=...)
#Entity
public class Troop {
#OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop has a bidirectional one to many relationship with Soldier through the troop property. You don't have to (must not) define any physical mapping in the mappedBy side.
To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one #JoinColumn as insertable and updatable to false. This solution is not optimized and will produce some additional UPDATE statements.
#Entity
public class Troop {
#OneToMany
#JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
Unidirectional one-to-many association
If you use the #OneToMany annotation with #JoinColumn, then you have a unidirectional association, like the one between the parent Post entity and the child PostComment in the following diagram:
When using a unidirectional one-to-many association, only the parent side maps the association.
In this example, only the Post entity will define a #OneToMany association to the child PostComment entity:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();
Bidirectional one-to-many association
If you use the #OneToMany with the mappedBy attribute set, you have a bidirectional association. In our case, both the Post entity has a collection of PostComment child entities, and the child PostComment entity has a reference back to the parent Post entity, as illustrated by the following diagram:
In the PostComment entity, the post entity property is mapped as follows:
#ManyToOne(fetch = FetchType.LAZY)
private Post post;
The reason we explicitly set the fetch attribute to FetchType.LAZY is because, by default, all #ManyToOne and #OneToOne associations are fetched eagerly, which can cause N+1 query issues.
In the Post entity, the comments association is mapped as follows:
#OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
The mappedBy attribute of the #OneToMany annotation references the post property in the child PostComment entity, and, this way, Hibernate knows that the bidirectional association is controlled by the #ManyToOne side, which is in charge of managing the Foreign Key column value this table relationship is based on.
For a bidirectional association, you also need to have two utility methods, like addChild and removeChild:
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
These two methods ensure that both sides of the bidirectional association are in sync. Without synchronizing both ends, Hibernate does not guarantee that association state changes will propagate to the database.
Which one to choose?
The unidirectional #OneToMany association does not perform very well, so you should avoid it.
You are better off using the bidirectional #OneToMany which is more efficient.
I disagree with the accepted answer here by Óscar López. That answer is inaccurate!
It is NOT #JoinColumn which indicates that this entity is the owner of the relationship. Instead, it is the #ManyToOne annotation which does this (in his example).
The relationship annotations such as #ManyToOne, #OneToMany and #ManyToMany tell JPA/Hibernate to create a mapping. By default, this is done through a seperate Join Table.
#JoinColumn
The purpose of #JoinColumn is to create a join column if one does
not already exist. If it does, then this annotation can be used to
name the join column.
MappedBy
The purpose of the MappedBy parameter is to instruct JPA: Do NOT
create another join table as the relationship is already being mapped
by the opposite entity of this relationship.
Remember: MappedBy is a property of the relationship annotations whose purpose is to generate a mechanism to relate two entities which by default they do by creating a join table. MappedBy halts that process in one direction.
The entity not using MappedBy is said to be the owner of the relationship because the mechanics of the mapping are dictated within its class through the use of one of the three mapping annotations against the foreign key field. This not only specifies the nature of the mapping but also instructs the creation of a join table. Furthermore, the option to suppress the join table also exists by applying #JoinColumn annotation over the foreign key which keeps it inside the table of the owner entity instead.
So in summary: #JoinColumn either creates a new join column or renames an existing one; whilst the MappedBy parameter works collaboratively with the relationship annotations of the other (child) class in order to create a mapping either through a join table or by creating a foreign key column in the associated table of the owner entity.
To illustrate how MapppedBy works, consider the code below. If MappedBy parameter were to be deleted, then Hibernate would actually create TWO join tables! Why? Because there is a symmetry in many-to-many relationships and Hibernate has no rationale for selecting one direction over the other.
We therefore use MappedBy to tell Hibernate, we have chosen the other entity to dictate the mapping of the relationship between the two entities.
#Entity
public class Driver {
#ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}
#Entity
public class Cars {
#ManyToMany
private List<Drivers> drivers;
}
Adding #JoinColumn(name = "driverID") in the owner class (see below), will prevent the creation of a join table and instead, create a driverID foreign key column in the Cars table to construct a mapping:
#Entity
public class Driver {
#ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}
#Entity
public class Cars {
#ManyToMany
#JoinColumn(name = "driverID")
private List<Drivers> drivers;
}
The annotation mappedBy ideally should always be used in the Parent side (Company class) of the bi directional relationship, in this case it should be in Company class pointing to the member variable 'company' of the Child class (Branch class)
The annotation #JoinColumn is used to specify a mapped column for joining an entity association, this annotation can be used in any class (Parent or Child) but it should ideally be used only in one side (either in parent class or in Child class not in both) here in this case i used it in the Child side (Branch class) of the bi directional relationship indicating the foreign key in the Branch class.
below is the working example :
parent class , Company
#Entity
public class Company {
private int companyId;
private String companyName;
private List<Branch> branches;
#Id
#GeneratedValue
#Column(name="COMPANY_ID")
public int getCompanyId() {
return companyId;
}
public void setCompanyId(int companyId) {
this.companyId = companyId;
}
#Column(name="COMPANY_NAME")
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
#OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
public List<Branch> getBranches() {
return branches;
}
public void setBranches(List<Branch> branches) {
this.branches = branches;
}
}
child class, Branch
#Entity
public class Branch {
private int branchId;
private String branchName;
private Company company;
#Id
#GeneratedValue
#Column(name="BRANCH_ID")
public int getBranchId() {
return branchId;
}
public void setBranchId(int branchId) {
this.branchId = branchId;
}
#Column(name="BRANCH_NAME")
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="COMPANY_ID")
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
I'd just like to add that #JoinColumn does not always have to be related to the physical information location as this answer suggests. You can combine #JoinColumn with #OneToMany even if the parent table has no table data pointing to the child table.
How to define unidirectional OneToMany relationship in JPA
Unidirectional OneToMany, No Inverse ManyToOne, No Join Table
It seems to only be available in JPA 2.x+ though. It's useful for situations where you want the child class to just contain the ID of the parent, not a full on reference.
Let me make it simple.
You can use #JoinColumn on either sides irrespective of mapping.
Let's divide this into three cases.
1) Uni-directional mapping from Branch to Company.
2) Bi-direction mapping from Company to Branch.
3) Only Uni-directional mapping from Company to Branch.
So any use-case will fall under this three categories. So let me explain how to use #JoinColumn and mappedBy.
1) Uni-directional mapping from Branch to Company.
Use JoinColumn in Branch table.
2) Bi-direction mapping from Company to Branch.
Use mappedBy in Company table as describe by #Mykhaylo Adamovych's answer.
3)Uni-directional mapping from Company to Branch.
Just use #JoinColumn in Company table.
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
#JoinColumn(name="courseId")
private List<Branch> branches;
...
}
This says that in based on the foreign key "courseId" mapping in branches table, get me list of all branches. NOTE: you can't fetch company from branch in this case, only uni-directional mapping exist from company to branch.
JPA is a layered API, the different levels have their own annotations. The highest level is the (1) Entity level which describes persistent classes then you have the (2) relational database level which assume the entities are mapped to a relational database and (3) the java model.
Level 1 annotations: #Entity, #Id, #OneToOne, #OneToMany, #ManyToOne, #ManyToMany.
You can introduce persistency in your application using these high level annotations alone. But then you have to create your database according to the assumptions JPA makes. These annotations specify the entity/relationship model.
Level 2 annotations: #Table, #Column, #JoinColumn, ...
Influence the mapping from entities/properties to the relational database tables/columns if you are not satisfied with JPA's defaults or if you need to map to an existing database. These annotations can be seen as implementation annotations, they specify how the mapping should be done.
In my opinion it is best to stick as much as possible to the high level annotations and then introduce the lower level annotations as needed.
To answer the questions: the #OneToMany/mappedBy is nicest because it only uses the annotations from the entity domain. The #oneToMany/#JoinColumn is also fine but it uses an implementation annotation where this is not strictly necessary.
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "company_id_ref", referencedColumnName = "company_id")
private List<Branch> branches;
...
}
That Will give below Hibernate logs
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
Hibernate: update branch set company_id_ref=? where id=?
And
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "company")
private List<Branch> branches;
...
}
That will give below Hibernate logs
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
We can clearly see that #joinColumn will cause additional update queries.
so you do not need to set parent entity explicitly to child entity,
That we have to do while using mappedBy
to save children with a parent

what is #JoinColumn and how it is used in Hibernate

I have been reading a lot about #JoinColumn but I still don't get the idea behind it.
Patient Table
CREATE TABLE patient (
patient_id BIGINT NOT NULL,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255) NOT NULL,
PRIMARY KEY(patient_id));
Vehicle Table
CREATE TABLE vehicles (
patient_id BIGINT NOT NULL,
vehicle_id BIGINT NOT NULL,
vehicle_manufacturer VARCHAR(255),
PRIMARY KEY (vehicle_id),
CONSTRAINT patienthasmanyvehicle FOREIGN KEY(patient_id) REFERENCES patient(patient_id));
Patient Class
#OneToMany(mappedBy = "patient")
private Collection<Vehicle> patientVehicles = new ArrayList<Vehicle>();
Vehicle Class
#ManyToOne
#JoinColumn(name="patient_id")
private Patient patient;
I'm confused on how the Vehicle class part, what is the relationship between
Vehicle Class ---- Entity
#JoinColumn(name="patient_id") ---- annotation
private Patient patient ----field
Does it say; The Vehicle Entity has a Foreign Key to Patient entity named patient_id.
Add the patient_id as a column in the Vehicle Entity table
Do the name parameter of the JoinColumn should always be a Foreign Key or Primary Key?
I have been reading this but I'm still confuse.
JPA JoinColumn vs mappedBy
A unidirectional association via a join table
#Entity
class Patient {
#OneToMany
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
}
A bidirectional association via a join table
#Entity
class Patient {
#OneToMany
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
private Patient patient;
}
A unidirectional association via a foreign key
#Entity
class Patient {
#OneToMany
#JoinColumn
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
}
A bidirectional association via a foreign key
#Entity
class Patient {
#OneToMany(mappedBy = "patient")
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
private Patient patient;
}
We don't need to use #JoinColumn on the Vehicle side, Hibernate assumes
it by default. Sometimes I use it just to stress it out (another case, when we want to specify a join column name).
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn
private Patient patient;
}
A bidirectional association via a foreign key with a foreign column name specification
#Entity
class Patient {
#OneToMany(mappedBy = "patient")
private Collection<Vehicle> vehicles = new ArrayList<Vehicle>();
}
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="patient_id")
private Patient patient;
}
This is the basic starting point of using #JoinColumn.
To verify that the foreign key(patient_id in the Vehicle table) is really mapped in the patients table you can use #JoinColumn(nullable = false)
#Entity
class Vehicle {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="patient_id", nullable = false)
private Patient patient
}
The join column is declared with the #JoinColumn annotation which looks like the #Column annotation. It has one more parameters named referencedColumnName. This parameter declares the column in the targeted entity that will be used to the join.
In a bidirectional relationship, one of the sides (and only one) has to be the owner: the owner is responsible for the association column(s) update. To declare a side as not responsible for the relationship, the attribute mappedBy is used. mappedBy refers to the property name of the association on the owner side.
Here is Sample code :
EntityOne :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "TEST_ID")
private EntityTwo entityTwo;
EntityTwo :
// bi-directional many-to-one association to EntityOne Here TEST_ID is the Primary key
#OneToMany(mappedBy = "entityTwo")
private List<EntityOne> entityOne;
Vehicle Class ---- Entity
#JoinColumn(name="patient_id") ---- annotation
private Patient patient ----field
Above code will generate a column patient_id (a foreign key) in Vehicle class which will point to Patient Class primary key.
MappedBy - This attribute tells us that this relation will be managed by Vehicle class. Example. If we insert a vehicle, then two SQL will be injected if cascadetype is all/save. 1st SQL will inject details in Patient table and 2nd SQL will inject vehicle details in vehicle table with patient_id column of Vehicle column pointing to Patient tuple inserted.
The table in which join column will be found depends upon context.
If the join is for a OneToOne or ManyToOne mapping using a foreign key mapping strategy, the foreign key column is in the table of the source entity or embeddable.
If the join is for a unidirectional OneToMany mapping using a foreign key mapping strategy, the foreign key is in the table of the target entity.
If the join is for a ManyToMany mapping or for a OneToOne or bidirectional ManyToOne/OneToMany mapping using a join table, the foreign key is in a join table.
If the join is for an element collection, the foreign key is in a collection table.
Why is that the patient_id (generated column which is a FK) in the
Vehicle Table doesn't have any value when I run my code?
All #JoinColumn does is to specify a column for joining an entity association or element collection. Since you have made #JoinColumn associated with Patient class object, that's why foreign key is created on Patient table.
For more please refer https://docs.jboss.org/hibernate/jpa/2.1/api/javax/persistence/JoinColumn.html

How to set null to foreign key column in OneToMany relationship, Hibernate

I have 2 classes: User and Company.
User
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
public Company getCompany() {
return company;
}
Company
#OneToMany(mappedBy = "company", fetch = FetchType.LAZY)
public List<User> getEmployees() {
return employees;
}
I need when I delete Company to set foreign key company_id as null in User table. How could I do this?
Typically the relationship you describe would be created with a constraint on the Foreign Key that references the User entity. This is so you can't set it to null. If you set it to null, it becomes an orphaned row, which makes for a bad database.
If, on the other hand, you need the two entities to persist separately from their joins, then your relationship is not as you are describing it.
It seems that in this case Companies and Users (Persons?) both need to exist without necessarily being joined. I would suggest a new entity, something like Employment. It would effectively act as a join table in the sense that it has both Company and User id's, but it could also have more information such as startdate, enddate, and current employer.

JPA JoinColumn vs mappedBy

What is the difference between:
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
#JoinColumn(name = "companyIdRef", referencedColumnName = "companyId")
private List<Branch> branches;
...
}
and
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "companyIdRef")
private List<Branch> branches;
...
}
The annotation #JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the "other" entity. This also means that you can access the other table from the class which you've annotated with "mappedBy" (fully bidirectional relationship).
In particular, for the code in the question the correct annotations would look like this:
#Entity
public class Company {
#OneToMany(mappedBy = "company",
orphanRemoval = true,
fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<Branch> branches;
}
#Entity
public class Branch {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "companyId")
private Company company;
}
#JoinColumn could be used on both sides of the relationship. The question was about using #JoinColumn on the #OneToMany side (rare case). And the point here is in physical information duplication (column name) along with not optimized SQL query that will produce some additional UPDATE statements.
According to documentation:
Since many to one are (almost) always the owner side of a bidirectional relationship in the JPA spec, the one to many association is annotated by #OneToMany(mappedBy=...)
#Entity
public class Troop {
#OneToMany(mappedBy="troop")
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk")
public Troop getTroop() {
...
}
Troop has a bidirectional one to many relationship with Soldier through the troop property. You don't have to (must not) define any physical mapping in the mappedBy side.
To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one #JoinColumn as insertable and updatable to false. This solution is not optimized and will produce some additional UPDATE statements.
#Entity
public class Troop {
#OneToMany
#JoinColumn(name="troop_fk") //we need to duplicate the physical information
public Set<Soldier> getSoldiers() {
...
}
#Entity
public class Soldier {
#ManyToOne
#JoinColumn(name="troop_fk", insertable=false, updatable=false)
public Troop getTroop() {
...
}
Unidirectional one-to-many association
If you use the #OneToMany annotation with #JoinColumn, then you have a unidirectional association, like the one between the parent Post entity and the child PostComment in the following diagram:
When using a unidirectional one-to-many association, only the parent side maps the association.
In this example, only the Post entity will define a #OneToMany association to the child PostComment entity:
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "post_id")
private List<PostComment> comments = new ArrayList<>();
Bidirectional one-to-many association
If you use the #OneToMany with the mappedBy attribute set, you have a bidirectional association. In our case, both the Post entity has a collection of PostComment child entities, and the child PostComment entity has a reference back to the parent Post entity, as illustrated by the following diagram:
In the PostComment entity, the post entity property is mapped as follows:
#ManyToOne(fetch = FetchType.LAZY)
private Post post;
The reason we explicitly set the fetch attribute to FetchType.LAZY is because, by default, all #ManyToOne and #OneToOne associations are fetched eagerly, which can cause N+1 query issues.
In the Post entity, the comments association is mapped as follows:
#OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
The mappedBy attribute of the #OneToMany annotation references the post property in the child PostComment entity, and, this way, Hibernate knows that the bidirectional association is controlled by the #ManyToOne side, which is in charge of managing the Foreign Key column value this table relationship is based on.
For a bidirectional association, you also need to have two utility methods, like addChild and removeChild:
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
These two methods ensure that both sides of the bidirectional association are in sync. Without synchronizing both ends, Hibernate does not guarantee that association state changes will propagate to the database.
Which one to choose?
The unidirectional #OneToMany association does not perform very well, so you should avoid it.
You are better off using the bidirectional #OneToMany which is more efficient.
I disagree with the accepted answer here by Óscar López. That answer is inaccurate!
It is NOT #JoinColumn which indicates that this entity is the owner of the relationship. Instead, it is the #ManyToOne annotation which does this (in his example).
The relationship annotations such as #ManyToOne, #OneToMany and #ManyToMany tell JPA/Hibernate to create a mapping. By default, this is done through a seperate Join Table.
#JoinColumn
The purpose of #JoinColumn is to create a join column if one does
not already exist. If it does, then this annotation can be used to
name the join column.
MappedBy
The purpose of the MappedBy parameter is to instruct JPA: Do NOT
create another join table as the relationship is already being mapped
by the opposite entity of this relationship.
Remember: MappedBy is a property of the relationship annotations whose purpose is to generate a mechanism to relate two entities which by default they do by creating a join table. MappedBy halts that process in one direction.
The entity not using MappedBy is said to be the owner of the relationship because the mechanics of the mapping are dictated within its class through the use of one of the three mapping annotations against the foreign key field. This not only specifies the nature of the mapping but also instructs the creation of a join table. Furthermore, the option to suppress the join table also exists by applying #JoinColumn annotation over the foreign key which keeps it inside the table of the owner entity instead.
So in summary: #JoinColumn either creates a new join column or renames an existing one; whilst the MappedBy parameter works collaboratively with the relationship annotations of the other (child) class in order to create a mapping either through a join table or by creating a foreign key column in the associated table of the owner entity.
To illustrate how MapppedBy works, consider the code below. If MappedBy parameter were to be deleted, then Hibernate would actually create TWO join tables! Why? Because there is a symmetry in many-to-many relationships and Hibernate has no rationale for selecting one direction over the other.
We therefore use MappedBy to tell Hibernate, we have chosen the other entity to dictate the mapping of the relationship between the two entities.
#Entity
public class Driver {
#ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}
#Entity
public class Cars {
#ManyToMany
private List<Drivers> drivers;
}
Adding #JoinColumn(name = "driverID") in the owner class (see below), will prevent the creation of a join table and instead, create a driverID foreign key column in the Cars table to construct a mapping:
#Entity
public class Driver {
#ManyToMany(mappedBy = "drivers")
private List<Cars> cars;
}
#Entity
public class Cars {
#ManyToMany
#JoinColumn(name = "driverID")
private List<Drivers> drivers;
}
The annotation mappedBy ideally should always be used in the Parent side (Company class) of the bi directional relationship, in this case it should be in Company class pointing to the member variable 'company' of the Child class (Branch class)
The annotation #JoinColumn is used to specify a mapped column for joining an entity association, this annotation can be used in any class (Parent or Child) but it should ideally be used only in one side (either in parent class or in Child class not in both) here in this case i used it in the Child side (Branch class) of the bi directional relationship indicating the foreign key in the Branch class.
below is the working example :
parent class , Company
#Entity
public class Company {
private int companyId;
private String companyName;
private List<Branch> branches;
#Id
#GeneratedValue
#Column(name="COMPANY_ID")
public int getCompanyId() {
return companyId;
}
public void setCompanyId(int companyId) {
this.companyId = companyId;
}
#Column(name="COMPANY_NAME")
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
#OneToMany(fetch=FetchType.LAZY,cascade=CascadeType.ALL,mappedBy="company")
public List<Branch> getBranches() {
return branches;
}
public void setBranches(List<Branch> branches) {
this.branches = branches;
}
}
child class, Branch
#Entity
public class Branch {
private int branchId;
private String branchName;
private Company company;
#Id
#GeneratedValue
#Column(name="BRANCH_ID")
public int getBranchId() {
return branchId;
}
public void setBranchId(int branchId) {
this.branchId = branchId;
}
#Column(name="BRANCH_NAME")
public String getBranchName() {
return branchName;
}
public void setBranchName(String branchName) {
this.branchName = branchName;
}
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="COMPANY_ID")
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}
I'd just like to add that #JoinColumn does not always have to be related to the physical information location as this answer suggests. You can combine #JoinColumn with #OneToMany even if the parent table has no table data pointing to the child table.
How to define unidirectional OneToMany relationship in JPA
Unidirectional OneToMany, No Inverse ManyToOne, No Join Table
It seems to only be available in JPA 2.x+ though. It's useful for situations where you want the child class to just contain the ID of the parent, not a full on reference.
Let me make it simple.
You can use #JoinColumn on either sides irrespective of mapping.
Let's divide this into three cases.
1) Uni-directional mapping from Branch to Company.
2) Bi-direction mapping from Company to Branch.
3) Only Uni-directional mapping from Company to Branch.
So any use-case will fall under this three categories. So let me explain how to use #JoinColumn and mappedBy.
1) Uni-directional mapping from Branch to Company.
Use JoinColumn in Branch table.
2) Bi-direction mapping from Company to Branch.
Use mappedBy in Company table as describe by #Mykhaylo Adamovych's answer.
3)Uni-directional mapping from Company to Branch.
Just use #JoinColumn in Company table.
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
#JoinColumn(name="courseId")
private List<Branch> branches;
...
}
This says that in based on the foreign key "courseId" mapping in branches table, get me list of all branches. NOTE: you can't fetch company from branch in this case, only uni-directional mapping exist from company to branch.
JPA is a layered API, the different levels have their own annotations. The highest level is the (1) Entity level which describes persistent classes then you have the (2) relational database level which assume the entities are mapped to a relational database and (3) the java model.
Level 1 annotations: #Entity, #Id, #OneToOne, #OneToMany, #ManyToOne, #ManyToMany.
You can introduce persistency in your application using these high level annotations alone. But then you have to create your database according to the assumptions JPA makes. These annotations specify the entity/relationship model.
Level 2 annotations: #Table, #Column, #JoinColumn, ...
Influence the mapping from entities/properties to the relational database tables/columns if you are not satisfied with JPA's defaults or if you need to map to an existing database. These annotations can be seen as implementation annotations, they specify how the mapping should be done.
In my opinion it is best to stick as much as possible to the high level annotations and then introduce the lower level annotations as needed.
To answer the questions: the #OneToMany/mappedBy is nicest because it only uses the annotations from the entity domain. The #oneToMany/#JoinColumn is also fine but it uses an implementation annotation where this is not strictly necessary.
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "company_id_ref", referencedColumnName = "company_id")
private List<Branch> branches;
...
}
That Will give below Hibernate logs
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
Hibernate: update branch set company_id_ref=? where id=?
And
#Entity
public class Company {
#OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY,
mappedBy = "company")
private List<Branch> branches;
...
}
That will give below Hibernate logs
Hibernate: select nextval ('hibernate_sequence')
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into company (name, company_id) values (?, ?)
Hibernate: insert into branch (company_id_ref, name, id) values (?, ?, ?)
We can clearly see that #joinColumn will cause additional update queries.
so you do not need to set parent entity explicitly to child entity,
That we have to do while using mappedBy
to save children with a parent

How to map two tables to one entity using foreign-key?

I have a problem very similar to this: How do I join tables on non-primary key columns in secondary tables?
But I'm not sure if I can apply the same solution.
I have two tables like these:
CREATE TABLE CUSTOMER
(
CUSTOMER_ID INTEGER NOT NULL,
DETAIL_ID INTEGER NOT NULL,
PRIMARY KEY( CUSTOMER_ID ),
CONSTRAINT cust_fk FOREIGN KEY( DETAIL_ID ) REFERENCES DETAILS( DETAIL_ID )
)
CREATE TABLE DETAILS
(
DETAIL_ID INTEGER NOT NULL,
OTHER INTEGER NOT NULL,
PRIMARY KEY( DETAIL_ID )
)
I'd like to map these tables to a single class called Customer, so I have:
#Entity
#Table(name = "CUSTOMERS")
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID"))
public class Customer {
#Id
#GeneratedValue
#Column(name = "CUSTOMER_ID")
private Integer id;
#Column(table = "DETAILS", name = "OTHER")
private Integer notes;
// ...
}
but this works only if DETAIL_ID matches CUSTOMER_ID in the primary table.
So my question is: how can i use a foreign-key field in my primary table to join on the primary-key of the secondary table?
UPDATE
I tried to set:
#SecondaryTable(name = "DETAILS", pkJoinColumns=#PrimaryKeyJoinColumn(name="DETAIL_ID", referencedColumnName="DETAIL_ID"))
but when I run the application I get this exception:
org.hibernate.MappingException: Unable to find column with logical name: DETAIL_ID in org.hibernate.mapping.Table(CUSTOMERS) and its related supertables and secondary tables
For anyone looking for an answer to this, using #SecondaryTable is not the way to join two tables with non-primary key columns, because Hibernate will try to assosiate the two tables by their primary keys by default; you have to use #OneToMany review http://viralpatel.net/blogs/hibernate-one-to-many-annotation-tutorial/ for a solution, here's a code snippet in case that url stops working:
Customer Class:
#Entity
#Table(name="CUSTOMERS")
public class Customer {
#Id
#GeneratedValue
#Column(name="CUSTOMER_ID")
private Integer id;
#ManyToOne
#JoinColumn(name="DETAIL_ID")
private Details details;
// Getter and Setter methods...
}
Details Class:
#Entity
#Table(name="DETAILS")
public class Details {
#Id
#GeneratedValue
#Column(name="DETAIL_ID")
private int detailId;
#Column(name="OTHER")
private String other;
#OneToMany(mappedBy="details")
private Set<Customer> customers;
// Getter and Setter methods...
}
This is easily accessible through hibernate with the following code:
Session session = HibernateUtil.getSessionFactory().openSession();
Query query = session.createQuery("select id, details.other from Customer");
I hope this helps anyone out there spending hours searching for a way to achieve this like I did.
You can use the referenceColumnName attribute of the #PrimaryKeyJoinColumn annotation to define the join column to the referenced table. In fact, by combining use of name/referencedColumnName you can join on arbitrary on both sides, with the constraint that if duplicates are found your ORM provider will throw an exception.

Categories