I have problem with annotation #OnDelete with #OneToMany relation.
public class Patent {
#OneToMany
#JoinCollumn(name = "parent_id")
#OnDelete(action = OnDeleteAction.CASCADE)
private List<Child> children;
}
public class Child {
}
When I run it I get this error: "only inverse one-to-many associations may use on-delete="cascade"". How I need to change code to get it functional, without bidirectional relation? I know that, it can be solved with adding #ManyToOne relation, with appropriate annotations, to Child class, but I do not want to use this solution.
Edit: Purpose for this is that i need to generate "on delete cascade" to foreign key constraint in exported ddl schema.
All you need is to use orphanRemoval parameter for your OneToMany relation. See https://docs.oracle.com/cd/E19798-01/821-1841/giqxy/ for reference.
Example:
#OneToMany(mappedBy="customer", orphanRemoval="true")
public List<Order> getOrders() { ... }
However I think that your mapping is quite wrong, for such relation you should add Patent field to your Child class, mark relation as ManyToOne, then use JoinCollumn and set the reference as parent_id. With mappedBy and orphanRemoval options inside Patent - usability will be the same as you want.
Related
I am using Hibernate as an ORM framework.
I have a bidirectional relationship that is implemented in Java as:
#Entity
#Table(name = "Parent")
class Parent {
...
#OneToMany(cascade = CascadeType.ALL, mappedBy="parent", orphanRemoval=true)
private List<Child> child;
}
#Entity
#Table(name = "Child")
class Child {
...
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "parent_id")
private Parent parent;
}
Further the relational database table "Child" does have the following foreign key specification
fk_child_parent FOREIGN_KEY (parent_id) REFERENCES parent(id) ON DELETE CASCADE
Question 1: Is it bad practice that the child class's foreign key is annotated with cascade = CascadeType.All? Based on my current understanding, I would assume that whenever I delete a child, I cascade the transaction and delete the parent as well.
Since the parent might have more than one child, this will leave some orphan childs which will be removed due to the orphanRemoval=true option. Is this correct?
Question 2: In Java I already specify the cascade operation from the parent down to the children with #OneToMany(cascade = CascadeType.ALL, ...). Is the SQL line, or at least the ON DELETE CASCADE part of it, superfluous?
Vice versa, if I have the SQL line ... ON DELETE CASCADE that specifies the foreign key, can I skip the #JoinColumn or #ManytoOne annotations?
Please excuse if this is trivial question. I am new to databases / ORM, trying to wrap my head around the concepts.
Using CascadeType.ALL is like using all of the CascadeType values i.e. CascadeType.PERSIST, CascadeType.MERGE etc.
Every CascadeType refers to an operation on the EntityManager. If you do entityManager.persist(parent) then Hibernate will automatically do entityManager.persist(child) for every element in the list if you CascadeType.PERSIST is enabled. Same goes for other cascade types.
I'm using SpringBoot, when a repository is called for the AModel, the repository is executing queries for BModel, CModel and DModel even when I am not calling for either CModel or DModel. any idea why this is happening and how I can prevent it?
#Entity
public class AModel extends Model {
#OneToOne(fetch = FetchType.EAGER)
#JsonIgnore
private BModel;
}
#Entity
public class BModel extends Model {
#OneToOne(fetch = FetchType.LAZY)
private CModel;
#OneToOne(fetch = FetchType.LAZY)
private DModel;
}
#Query("select a from com.project.models.AModel a where a.id = :id")
#Override
Candidate findOne(#Param("id")Long id);
The reason here is that when the entity AModel includes entity BModel, which in turn includes CModel and DModel. It has to fetch CModel and DModel when the fetch for AModel is called, otherwise your queries won't be able to complete if the objects of CModel and DModel are not fetched for, and the entire purpose of making the fetchType as Eager for AModel will be gone.
This happens cause of the oneToOne relations from BModel to CModel and DModel.
When you define a relation with FetchType.LAZY, then hibernate needs to substitute the object with a proxy, so that when you access this the first time, it can load it.
Now with oneToOne relation which is nullable, hibernate has no chance to know, if the relation is null or not without execute a select, cause the table in relation usually use the same primary key.
So if your relations are nonnullable, then define optional = false and no eager fetching is done. If this is not the case you could also use a oneToMany relation instead.
See also this stackoverflow question
I have a somewhat strange question, I don't know if this is supported in JPA:
I have an #Entity Child and two other entities, #Entity Parent1 and #Entity Parent2.
What I would like to do, is have a #OneToMany relationship between Parent1 and Child, and another #OneToMany relationship between Parent2 and Child.
The reason is that I want Childs to be deleted if their Parent1 is deleted, and Childs to be deleted if their Parent2 is deleted.
I have tried many combinations but I can't get it to work...
TL;DR: Any Child that does not have a Parent1 AND a Parent2 should be deleted.
Here is my code right now (ommitting #Id and getter/setters):
#Entity
class Parent1 {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Set<Child> childs;
}
#Entity
class Parent2 {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
Set<Child> childs;
}
#Entity
class Child {
String name;
}
Thank you,
Yes as per #jmvivo answer you need to use orphanRemoval=true is solution of your use-case ,
Here As per Oracle On this link
When a target entity in one-to-one or one-to-many relationship is removed from the relationship, it is often desirable to cascade the remove operation to the target entity. Such target entities are considered “orphans,” and the orphanRemoval attribute can be used to specify that orphaned entities should be removed. For example, if an order has many line items and one of them is removed from the order, the removed line item is considered an orphan. If orphanRemoval is set to true, the line item entity will be deleted when the line item is removed from the order.
You might also want to look at below SO Question as you go further with your requirement
One to Many relationship JPA/Hibernate removing links
JPA 2.0 orphanRemoval=true VS on delete Cascade
Reading OneToMany.ophanRemoval, you can try this:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval=true)
Set<Child> childs;
Good luck!
You will have to handle this on service/DAO level, I don't think you'll find any JPA support for this specific use case. Just manually check the conditions prior to deleting the parent/child.
I have referred to various docs on Hibernate and have finally come to a very simple answer on this question.
I just wanted to clarify whether my final simple answer on this question is correct or not. Please help
Here is my answer
Inverse is a an attribute of tag in hibernate. It is generally used in OneToMany / ManyToOne bidirectional mapping.
inverse="true" means the child entity is the owner of relationship between the two entities and the child entity should take care of persistence operation with the foreign key column in the child table.
You are right with your answer.
The reason for "inverse" is that in a database there is always one side controlling a relation (e.g. the FK side), while in your bidirectional domain model mapping you can make changes to both the one-to-many side (add/remove children) and the child side (setting the parent). That's why both sides need to stay in sync.
Hibernate can't automatically enforce the synchronism constraint, so it's the developer responsibility to make it happen. The only thing Hibernate can do is to observe one side only (in order to translate state transitions into SQL queries).
Therefore it's a good habit to provide add/removeChild like utility methods:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "parent", orphanRemoval = true)
private List<Child> children = new ArrayList<Child>();
public List<Child> getChildren() {
return children;
}
public void addChild(Child child) {
children.add(child);
child.setParent(this);
}
public void removeChild(Child child) {
children.remove(child);
child.setParent(null);
}
}
Adding to what #Vlad rightly pointed out, another side effect of how this impacts the DML query (atleast how Hibernate does it ) generated is that the DML for state change of non-inverse side is a single query (the update of foreign key is part of that DML) instead of two. This is well illustrated here.
I have two tables: t_promo_program and t_promo_program_param.
They are represented by the following JPA entities:
#Entity
#Table(name = "t_promo_program")
public class PromoProgram {
#Id
#Column(name = "promo_program_id")
private Long id;
#OneToMany(cascade = {CascadeType.REMOVE})
#JoinColumn(name = "promo_program_id")
private List<PromoProgramParam> params;
}
#Entity
#Table(name = "t_promo_program_param")
public class PromoProgramParam {
#Id
#Column(name = "promo_program_param_id")
private Long id;
//#NotNull // This is a Hibernate annotation so that my test db gets created with the NOT NULL attribute, I'm not married to this annotation.
#ManyToOne
#JoinColumn(name = "PROMO_PROGRAM_ID", referencedColumnName = "promo_program_id")
private PromoProgram promoProgram;
}
When I delete a PromoProgram, Hibernate hits my database with:
update
T_PROMO_PROGRAM_PARAM
set
promo_program_id=null
where
promo_program_id=?
delete
from
t_promo_program
where
promo_program_id=?
and last_change=?
I'm at a loss for where to start looking for the source of the problem.
Oh crud, it was a missing "mappedBy" field in PromoProgram.
Double-check whether you're maintaining bidirectional association consistency. That is; make sure that all PromoProgramParam entities that link to a PromoProgram as its parent are also contained in said parent's params list. It's a good idea to make sure this happens regardless of which side "initiates" the association if you will; if setPromoProgram is called on a PromoProgramParam, have the setter automatically add itself to the PromoProgram's params list. Vice versa, when calling addPromoProgramParam on a PromoProgram, have it set itself as the param's parent.
I've encountered this problem before as well, and it was due to not maintaining bidirectional consistency. I debugged around into Hibernate and found that it was unable to cascade the delete operation to the children because they weren't in the list. However, they most certainly were present in the database, and caused FK exceptions as Hibernate tried to delete only the parent without first deleting its children (which you've likely also encountered with the #NonNull in place).
FYI, I believe the proper "EJB 3.0"-way of making the PromoProgramParam.promoProgram field (say that a 100 times) non-nullable is to set the optional=false attribute on the #ManyToOne annotation.