JPA is not persisting child entities created with static methods - java

I frequently have to create a record that has child records. I created a utility class U that is not managed by the container with a static method to create the objects. It creates the required entities and relates them to each other and then returns the parent entity - BUT DOES NOT PERSIST.
When I go to persist the parent entity I expect all the attached child entities to be persisted as well but only the parent is persisted.
In another place, quite by accident, I noticed that if I created and attached the entities to the parent in class X that is not directly managed by the container but was created by a class C that was managed by the container - everything works as expected. One other thing to note is that in this case the parent already existed so I set the ID on the child when I created it.
Does this mean I cannot create entities using static methods? Or is there something else going on?
Here are the entities in question.
PARENT:
#Id
#GeneratedValue(generator = "INFO_ID_GEN", strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "INFO_ID_GEN", sequenceName = "INFO_ID",allocationSize=1)
#Column(name="INFO_ID")
private int infoId;
#OneToOne(mappedBy="PARENT", cascade=CascadeType.ALL, fetch = FetchType.EAGER)
private CHILD child;
CHILD:
#Id
#Column(name="INFO_ID", insertable=false)
private int infoId;
#OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE})
#JoinColumn(name="INFO_ID")
private PARENT parent;
If you need anything else let me know.

Related

Fetching a list of parent entities with a filtered collection of children in Spring Data JPA

So I've been stuck on this problem for about half a day so I am wondering if I am just over-complicating things.
My application has three different Java object classes: Grandparent, Parent, and Child. Each Grandparent contains a List of Parents, and each Parent contains a List of Children. Child has an "isWellBehaved" property, which is a boolean.
We are using Spring Data JPA and Hibernate in order to map the data to a database. Our application contains a lot of nested entities and circular relationships and we are relying on projections to keep our request size down.
The Problem: given a grandparent id, I want to return a list of all Parents (as projections). I want each of the Parents to contain a list of Child projections, but only if the Child is well behaved. The rest of the children in the collection should be filtered out from the collection.
What would be the simplest way to achieve this? We are not using Hibernate filters at the moment and I am not keen on introducing them as we are not likely to need them anywhere else (either way, would it be suited for this purpose?). I have used JPA Criteria API predicates (very little) but find it difficult to adapt that to this particular scenario. Is a native query the way to go? I've started going in that direction but am having some issues mapping all the fields to our Spring entity due to all the nested dependencies so just want to make sure I am even headed in the right direction before I continue.
My (simplified) parent entity class looks like this:
#Entity
#Table(name="parent"
public class Parent {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="parent_id")
Integer id;
Integer grandparentId;
#OneToMany(mappedBy = "parent")
List<Child> children;
}
Child class:
#Entity
#Table(name="child"
public class Child {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="child_id")
Integer id;
#ManyToOne
#JoinColumn(name="parent_id")
Parent parent;
boolean isWellBehaved;
}
Parent repository interface:
#RepositoryRestResource(excerptProjection = ParentProjection.class)
public interface ParentRepository extends JpaProjectionRepository<Parent, Integer, ParentProjection> {
List<ParentProjection> findAllByGrandparent_Id(Integer grandpaId);
}
You can use #Where annotation of hibernate on collection. It will be something like
#Entity
#Table(name="parent"
public class Parent {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="parent_id")
Integer id;
Integer grandparentId;
#Where(clause = "isWellBehaved=true")
#OneToMany(mappedBy = "parent")
List<Children> children;
}
As you have said:
I want each of the Parents to contain a list of Child projections, but only if the Child is well behaved.
List<childerns>allChilderns=parentsList.stream().map(parent>dao.findchildernByParentId()).collect(Collectors.List());
allChilderns.stream().filter(childern->childern.isWellBehaved()==true).collect(Collectors.toList());
Get all the parents by GrandParentId--the one which you are doing.
Once you got all the parents,for each parent findchildernByParentId.
and then filter out the childern on the basis of condition.
Let me know:)

How to prevent hibernate from creating a proxy

I have a tricky problem with hibernate using more queries than necessary for a simple findAll call. In my model there is two entities Parent and Child with oneToMany association;
Parent
class Parent{
#id
private long id;
//unique
private String code;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
private List<OperatorAttribute> children;
}
Child
class Child{
#id
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_code", referencedColumnName = "code")
#LazyToOne(LazyToOneOption.NO_PROXY) // here i'm trying to tell hibernate to create no proxy and just ignore the field but no luck :/
public Parent parent;
}
The problem is that whenever I try to fetch the list of child using childRepository.findAll() from the database, hibernate make N+1 select query, why ?
I think this may be the explanation for that: IMHO when Hibernate populate the child object, he tries to create a proxy for the parent field,
and for that he needs the id of the parent row, which should normally be the foreign key in the child table, but in my case the #fk isn't binded to the primary key of the Parent table but to a unique column (plz don't ask me why) so in order to populate the id he needs to do an additional select query just to initialize the proxy of the parent field.
So my question is how to prevent Hibernate from creating a proxy for the parent field.
Thanks.
You are right. The proxy needs the #Id of the proxied entity (this way it could be made sure that it could be find). As soon you define the LazyToOneOption.NO_PROXY it tells the system to give back the real object. And this is what happens here. What you get mapped on the result is not a proxy, because with this annotation you explicitly disabled it so you have to get the real object.
Based on the mapping provided you cannot ignore the field because You'll loose the information what was is the Parent on the Child. So with this kind of setup you'll always need to read the parent.
If this field is not needed at all in a specific area, you can create some other mappings to the same table. But be careful! This could introduce a load of other cache related problems.

