I have the following Schema:
#Entity
public class A{
#OneToMany(mappedBy = "dest",cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<C> myDest; //Owns these
#OneToMany(mappedBy = "owner", cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<C> myParents; //can be reached from these
#ManyToMany
private Collection<B> myOwnB;;
}
#Entity
public class B{
#OneToMany(mappedBy = "forB",cascade = CascadeType.ALL, orphanRemoval = true)
private Collection<C> associatedC;
#ManyToMany(mappedBy = "myOwnB")
private Collection<A> associatedA;
}
#Entity
public class C{
#JoinColumn(referencedColumnName = "myDest")
#ManyToOne
private A dest;
#JoinColumn(referencedColumnName = "myParents")
#ManyToOne
private A owner;
#JoinColumn(referencedColumnName = "associatedC")
#ManyToOne
private B forB;
}
Here, if I delete a record of A or B, all the associated Cs get deleted. Perfect.
So my problem is this:
If I delete a reference of B from A (in that ManyToMany relation), I need all the Cs deleted as well for that A and B. (All Cs where forB=removedB and owner=sourceA). I have now written a similar query and it works fine now that I explicitly execute in my EJB. Is there a workaround or a annotation saying that a record of C gets deleted if one of the attributes(column) become null?
Related
Let's say I have the following entities and associations:
Entity A:
#Entity
public class A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "a", cascade = CascadeType.ALL, orphanRemoval = true)
private List<B> b;
}
Entity B:
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id")
private A a;
#OneToMany(mappedBy = "b", cascade = CascadeType.ALL, orphanRemoval = true)
private List<C> c;
}
Entity C:
#Entity
public class C {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "b_id")
private B b;
}
Using the .findAll() method of the CrudRepository for entity A, it will return each A with its associated B's in a list. Also, each B will have each of its associated C's in a list.
My question is: If I in some cases only want to load all A's with their B's, but I don't want the C's in the B's, would that be possible? Could I create a custom query to do that, or is there another way? I hope it is clear what I want to achieve.
I think your problem is mappedBy values in one side of OneToMany relationships.
mappedBy value must be the the name of variable in the other side. So in your cases, you can do this:
In Entity A: change mappedBy = "citizen" to mappedBy = "a"
In Entity B: change mappedBy = "citizen" to mappedBy = "b"
I know Entity a,b,c is just an example, but you should follow above pattern when designing your models relationships.
I have a set entities such as:
public class EntityToDrop {
#NaturalId
#ManyToOne(fetch = FetchType.EAGER)
private ParentEntity parentEntity;
#OneToMany(mappedBy = "entityToDrop", cascade = {CascadeType.REMOVE, CascadeType.REFRESH}, orphanRemoval = true)
private Set<OtherEntity> otherEntities= new HashSet<>();
[...] (other fields and relationships)
}
public class ParentEntity {
#OneToMany(mappedBy = "parentEntity", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<EntityToDrop> childEntities = new HashSet<>();
[...]
}
public class OtherEntity{
#ManyToOne(optional = true,fetch = FetchType.EAGER)
#NaturalId
private EntityToDrop entityToDrop;
[...]
}
In some part of the code I'm calling
parentEntityInstance.getChildEntities().remove(entityToDrop);
[...]
entityManager.persist(parentEntityInstance);
Which I expect should always remove entityToDrop from the database, since it's marked with orphanRemoval. However, when the relationship with OtherEntity exists it's not getting removed, and more interestingly, if I remove the relationship with OtherEntity then try to remove it from the ParentEntity in the same transaction, it's not getting dropped either.
Is there any case where orphanRemoval = true will not work as I expect it to? And as a followup how would I figure out what's stopping my entity from being removed in this case?
I have multiple back-reference classes in a class. Since I use #JsonBackReference for them, I get an error. I assigned #JsonIdentityInfo annotation for those classes, but I still get the same error.
public class X implements Serializable {
....
//bi-directional many-to-one association to Booking
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "xxA", nullable = false)
#JsonBackReference
private A a;
//bi-directional many-to-one association to Client
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "xxB", nullable = false)
#JsonBackReference
private B b;
...getters setters
}
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
public class B implements Serializable {
........
//bi-directional many-to-one association to BookedClient
#OneToMany(mappedBy = "b", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonManagedReference
private List < X > xxB;
........ getters setters
}
#JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class, property = "#id")
public class A implements Serializable {
........
//bi-directional many-to-one association to BookedClient
#OneToMany(mappedBy = "a", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JsonManagedReference
private List < X > xxA;
........ getters setters
}
error:
com.fasterxml.jackson.databind.JsonMappingException: Multiple back-reference properties with name 'defaultReference'
How can I resolve this error? Can I not use multiple back-reference in a class?
According to Jackson's javadoc, both #JsonManagedReference and #JsonBackReference accept a name value that binds them together:
#JsonBackReference("a")
private A a;
#JsonManagedReference("a")
private List < X > xxA;
I also faced this issue, but in the last I resolved it.
//This is parent class
#Entity
#Table(name = "checklist")
#JsonIgnoreProperties("inspection")
public class Checklist implements java.io.Serializable {
#ManyToOne
#JoinColumn(name = "product_id", referencedColumnName = "id")
#JsonBackReference
private Product product;
#OneToMany(mappedBy = "checklists", cascade = CascadeType.ALL)
#JsonManagedReference
private Set<Inspection> inspection = new HashSet<Inspection>();
//Constructor
//Getter and Setter
}
//This is child class
#Entity
#Table(name = "inspections")
public class Inspection {
#ManyToOne
#JoinColumn(name = "chk_id", referencedColumnName = "id")
private Checklist checklists;
//Constructor
//Getter and Setter
}
By mentioning #JsonIgnoreProperties("inspection") and #JsonManagedReference in Parent class
Resolved the issue raised by using two #JSONBackRefrence in same parent class.
So this did actually take me a while...
You can annotate your referencse accordingly using #JsonIdentityReference(alwaysAsId = true) and then leave it off the master reference
#JsonIdentityReference(alwaysAsId = true)
private Set<PackInstructionGroup> groups = new TreeSet<>();
In my application I have the following model relationship.
public class EntityA {
(...)
#OneToMany(fetch = FetchType.EAGER, mappedBy = "a")
#Fetch(FetchMode.SELECT)
private List<EntityB> listOfBs;
(...) // and getters and setters
}
public class EntityB {
(...)
#OneToOne
#JoinColumn(name = "idA")
private EntityA a;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "b")
#Fetch(FetchMode.SELECT)
private List<EntityC> listOfCs;
(...) // and getters and setters
}
public class EntityC {
(...)
#ManyToOne
#JoinColumn(name = "idB")
private EntityB b;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "c")
#Fetch(FetchMode.SELECT)
private List<EntityD> listOfDs;
(...) // and getters and setters
}
public class EntityD {
(...)
#ManyToOne
#JoinColumn(name = "idC")
private EntityC c;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "d")
#Fetch(FetchMode.SELECT)
private List<EntityE> listOfEs;
(...) // and getters and setters
}
public class EntityE {
(...)
#ManyToOne
#JoinColumn(name = "idD")
private EntityD d;
private Date dateA;
private Date dateB;
private Float float;
private String stringX;
(...) // and getters and setters
}
The idea here is:
each EntityA can have one or more EntityBs that are associated
from a DualListBox element;
each EntityB can be assigned to only
one EntityA;
each EntityB can have one or more EntityCs. When
the EntityC is created, a List<EntityD> is created, one EntityD
is created and added to listOfDs and set into EntityC. Also, a
List<EntityE> is created, one EntityE is created and added to
listOfEs and set into EntityD.
I hope I managed to make myself clear on the business rules above.
The problem is the removal of an EntityA and its cascading effect.
On my EntityAServiceThe code I'm trying is:
public removeA(String idA) {
A a = service.findAById(idA);
for (EntityB b : a.getListOfBs()) {
for (EntityC c : b.getListOfDs()) {
for (EntityD d : c.getListOfDs()) {
for (EntityE e : d.getListOfEs()) {
service.removeE(e);
}
service.removeD(d);
}
service.removeC(c);
}
b.setA(null);
}
a.setListOfBs(null);
update(a);
remove(a);
}
When I try to remove an entityA, I the entityBs are disassociated from entityA, but all entityCs associated to a EntityB are kept. Same goes, respectively, to entityDs and entityEs.
I have been at this for a while now but still can't see the problem. What's going on and how to fix?
I have three classes.
Class EntranceExam :
#Entity
public class EntranceExam extends GenericModel implements Comparable<EntranceExam> {
#OneToMany(cascade = CascadeType.REMOVE, mappedBy = "entranceExam")
public List<Examination> examinations;
}
Class Examination :
#Entity
public class Examination extends Model implements Comparable<Examination> {
#ManyToOne(optional = false)
public EntranceExam entranceExam;
#OneToOne(cascade = CascadeType.ALL, mappedBy = "examination")
public Question mainQuestion;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "examination", fetch = FetchType.LAZY)
public Set<Question> questions = new HashSet<Question>();
}
Class Question :
#Entity
public class Question extends GenericModel implements Serializable,
Comparable<Question> {
#ManyToOne
public Examination examination;
#Sort(type = SortType.NATURAL)
#OneToMany(fetch = FetchType.EAGER, mappedBy = "parentQuestion", cascade = { CascadeType.ALL })
public Set<Question> childQuestions;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "PARENT_ID", nullable = true)
public Question parentQuestion;
}
So, when I use it all together :
EntranceExam entranceExam = EntranceExam.findById(id);
System.out.println(entranceExam.examinations.size());
It returns the number of questions in entranceExam's examinations instead of the numbers of examinations.
If I simplify, the matching query in MySQL log looks like this :
select examinations0_.entranceExam_id as entranceExam4_108_3_,
examinations0_.id as id3_
from Examination examinations0_
left outer join Question question1_
on examinations0_.id=question1_.examination_id
left outer join Question question2_
on question1_.PARENT_ID=question2_.questionId
where epreuves0_.entranceExam_id=4;
Where is the problem in my Java code ?
You may need to wrap entranceExam.examinations into a HashSet, by default hibernate creates one parent object for each child.
Set examinations = new HashSet(entranceExam.examinations)
An other way is to set the distinct strategy
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);