I have the following scenario:
I'm having troubles figuring out what the owner side is and why ?
Please can you help ?
public class Basket
{
...
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumns({#JoinColumn(name="BASKET", referencedColumnName="ID")})
public Set<Product> getProductList() {
return this.productList;
}
}
public class Product
{
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumns({#JoinColumn(name="BASKET", referencedColumnName="ID")})
public Basket getBasket() {
return this.basket;
}
}
Basket is the "One" side of the relationship and there are many products identified as Baskets. So the "one" is usually the parent code and the child records reference the parent. Usually the easiest way to understand these is to look at the database foreign key constraint (hopefully there is one) that connects the backing tables.
One client has One basket and One basket has One owner, so:
class Owner has basket in relation #OneToOne.
One basket has many products, so:
class Basket has List of products in relation #OneToMany.
Many Products has One basket so:
class Product has relation in #ManyToOne.
The extract from the JPA 2.0 specification might help you to understand the issue:
2.10.2Bidirectional ManyToOne / OneToMany Relationships
Assuming that:
Entity A references a single instance of Entity B.
Entity B references a collection of Entity A[21].
Entity A must be the owner of the relationship.
The following mapping defaults apply:
Entity A is mapped to a table named A.
Entity B is mapped to a table named B.
Table A contains a foreign key to table B. The foreign key column name is formed as the concatenation of the following: the name of the relationship property or field of entity A; "_"; the name of the primary key column in table B. The foreign key column has the same type as the
primary key of table B.
And in section 11.1.21 the following is stated:
If there is more than one join column, a JoinColumn annotation must be specified for each join column using the JoinColumns annotation. Both the name and the referencedColumnName elements must be specified in each such JoinColumn annotation.
In your case, there is only one join column. So you don't need to the #JoinColumns annotation. Just use#JoinColumn. The #JoinColumn annotation is used always on the owning side of the relationship, that is the #ManyToOne side in this case.
Related
Consider a one-to-many association like project and task, where a project can have multiple tasks. If this is modeled using a foreign key clause in the task table which refers to the primary key of the project table, then we put the #JoinColumn annotation in the owning side i.e. the task table as the many side is always the owning side.
But if this is mapped by using a third table project_task, the #JoinTable annotation is being used on the inverse side i.e. on the project entity. But #JoinTable should always be on the owning side.
Approach one with foreign key:
#Entity
public class Project {
#OneToMany(mappedBy = "project")
private Collection<Task> tasks;
}
#Entity
public class Task {
#ManyToOne
#JoinColumn(name="PROJECT_ID")
private Project project;
}
Approach two with third table and #JoinTable Annotation:
#Entity
public class Project {
#JoinTable(name = "MY_JT",
joinColumns = #JoinColumn(
name = "PROJECT_ID",
),
inverseJoinColumns = #JoinColumn(
name = "TASK_ID"
)
#OneToMany
private List<Task> tasks;
}
#Entity
public class Task {
private Project project;
}
Why is the owning side shifted to the project entity when using #JoinTable?
Try to understand owning side as the entity which knows of the other end of the association.
I am the owning entity if i'm able to reach the associated entity so i have informations which allow me to reach the associated entity.
The most used case is to put the foreign key on the many side of the association.
So if you have a relation between Project and Task and we 're telling that 1 Project can have many Tasks.
A project_id column will be add on Task table which will be the owning side of the relation because it held information (Project Fk) which allow it to reach Project Entity from Car.
When you're using #Jointable on OneToMany association you're designing a unidirectional association so car entity don't have anymore the information to reach associated User entity like in a bidirectional association.
This information is now kept by a join table which is in User entity so User entity become the owning side of the relation.
So to answer your question the owning side shifted to Projet because now you can't anymore reach a project from a task and the only way to have tasks associated to a project is to use the join table held by Project entity. So now It's project entity through its joinTable wich keep informations to retrieve the tasks associated to a project.
Hope it's clear.
What do you mean by 'shifted'? The owning side of an association is either the only side of that association, or the side whose association annotation does not have the mappedBy parameter present.
In your second example, Project is the owning side, because the association is unidirectional (there is no mapping of that association in Task).
The owning side of a association is the one that manages the association (i.e. changes to that side will be recorded). Had Task been defined like this:
#Entity
public class Task {
#ManyToOne(mappedBy="tasks")
private Project project;
}
Project would still be the owning side (meaning: changes to Project.tasks would be recorded, while changes to Task.project would be ignored). This is an unusual, yet valid mapping (it typically makes more sense to declare the many side the owning side in a bidirectional one-to-many association, but doing the opposite is also permitted).
I have defined entities relation in Hibernate ORM with following code:
#Entity
public class Treatment {
#OneToMany(fetch = FetchType.EAGER, mappedBy="treatment")
private List<Consultation> consultations;
...
}
#Entity
public class Consultation {
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "treatment_id")
private Treatment treatment;
...
}
My question is, how should I update Treatment/Consultation when I want to make relation?
Is it enough to update it on one side:
treatment.getConsultations().add(newCon);
Or should I update it on both sides?
treatment.getConsultations().add(newCon);
newCon.setTreatment(treatment);
How it looks in case of delete?
Well, using the mappedBy you tell Hibernate that the relationship is maintained by the other side, a filed called treatment in Consultation class. So first you have to get the consultation instance, then set the treatment, and finally persist the consultation instance. It will update all the references in the DB as integrity constraints (Primary Key/ Foreign Key pairs). So here the consultation table will have a treatmentId foreign key column pointing to the ID column (primary key) of the Treatment table.
an example code is,
Consultation consultation = new Consultation();
// This maintains the relationship.
consultation.setTreatment(treatment);
someDaoRepository.save(consultation);
Hope this helps, Happy Coding !
I have two entities A and B with one-to-one relationship. When I want to insert them into DB, I get the following error:
Cannot add or update a child row: a foreign key constraint fails
(mydb.a, CONSTRAINT
FK_77pkrkrin5nqsx16b6nw6k9r7 FOREIGN KEY (id) REFERENCES
b (b_id))
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true, value={"hibernateLazyInitializer", "handler"})
#Generated("org.jsonschema2pojo")
#Inheritance(strategy = InheritanceType.JOINED)
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#JsonIgnore
private Integer id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#PrimaryKeyJoinColumn(referencedColumnName="AId")
#JsonIgnore
private B b;
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true, value = {"hibernateLazyInitializer", "handler"})
#Generated("org.jsonschema2pojo")
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int bId;
#OneToOne()
#JsonIgnore
private A a;
}
The insert operation will work perfectly if I remove the optional=false. I checked the objects before inserting them into DB and I make sure that A contains B and B contains A.
The SQL scripts for A and B creation are:
Hibernate: create table b (b_id integer not null auto_increment,
string_results longtext, a_id integer, primary key (b_id))
Hibernate: create table a (id integer not null auto_increment, primary
key (id))
Hibernate: alter table b add constraint FK_o3oen721etlltdc7ls82524nh
foreign key (detail_id) references a (id)
Hibernate: alter table a add constraint FK_77pkrkrin5nqsx16b6nw6k9r7
foreign key (id) references b (b_id)
When I see the following sentence:
I checked the objects before inserting them into DB and I make sure that A contains B and B contains A.
I guess that you want to create a bidirectional one-to-one relationship. If so, your current mapping does not work as expected. Let us see what the JPA 2.0 spec(download link) states about this to understand the matter:
Relationships may be bidirectional or unidirectional. A bidirectional relationship has both an owning side and an inverse (non-owning) side. A unidirectional relationship has only an owning side. The owning side of a relationship determines the updates to the relationship in the database, as described in section 3.2.4.
The following rules apply to bidirectional relationships:
• The inverse side of a bidirectional relationship must refer to its owning side by use of the mappedBy element of the OneToOne, OneToMany, or ManyToMany annotation. The mappedBy element designates the property or field in the entity that is the owner of the relationship.
•The many side of one-to-many / many-to-one bidirectional relationships must be the owning side, hence the mappedBy element cannot be specified on the ManyToOne annotation.
• For one-to-one bidirectional relationships, the owning side corresponds to the side that contains the corresponding foreign key.
• For many-to-many bidirectional relationships either side may be the owning side.
So, according to the specification in a bidirectional one-to-one relationship one of the entities must be made the owning side and the other one the inverse side. Let assume entity A is the owning side the following mapping should work:
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, optional = false)
#JoinColumn(name="b_id", referencedColumnName="ID")
private B b;
}
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne(mappedBy = "b")
private A a;
}
In order to make the above mapping work either the physical tables must be generated automatically or if you want to create the tables yourself, the corresponding SQL should look the following:
create table a (id integer not null auto_increment,
b_id integer not null,
primary key (id),
foreign key b_id references b (id));
create table b (id integer not null auto_increment,
string_results longtext,
primary key (id));
NOTE:
I removed the JSON-specific annotation to make the code shorter (I don't have any knowledge about them)
If you want to make entity B the owning side, you have to adjust the relationship mapping accordingly.
The #JoinColumn annotation is always on the owning side.
Due to lack of time I haven't tested the code. If you find any bug (especially the MySQL syntax) just leave me a comment.
From the hibernate docs:
(Optional) Whether the association is optional. If set to false then a non-null relationship must always exist.
Right at the time of creation of B, the b_id is not available, it is created when the flush happens to database.
Since A has a foreign key relation on B(b_id), B needs to be created along with its id before you can insert A or mark this foreign relation as optional.
Create B and flush it to database, then create A. B has A is not required as its A who defines a foreign key to B and not vice-versa, B's reference to A is only reflexive, two-way foreign key will create cyclic reference issue.
#JoinColumn gives an Entity a foreign key to another Entity whereas #JoinTable will list the relationship between all relationships between Entity A and Entity B. As far as I can tell, they both appear to do similar things. When should I use one or the other?
Let's say you have an entity A which has a #ManyToOne association ot an entity B
#JoinColumn will define the target table Foreign Key (e.g B_ID) while using the target Entity table (e.g. B).
#Entity
public class A {
private Long id;
#ManyToOne
#JoinColumn(name="B_ID")
private B b;
}
#JoinTable will use a separate table to hold the relationship between A and B.
#Entity
public class A {
private Long id;
#ManyToOne
#JoinTable(
name = "A_B",
joinColumns = #JoinColumn(name = "B_ID"),
inverseJoinColumns = #JoinColumn(name = "A_ID")
)
private B b;
}
This time neither A nor B contain any Foreign Key, because there's a separate table (e.g. A_B) to hold the association between A and B.
#JoinTable stores the id of both the table into a separate table while #JoinColumn stores id of the another table in a new column.
#JoinTable : This is the default type. Use this when you you need a more normalized database. ie. to reduce redundancy.
#JoinColumn : Use this for better performance as it does not need to join extra table.
one important difference: #JoinColumn always depends upon the context it is used:
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.
I have Person entity which has composition with Location Entity
#ManyToOne(fetch = FetchType.EAGER, cascade =
{ CascadeType.PERSIST, CascadeType.MERGE })
#Cascade(
{org.hibernate.annotations.CascadeType.SAVE_UPDATE })
public Location getLocation()
{
return location;
}
And Location Entity has Name as Id
#Id
public String getName()
{
return name;
}
I am getting following Exception when Person's location is changed from L1 to L2 in Spring MVC form where this Person entity is modelAttribute for the form.
org.springframework.orm.hibernate3.HibernateSystemException:identifier of an instance of com.x.y.z.Location was altered from L2 to L1; nested exception is org.hibernate.HibernateException: identifier of an instance of com.x.y.z.Location was altered from L2 to L1
You're confusing Composition with Association.
What you have mapped is an association; composition in Hibernate (JPA) is mapped via #Embeddable / #Embedded annotations. Associations are relationships between separate entities; they are usually connected via entity identifiers (foreign keys in the database).
In your particular case, Person entity points to Location entity which means in the database PERSONS table has a LOCATION_ID foreign key (names may differ) to LOCATIONS table. What you're trying to do is to update that key on Location end which is illegal because it would sever Hibernate's relationship (the other end still holds the previous key value internally).
Primary keys should generally be surrogate and not updatable to begin with; if you do need to "update" it you'll have to either disassociate Location from Person, update Location and assign it to Person again OR create a brand new Location instance and assign that to your Person.
All that said, if you're really trying to model Composition relationship, you need to replace #ManyToOne with #Embedded and change your table schema accordingly. Here's a link to
Hibernate Annotations documentation on mapping components.
Also, specifying cascade types in two separate annotations (JPA vs Hibernate extension) is not a good thing. If you really need the Hibernate extension one (which you don't in this case), just use it and leave cascade attribute in JPA annotations empty.
I done same thing in standalone application . The thing works. I think it should be some problem with #modelAttribute.
In your Location entity attribute id type has been changed in your model class.Please refer the id and mapping attribute id types are same.Make sure that id attribute getter and setter function return types.