I am trying to understand the javax.persistence annotations #OneToOne, #ManyToOne and #ManyToMany. The descriptions for these annotations make a mention of non-owning side. Specifically:
[#OneToOne]: If the relationship is bidirectional, the non-owning side must use the mappedBy element of the OneToOne annotation to specify the relationship field or property of the owning side.
[#ManyToOne]: If the relationship is bidirectional, the non-owning OneToMany entity side must used the mappedBy element to specify the relationship field or property of the entity that is the owner of the relationship.
[#ManyToMany]: If the relationship is bidirectional, the non-owning side must use the mappedBy element of the ManyToMany annotation to specify the relationship field or property of the owning side.
I am having trouble understanding this ownership aspect. For example, I have the following associations:
Note: Images taken from here.
So which are the non-owning entity sides of these associations?
In the bi-directional relationship betweens two objects ,you have to choose which sides to manage the relationship. From the database perspective , managing the relationship means managing the value of some FK column that link between two tables. The side that managing it is called owning side. Otherwise, it is call non-owning side.
So back to your example on ProjectManager and Project. Which object is the owning side depends on which object you choose to manage their relationship.
If you choose ProjectManager to be the owning side (hence Project is the non-owning side), only the values of ProjectManager#getProjects() will be used to determine the value of such FK column. (i.e. project table 's project_manager_id column in this case) The value of Project#getProjectManager() will be ignored and does not affect the value of this FK column.
In term of JPA mapping , it is :
#Entity
#Table(name="project_manager")
public class ProjectManager{
#OneToMany
private List<Project> projects = new ArrayList<>();
}
#Entity
#Table(name="project")
public class Project {
#ManyToOne
#JoinColumn(name = "project_manager_id")
private ProjectManager projectManager;
}
On the other hands , if you choose Project to the owning side (hence ProjectManager is the non-owning side) , only the value of Project#getProjectManager() will be used to determine the value of this FK column while the value of ProjectManager#getProjects() will be ignored. The JPA mapping in the case will be :
#Entity
#Table(name="project_manager")
public class ProjectManager{
#OneToMany(mappedBy="projectManager")
private List<Project> projects = new ArrayList<>();
}
#Entity
#Table(name="project")
public class Project {
#ManyToOne
#JoinColumn(name = "project_manager_id")
private ProjectManager projectManager;
}
P.S: I explain it using property access , hopefully you should get the idea.
Related
I'm trying to model a business entity, where said business can have several parent businesses and several child businesses. I'm not sure which relationships are suited, or even what mapping is appropriate. I'm familiar with SQL but new to ORM in Java.
My thinking is that a business can have many or none children, and a business can have many or none parents. Therefore I've tried setting both as OneToMany but also as OneToMany, both resulting in this error: Illegal use of mappedBy on both sides of the relationship.
The implementation:
#Entity
public class Business{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "parentOrgs")
private Collection<Business> chlidOrgs;
#OneToMany(mappedBy = "chlidOrgs")
private Collection<Business> parentOrgs;
// --- Getters and setters below ---
What am I not understanding here? Any and all help much appreciated.
Your current mapping is syntactically incorrect, because only one side of the relationship can be owning side. Owning side is the field defined by value of mappedBy attribute. More detailed explanation can be found from here.
Also removing mappedBy from the one side does not solve the problem, because counterpart of OneToMany must be ManyToOne. Removing it from the both sides leaves us with two unirectional associations, which is also not what is needed.
Because each Business can have multiple parents and it seems to be preferred to be able to navigate directly to the childrens as well, solution is to use bidirectional ManyToMany:
#Entity
public class Business {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "parents")
private Collection<Business> childrens;
#ManyToMany
private Collection<Business> parents;
}
From database point of view this means following tables:
Business(id)
Business_Business(childrens_id, parents_id)
When necessary, name of the join table and columns can be controlled via JoinTable.
Under hibernate-jpa-api-2.0, can i specify the name of the foreign key using #PrimaryKeyJoinColumn with oneToOne relation ?
I tried the folowing but it doesn't seem to work.
#OneToOne(optional = false, fetch = FetchType.LAZY)
#PrimaryKeyJoinColumn(name = "CARID")
#ForeignKey(name = "FK_CAR_CORP")
public CarEntity getCar() {
return car;
}
There are two #ForeignKey annotations can be used:
org.hibernate.annotations.ForeignKey (Hibernate annotation)
javax.persistence.ForeignKey (JPA annotation)
Hibernate 4 (even the last version) sometimes doesn't process, probably cause of bug, javax.persistence.ForeignKey annotation.
For example, with Hibernate 4.3.11 we should use org.hibernate.annotations.ForeignKey on the #OneToMany part of unidirectional association. And we can use javax.persistence.ForeignKey on the #ManyToOne part of bidirectional association.
So you can try to use Hibernate org.hibernate.annotations.ForeignKey annotation.
The mappedBy attribute is only necessary for a bidirectional relationship, this element can be omitted on the annotation. It is used on the source entity to point back to a field on the target entity that defines the relationship (contains #JoinColumn).
The #JoinColumn annotation should be placed upon the billSimpleEntry field to define the column that should be used to join the two tables. In the case of a OneToOne the following applies:
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.
Here is a code example:
#OneToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER)
#JoinColumn(name="id") // use actual column name
private ClassName className; // use class name
I don't think #ForeignKey will work for hibernate-jpa-api-2.0, as per doc it's release in 2.1
Since:
Java Persistence 2.1
here is doc
I am using hibernate with JPA annotations for relationship mapping.
I have three entities in my code User Group & User_Group
User & Group are in a ManyToMany relationship.
User_Group is a kinda bridge table but with some additional fields. So here is the modified mapping code.
User
#Entity
#Table(name = "USERS")
public class User {
#OneToMany(mappedBy = "user")
private Set<UserGroup> userGroups
}
Group
#Entity
#Table(name = "GROUPS")
public class Group {
#OneToMany(mappedBy = "group")
private Set<UserGroup> userGroups
}
UserGroup
#Entity
#Table(name = "USERS_GROUPS")
public class UserGroup {
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "USER_ID")
private User user;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "GROUP_ID")
private Group group;
}
When I set the user & group object to the usergroup & save it.
User user = new User("tommy", "ymmot", "tommy#gmail.com");
Group group = new Group("Coders");
UserGroup userGroup = new UserGroup();
userGroup.setGroup(group);
userGroup.setUser(user);
userGroup.setActivated(true);
userGroup.setRegisteredDate(new Date());
session.save(userGroup);
Things work fine. With CascadeType.ALL the group object & user object are updated too. But when I delete the userGroup object. The child object are deleted too.
Deletion of child objects is a strict no no.
There is no CascadeType.SAVE-UPDATE in JPA, which just does save or update but no delete. How do I achieve this.
If I remove the CascadeType.ALL from the mapping the child objects don't get updated & I need them to be updated.
SAVE_UPDATE is for save(), update(), and saveOrUpdate(), which are 3 Hibernate-proprietary methods. JPA only has persist() and merge(). So, if you want to use cascading on Hibernate-proprietary methods, you'll need to use Hibernate-proprietary annotations. In this case, Cascade.
Or you could stop using the Hibernate Session, and use the standard JPA API instead.
CascadeType.ALL includes CascadeType.REMOVE too.
The solution is to use all CascadeType.* you need except CascadeType.REMOVE, like so:
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REFRESH, CascadeType.MERGE}))
in your UserGroup definitions.
It's almost always a code smell when propagating from child to parent entity, it should be the other way round.
From Cascading best practices:
Cascading only makes sense only for Parent – Child associations (the
Parent entity state transition being cascaded to its Child entities).
Cascading from Child to Parent is not very useful and usually, it’s a
mapping code smell.
From Hibernate best practices:
Avoid cascade remove for huge relationships
Most developers (myself included) get a little nervous when they see a
CascadeType.REMOVE definition for a relationship. It tells Hibernate
to also delete the related entities when it deletes this one. There is
always the fear that the related entity also uses cascade remove for
some of its relationships and that Hibernate might delete more
database records than intended. During all the years I’ve worked with
Hibernate, this has never happened to me, and I don’t think it’s a
real issue. But cascade remove makes it incredibly hard to understand
what exactly happens if you delete an entity. And that’s something you
should always avoid. If you have a closer look at how Hibernate
deletes the related entities, you will find another reason to avoid
it. Hibernate performs 2 SQL statements for each related entity: 1
SELECT statement to fetch the entity from the database and 1 DELETE
statement to remove it. This might be OK, if there are only 1 or 2
related entities but creates performance issues if there are large
numbers of them.
I have a classic one to many relationship and while saving it with Hibernate, I am not able to pass parent's PK column value to Child's FK column.
Parent Class
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int holdingPK;
#OneToMany(mappedBy="holding",targetEntity=PolicyType.class,fetch=FetchType.LAZY, cascade = CascadeType.ALL)
#XmlElement(name = "Policy")
private Set<PolicyType> policy;
Child Class
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int policyPK;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="HoldingFK",nullable = false)
private HoldingType holding;
Here HoldingPKis a auto generated sequence column which represents primary key. Value gets generated when I insert a Holding row. So I want to pass HoldingPK value to child's HoldingFK column on the fly.
Test Code
HoldingType obj = new HoldingType();
obj.setCurrencyTypeCode("6");
obj.setHoldingKey("123");
Set<PolicyType> set = new TreeSet<PolicyType>();
PolicyType policy = new PolicyType();
policy.setJurisdiction("Haha");
set.add(policy);
obj.setPolicy(set);
session.save(obj);
transaction.commit();
So I am able to pass Child's other values to Child Table column, just Parent PK is not reaching to Child's FK column.
Here I am persisting XML document values to database. For this I am marshalling XML to Java Objects using JAXB then persisting objects using Hibernate. In this way I am reusing JAXB generated classes with Hibernate and these PK and FK elements do not exist on XML. These are specific to Database.
You simply forgot to initialise the owning side of the bidirectional association. You only initialized the inverse side (the one which has the mappedBy attribute). Hibernate only considers the owning side to know if an association exists or not (the side without the mappedBy attribute).
Add this to your code (before the holding is saved):
policy.setHolding(obj);
Side note: your code would be much more readable if you named the policy field (and accessors) policies. There are many of them, so it should have a plural form.
I have an Entity that holds the last instance of a Component:
#Entity
public class Customer {
...
#Embedded
public ServiceCall getLastServiceCall() {...}
...
}
#Embeddable
public class ServiceCall() {
public Date getDate();
public Status getStatus();
}
The ServiceCall is embedded in order to call customer.getLastServiceCall().getDate() without requiring another table join.
I want to make that association one-to-many, and start saving all ServiceCalls, while holding one embedded in the customer.
Hibernate's Docs (v3.6, which I'm using) states:
You can also use association annotations in an embeddable object (ie #OneToOne, #ManyToOne, #OneToMany or #ManyToMany). To override the association columns you can use #AssociationOverride.
and it seem that all I should do is add #OneToMany to the LastServiceCall association.
Will that work for me? If not, what are my alternatives? If yes, how will that affect 2nd level cache, and is there a limitation on updating that embedded instance (I can live with an immutable objects)?
#Embeded types are not supposed to have their own identity in the database, so I don't think you can add #OneToMany to the Customer class on the ServiceCall.
#OneToMany
#Embedded
public ServiceCall getLastServiceCall() {...}
However you can add an association to the #Embeded Service call element like so.
#Entity
pubic class HistoricalServiceCall extends ServiceCall
{
#Id
private String id;
}
#Embeddable
public class ServiceCall {
#OneToMany(fetch=FetchType.LAZY)
#JoinColumn(name="join_column_defined_on_customer_table")
List<HistoricalServiceCall> getServiceCallHistory();
}
Update: putting FetchType.LAZY on the getServiceCallHistory() is a hint to the JPA provider to wait until you call getServiceCallHistory before it does another select to pull in that association.
So with the setup I am describing if you do customer.getLastServiceCall().getDate() it will not pull
in the ServiceCallHistory before the relationship is lazy.
What you need is the following:
A Customer entity
An embeddable ServiceCall
A HistoricalServiceCall entity.
The Customer should contain an embedded ServiceCall field (the last service call).
The HistoricalServiceCall entity should have an ID, an embedded ServiceCall field (the data of the HistoricalServiceCall), and, potentially, a ManyToOne association to Customer if you want the OneToMany to be bidirectional.
The Customer should have a OneToMany association to HistoricalServiceCall.