For example, I have the following entities:
Room entity:
#Entity
public class Room extends Base {
#OneToMany(mappedBy = "room")
private List<Person> persons;
//getters and setters
}
And the Person entity:
#Entity
public class Person extends Base {
#ManyToOne
private Room room;
private String name;
//getters and setters
}
If I add person to room's persons list and then persist, the room is added, but person is not. I thought, enough is to add mappedBy parameter to OneToMany annotation, but.. what should I do else?
You need to set CascadeType=PERSIST or ALL on #OneToMany. And Also set room variable in Person class to populate foreign key in Person table.
Do the opposite, set a Room as the room field of a Person instance:
Room room = ...;
person.setRoom(room);
// persist the person
With the mappedBy, you specify that which entity owns the relationship. Updating the relationship between those two entities is the responsibility of the owning side.
In your example, the Person owns the relationship, so it's his responsibility to update the foreign key, not Room's, so when you add a Person to list of persons in Room and update the Room, Room wouldn't update the foreign key for you.
Related
I have a multimodule application with two modules:
department-management
communication-management
now in my department-management i have an entity Department and in communication-management module i have MailingGroup entity.
Also the communication-management depends on department-management module.
Now i want to have bidirectional ManyToOne relation between Department and MailingGroup
#Entity
public class Department {
#OneToMany(mappedBy = "department")
List<MailingGroup> mailingGroups;
}
#Entity
public class MailingGroup{
#ManyToOne
#JoinColumn(name = "DEPARTMENT_ID")
Department department;
}
This, of course, is not archiveable the way above, but can i archive this bidirectional relation using interfaces? My initial idea was to solve it like this:
public interface MailingGroupProvider {
Department getDepartment()
}
#Entity
public class Department {
#OneToMany(mappedBy = "department")
List<MailingGroupProvider> mailingGroups;
}
#Entity
public class MailingGroup implements MailingGroupProvider {
#ManyToOne
#JoinColumn(name = "DEPARTMENT_ID")
Department department;
}
But it raises questions:
Is this prefered solution in such cases?
What methods should my interface provide to be treated as Entity by JPA?
is this even possible what im trying to do?
First Approach is Perfect: You need to add a Relationship in Both Entity Sides.
departmentid is the foreign key of the mailing group Table. Apply Relationship in both entity sides it will work.
#OneToMany Relationship means u can have one Department can have Many Communication.
Bidirectional means suppose if u do any operation in Communication Management like deleting Communication it will affect on Department Table Also. It will Remove Departmentid matched in the Department Table
I am learning Hibernate and came across the terms Owning-side and Non-owing side.
Upon reading, I came to know that Owning-side means the Entity who has FK reference to the other Entity.
Consider the below scenario:
User entity:
#Entity
public class User {
#Id
private int userId;
private String userName;
#OneToOne
#JoinColumn("VEHICLE_ID")
private Vehicle vehicle;
// other code
}
Vehicle Entity:
#Entity
public class Vehicle{
#Id
private int vehicleId;
private String vehicleName;
#OneToOne
#JoinColumn("USER_ID")
private User user;
// other code
}
If I keep the #JoinColumn in both these Entities, then would both these entities User and Vehicle become owning-side entities?
I am not able to understand as to why just placing #JoinColumn annotation makes an entity as the owning-side Entity.
Can anyone help me understand?
If I keep the #JoinColumn in both these Entities, then would both
these entities User and Vehicle become owning-side entities?
We don't start with using the #JoinColumn in an entity and then state that particular entity is an owning entity. In fact the reverse is true.
We first decide which should entity should be owning entity and which one should be non-owning.
And we decide that by looking at which entity has the Foreign Key reference. And this again is more of a design choice. The one having the foreign-key reference is said to be the owning entity.
We then use the #JoinColumn annottation in the owning entity to specify the foreign key.
And in the non-owning side, we use mappedBy attribute inside of the particular association type.
In your example you are specifying a bi-directional relationship. So if we consider Vehicle entity has the foreignKey as userId, then Vehicle entity would be our owning-entity and we annotate this 'userId' with '#JoinColumn'. And in the non-owning entity i.e. User entity, we would use the 'mappedBy' attribute to refer to the field in owning entity on which #JoinColumn has been used :
#Entity
public class User {
#Id
private int userId;
private String userName;
#OneToOne(mappedBy="user")
private Vehicle vehicle;
// other code
}
#Entity
public class Vehicle{
#Id
private int vehicleId;
private String vehicleName;
#OneToOne
#JoinColumn("USER_ID")
private User user;
// other code
}
Here is a detailed documentation that might help you as a reference :
The JPA API reference docs also provide a nice brief description for the different association types : OneToOne , OneTomany , ManyToMany
Assume the following data model:
a pet owner has a pet
a pet can perform many tricks
a pet owner can participate in a show where his pet can only perform his best trick
The association is shown below. Note that "tricks" are a set of strings marked with #ElementCollection since in this example it doesn't make sense to manage a trick as a separate entity (We only care about the name of the trick):
#Entity
class PetOwner {
#Id String name;
#OneToOne Pet pet;
}
#Entity
class Pet {
#Id String name;
#ElementCollection Set<String> tricks;
}
#Entity
class PetShowApplicant {
#Id Integer showId;
#ManyToOne PetOwner owner;
//....//
//what goes here
String trickToPerform;
}
I would like to associate PetShowApplicant.trickToPerform to owner.pet.tricks to ensure that it references the join table "pet_tricks" created by #ElementCollection - Is this possible? Thanks for your time
#Entity
public class Person {
#Id
#GeneratedValue
private int personId;
#OneToOne(cascade=CascadeType.ALL, mappedBy="person", fetch=FetchType.LAZY)
private PersonDetail personDetail;
//getters and setters
}
#Entity
public class PersonDetail {
#Id
#GeneratedValue
private int personDetailId;
#OneToOne(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
private Person person;
//getters and setters
}
when i do
Person person1=(Person)session.get(Person.class, 1);
i see two queries being fired. one for fetching person data and another for person detail data.
As per my understanding only 1 query should have been fired that is for fetching person data not for person detail data as i have mentioned
lazy loading. Why personDetail data is getting fetched along with person data ?
Hibernate cannot proxy your own object as it does for Sets / Lists in a #ToMany relation, so Lazy loading does not work.
I think this link could be useful to understand your problem: http://justonjava.blogspot.co.uk/2010/09/lazy-one-to-one-and-one-to-many.html
Based on your comment and since the PersonDetail entity contains a foreign key column that references the Person entity, it looks like you only have 1 problem:
Entity relationships include the concept of a relationship owner (in this case PersonDetail), which means that you want to add a #JoinColumn annotation in the PersonDetail entity.
You have already correctly defined the inverse side of the relationship (the entity that is not the owner of the relationship) with the mappedBy attribute that was added to the association annotation (#OneToOne in your case) to make the relationship bi-directional, which means that the associated PersonDetail may be reached from a Person instance.
Given the relationship that is clarified in your comment, you should only have to make 1 change to your code as shown here:
#Entity
public class Person {
#Id
#GeneratedValue
private int personId;
//Retain the mappedBy attribute here:
#OneToOne(cascade=CascadeType.ALL, mappedBy="person",
fetch=FetchType.LAZY)
private PersonDetail personDetail;
//getters and setters...
}
#Entity
public class PersonDetail {
#Id
#GeneratedValue
private int personDetailId;
#OneToOne(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
//Change: add the #JoinColumn annotation here:
#JoinColumn(name="PERSON_FK_COLUMN")
private Person person;
//getters and setters...
}
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;
...
}