I've got the following code:
#OneToOne(cascade = ALL)
private SquadMember commander;
#OneToOne(cascade = ALL)
private SquadMember lieutenant;
#OneToMany(orphanRemoval = true, cascade = REMOVE, mappedBy = "squad")
private Set<SquadMember> memberList = new LinkedHashSet<>();
When I add a commander or lieutenant, they end up in the SquadMember table and when I retrieve the memberList from the database the commander and lieutenant are included in the memberList(basically duplicated as they're in the Squad as commander/lieutenant but also as member).
How can I fix this so that the commander / lieutenant never appear in the memberList when retrieved from the database?
Posts I've already looked at but did not find my answer:
JPA OneToOne and OneToMany on the same entity
I suppose the piece of code you have put is from the entity "Squad".
In the SquadMember entity you must have three Squad type parameters for the bidirectional mapping to be correct, since if you are going to register a lieutenant and fill in his squad when you only have one parameter, JPA and Hibernate will associate it as being part of the ONE-TO-MANY relationship.
Verify that in the SquadMember entity you have the following:
//Relation members with squad
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SQUAD_ID")
private Squad squad;
//Relation bidirectional for lieutenant
#OneToOne(fetch = FetchType.LAZY, mappedBy = "lieutenant")
private Squad squadLt;
//Relation bidirectional for commander
#OneToOne(fetch = FetchType.LAZY, mappedBy = "commander")
private Squad squadCm;
For example if you need to update commander, use squadCM, etc..
Related
I have a table which has a self reference join table to itself.
#Entity
public class Employee {
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
private List<EmployeeDependency> employeeDependencies;
}
#Entity
public class EmployeeDependency {
#ManyToOne
#JoinColumn
private Employee employee;
#ManyToOne
#JoinColumn
private Employee employeeDependency;
}
As we can see the relation is managed by mappedBy = "employee".
DB looks like this for the dependency table
EmployeeDependency
----------------------------------------
id employee_id employee_dependency_id
----------------------------------------
1 2 3
2 2 4
3 3 10
Now when I try deleting employee= 2 , it is deleting the references in EmployeeDependency table and also deleting the record from Employee table as dependency is managed by employee in the mapping mappedBy = "employee". This is expected and working as it is written.
But now when I try to delete employee = 10 which is in other side of relation, I was getting foreign key reference error.
I was able to delete it if I put a dummy reference in Employee entity like this
#Entity
public class Employee {
#OneToMany(mappedBy = "employee", cascade = CascadeType.ALL, orphanRemoval = true)
private List<EmployeeDependency> employeeDependencies;
#OneToMany(mappedBy = "employeeDependency", cascade = CascadeType.ALL, orphanRemoval = true)
private List<EmployeeDependency> dummyEmployeeDependencies;
}
But this doesn't seems to be right! how can I put a bidirectional cascade so I can delete ids from either side of relation
I have two entities, related as below
#Entity
#Table(name = "APPOINTMENT")
public class Appointment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long codeAp;
#ManyToOne(fetch = FetchType.EAGER)
, #OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "codeP")
private Patient patient;
//attributes
//getters and setters
//constructors
#Entity
#Table(name = "PATIENT")
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long codeP;
//attributes
//getters and setters
//constructors
I'm using JpaRepository delete method.
There is a constraint between the tables PATIENT and APPOINTMENT in database,
I want to remove orphans, when I remove Patient.
I added #OnDelete hibernate annotation but it doesn't work for me!
Can you please tell me why?
I want to keep that unidirectional relationship, can you please help me in this?
If you want to keep using the association as unidirectional only, you can define the lazy-loaded inverse side in a field without exposing getters and setters for it:
#Entity
public class Patient {
#OneToMany(mappedBy = "patient", orphanRemoval = true)
private Collection<Appointment> appointments;
}
This way orphanRemoval logic is applied from patients to their appointments and as a bonus you get the ability to navigate from patients to appointments in HQL queries.
Notice the mappedBy attribute which tells that appointments are responsible for the association management, so you continue to associate appointments with patients by setting patients in the many-to-one relation defined in the Appointment.
There is no way that you could achieve that automatic behavior on the #ManyToOne side. Its just semantically incorrect, period.
Taking under consideration though, the fact that you only want to have an uni-directional mapping and do not specify the Set<Appointment> dependency on the Patient, then a kind of workaround to your situation would be to replace the #ManyToOne with a #OneToOne relationship. Then you would be able to use orphan-removal functionality:
#OneToOne(fetch = FetchType.EAGER, orphanRemoval=true)
#JoinColumn(name = "codeP")
private Patient patient;
Keep in mind though that if you follow this path, adapt you code and at some point you will be in need to introduce #OneToMany dependency on the `Patient' side then you will stumble upon problems. So i would recommend working out pros and cons first in relation to future possible alteration to the entity graph.
I try to audit an entity but I don't want to audit its relationships. If I put #Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) in #ManyToOne relations, this works and I don't have any exception, but when I try to use the same annotation in a #onetomany with the param mappedby defined, I have an exception that says me that I have to audit the other entity.
Example:
#Table(name = "OWNERS")
#Entity
#EntityListeners(AuditingEntityListener.class)
#Audited
public class Owner {
...
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(fetch=FetchType.LAZY)
private User user;
...
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#OneToMany(cascade = CascadeType.ALL, mappedBy = "owner" )
private Set<Pet> pets = new HashSet<Pet>();
...
}
When you use #Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED) you are telling hibernate not to audit this entity but audit the relation so you hibernate will save the id of the referenced entity. Thats why Pet must be an #Audited entity.
If you do not want to store the relation at all you need to use #NotAudited
Check this Whats the difference between #NotAudited and RelationTargetAuditMode.NOT_AUDITED in Hibernate EnVers?
Well, I think you have two options here:
Actually audit the entity Pet, if applicable;
Use the annotation #NotAudited instead of #Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED). Think about it, The audit table for Owner doesn't have to persist the Pet's associated. If it does, use option 1.
Hope it helps!
I have a many-to-many relationship with three tables and entities adn the join table contains additional column. On both sides of the relationship I have set cascadeType.All
When I add new objects to owner side the merge method works fine but when I remove a child object from the owner and merge it, the corresponding rows in the join table will not be removed and I will have duplicate rows in there.
owner entity
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "definitionType")
private List<DefinitionProperty> definitionProperties = new ArrayList<DefinitionProperty>();
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "property")
private List<DefinitionProperty> definitionProperties= new ArrayList<DefinitionProperty>();
mapping entity
#Id
#JoinColumn(name = "dtid", referencedColumnName = "id")
#ManyToOne(optional = false)
private DefinitionType definitionType;
#Id
#JoinColumn(name = "prid", referencedColumnName = "id")
#ManyToOne(optional = false)
private Property property;
I am not calling remove method of my entity manager at all and I am expecting the cascading to remove the unwanted rows automatically. Is that possible? what should I do to in order to remove those rows?
I can add my code here if it help
It just needed orphanRemoval=true on the owner side.
i would like to create an application in this context : Zk 6, Spring v3.1.1, JPA 2.0, Hibernate 4.1.4, all with annotations but i have some pb with JPA concept.
Here are a type of case study :
3 tables, all linked via a join table ; we are dealing with cardinality 0, n.
So we have T_E_USER, T_E_TYPE and T_E_AIR.
Each table has a numeric ID, and a simple VARCHAR field.
A join table is created with T_J_USR_TPE_AIR with the 3 ID referenced by foreign keys forming a composed primary key.
I'm using Hibernate Tools for generate my entities (version JPA).
And that's where the problems start ....
I have, in each entity class, an attribute of type set with annotation # OneToMany.
I have a class representing the join that has an id attribute of complex type (another class) with an annotation EmbeddedId for a composite key.
And attributes representing the three entities with annotations # ManyToOne.
Here are my questions, because that's where I'm confused:
which should i set into the "mappedBy" attribute in the annotation # OneToMany of my entities?
Am I forced to do a class entity representing the join?
How does the CASCADE? Is it possible to use it in this context to enrich the join table "automatically"? Or should I manually instantiate the class representative of the join in order to persist the information myself?
A big thank you in advance for any kind soul who could give me a helping hand.
Thank you for your answers but one said "yes" when the other says "no" lol
Here's what I did during the day but I have not yet been tested.
In each entity table, i added a #OneToMany relation with mappedBy setted to the attribute defined in "join" entity :
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "aircraft",
cascade = { CascadeType.REMOVE })
private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);
...
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "userAccount",
cascade = { CascadeType.REMOVE })
private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);
...
#OneToMany(fetch = FetchType.LAZY,
mappedBy = "referenceType",
cascade = { CascadeType.REMOVE })
private Set<UserConfig> userConfigs = new HashSet<UserConfig>(0);
And i created a new Entity for the join table.
#Entity
#Table(name = "T_J_USR_RFT_AIR_URA")
public class UserConfig implements java.io.Serializable {
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "airId",
column = #Column(name = "URA_AIR_ID", nullable = false)),
#AttributeOverride(name = "usrId",
column = #Column(name = "URA_USR_ID", nullable = false)),
#AttributeOverride(name = "rftId",
column = #Column(name = "URA_RFT_ID", nullable = false))
})
private UserConfigId id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "URA_RFT_ID", nullable = false, insertable = false, updatable = false)
private ReferenceType referenceType;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "URA_USR_ID", nullable = false, insertable = false, updatable = false)
private UserAccount userAccount;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "URA_AIR_ID", nullable = false, insertable = false, updatable = false)
private Aircraft aircraft;
...
getter & setter
}
Where UserConfigId is :
#Embeddable
public class UserConfigId implements java.io.Serializable {
#Column(name = "URA_AIR_ID", nullable = false)
private Integer airId;
#Column(name = "URA_USR_ID", nullable = false)
private Integer usrId;
#Column(name = "URA_RFT_ID", nullable = false)
private Integer rftId;
...
getter & setter
}
What do you think about this practice ?
I just used "cascade" if an object of the join table is deleted in order to delete all element associated in the join.
Is it all right ?
Anyway thank you Tom, i will analyzed your link.
Thank you JMelnyk too.
You are welcome if you want to demonstrate what are the best practices for this case.
Three-way joins are tricky. I think what you've done, using an entity for the join table, is probably the right thing to do. To answer your questions:
Your #OneToMany attributes refer to the entity mapping the join table; they should be mappedBy the appropriate #ManyToOne attribute in that entity.
Yes, unfortunately, an entity for the join table is the best way to do this.
Cascades can be used to automatically add objects to the database, but not to create objects. You will need to create instances of the join entity in code.
which should i set into the "mappedBy" attribute in the annotation #
OneToMany of my entities?
mappedBy attribute represents a property name you are joining on. Read more...
e.g. AnyEntity holds List<Employee> which is joined on (mappedBy) department property in Employee entity, and that department property holds the association.
Am I forced to do a class entity representing the join?
No, you do not provide an entity class for join tables.
How does the CASCADE? Is it possible to use it in this context to
enrich the join table "automatically"? Or should I manually
instantiate the class representative of the join in order to persist
the information myself?
Yes it is possible to enrich associations of the entity and itself by marking associations with desired cascade type.
e.g. We have a Department which holds List<Employee> and I put CascadeType.PERSIST on employees. Now we populate department objects with its properties and employees. When we are finished, we persist only the department, and it will cascade operation to employees.