Hibernate: mapping parent to child entity with 2 references to one column in child?

I am trying to have a cascade delete within my entities. But I think that it is being stopped by the fact that I have 2 references to the one column in my child entity.
In my child Dog entity I originally had the following field:
#Column(name = "KENNEL_ID", insertable = false, updatable = false)
private String kennelId;
I then added this because I wanted to get a list of all child entities related to the parent:
#ManyToOne
#JoinColumn(name = "KENNEL_ID" )
private Kennel kennel;
In my parent Kennel entity I also added this to refer to the field in the child I added:
#OneToMany(mappedBy = "kennel",cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private List<Dog> dogList= new ArrayList<Dog>();
Before I added the 2nd child reference and the parent references, cascade delete worked for all of my entities. However since I have added them it does not.
How can I fix this?
It is not a problem of mapping parent and child to the same class.The problem is that you need to maintain both ends of the bi-directional-relationship by hand.
child.setParent(parent)
parent.addChild(child)
BTW: Setting it only on one side (the one which is responsible to store the relationship in the database), store and reload the entity will work in some cases too. (And you will find this dirty trick in many old tutorials). But in my opinion it is bad practice. (In your test case, it would require to clean the cache before you reload the parent after the child is saved.)
public void setDogList(List<Dog> dogList) {
this.dogList.clear();
this.dogList.addAll(dogList);
}

Hibernate #OneToOne with updatable = false still updating

I have an hibernate managed entity:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// (omitted useless details)
#OneToOne
#JoinColumn(name = "child_id", updatable = false)
private Child child;
// (omitted useless details)
}
--
#Entity
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// (omitted rest...this is a POJO)
}
Since the responsibility of the update for Child is managed elsewhere, my goal is to simply not update child when the parent is saved. Unfortunately, this updatable = false on the #JoinColumn appears to be getting ignored, saving any changes made to both parent and child.
How can I achieve only saving the parent in a one to one relationship?
Any help is appreciated.
EDIT: Clarification, the Child class is an #Entity as well, but a simple POJO. The relationship between Parent and Child is unidirectional from the parent.
I looked into this for quite a while and looking at the documentation for #JoinColumn annotation and the updatable/insertable properties it says...
"(Optional) Whether the column is included in SQL UPDATE statements generated by the persistence provider."
What this means is setting updatable = false will only prevent the foreign key from being updated (column is included). When you save the parent entity it will also save all child entities attached to the object.
This means that any child properties that are attached to the parent entity when you save will also be persisted. This brings another issue to light if someone else may have updated this child entity, but because you are saving the parent entity with the child entity attached to it, you will un-knowingly overwrite any changes made to the child object.
Solution:
AFAIK the only way to resolve this is to be diligent about the object you save, unless you really need to be saving/updating the child entities don't. Keep your objects self contained, this is the downfalls of lazy/eager loaded properties and saving that same object graph. You could also do like another poster said and set the child entity to null before the save to prevent any updates, but can easily be missed.
If you are OK with saving the entire object tree (parent and children entities) you could add a property that utilizes the #Version attribute to introduce optimistic concurrency. This way if any entity was updated by someone else and you are trying to save an old version it will fail.
#Version
#Column(name=”version”)
private long version;
public long getVersion() { return version; }

Cannot delete detached child from collection in parent in Hibernate

I have the following entities with a parent-child relationship:
public class Parent {
#Id #GeneratedValue String id;
#Version Long version;
#OneToMany(mappedBy = "parent", orphanRemoval = true)
#Cascade({CascadeType.ALL})
Set<Child> children;
// getters and setters
}
public class Child {
#Id #GeneratedValue String id;
#ManyToOne
#JoinColumn("parent_id")
Parent parent;
// getters and setters
}
I retrieve a Parent for edit on the web UI by copy properties to a ParentDto, which has a list of ChildDtos.
Once I'm done editing, I send the ParentDto object back and copy all properties into a new Parent object (parent) with a new HashSet to store the Children created from the list of ChildDtos.
Then I call getCurrentSession().update(parent);
The problem
I can add children, update children, but I can't delete children. What is the issue here and how do I resolve it?
Thanks in advance.
You have a bidirectional association, you need to remove from Child class the link to the parent class, try to make Parent reference to null, and also set the Set<Child> to a new HashSet<Child> or whatever your implementation is.
Then save the changes that will remove the children form the table.
This action can only be used in the context of an active transaction.
public void remove(Object entity);
Transitions managed instances to removed. The instances will be deleted from the datastore on the next flush or commit. Accessing a removed entity has undefined results.
For a given entity A, the remove method behaves as follows:
If A is a new entity, it is ignored. However, the remove operation cascades as defined below.
If A is an existing managed entity, it becomes removed.
If A is a removed entity, it is ignored.
If A is a detached entity, an IllegalArgumentException is thrown.
The remove operation recurses on all relation fields of A whose cascades include CascadeType.REMOVE. Read more about entity lifecycle

Categories