Hibernate #OneToMany how to persist the relationship without updating the (many) elements? - spring

I have
public class Stale {
#OneToMany(fetch = FetchType.LAZY, mappedBy = "stale", cascade = CascadeType.ALL)
private List<Poney> poneys;
}
and
public class Poney {
#ManyToOne()
#JoinColumn(name = "stale_id")
private Stale stale;
private String name;
}
When I try to save a stale with an updated poney list, the persisting of a stale doesn't behave like I would it to do.
[GOOD] added poneys (existing in te db) are well persisted as stale members
[BAD] poneys removed from the stable are still in the stable after save(). Caution : I want them removed from the stable but I don't want to kill any pony from the db !
[BAD] if the poney's name is edited before persisting the stale, the name modification is persisted. But I would like to persist only the relationship, not poney modifications.
Can someone help me ?

The problem is that this is a bi-directional relationship. When updating the state of a bidirectional relationship it is applications duty to update the state on both sides (so not only remove Ponies from Stable, but also remove the Stable reference in Ponies). That can be accomplished for example by #BeforePersist or #BeforeUpdate method in the Stable entity. Since Pony is the owner of the relationship JPA sees the state of Pony as more important than state of Stable, and removing ponies in Stable does nothing.
As for your last point - again, since this is bidirectional and Pony is the owner it's impossible to change the state of the relationship without saving new state of Pony (including any changes like name).
They way you want this to behave makes me think what you actually want is a uni-directional relationship on the Stable side:
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="stale_id", referencedColumnName="id")
private List<Poney> poneys;
And removing the #ManyToOne mapping in Pony.

Related

How can I get an Entity with its referenced entity ids in a ManyToMany relation?

I have an basic spring application that uses hibernate and mapstruct
There are two Entities, each are implemented to have their subchild entities as List attribute in a ManyToMany relation
So there is
EntityA.class
with List<EntityB> (fetchType Lazy)
and vice versa
Now when my client calls, it wants to get a DTO that represents like following:
EntityADTO
with List<Long> entityBIds
How can I get my EntityA with only the Ids of EntityB most efficient and without loading the complete EntityB and post process it after?
Thanks a lot!
The #ManyToMany association information is persisted in a dedicated (join-)table and is loaded lazily on collection access, so there needs to be another query.
Instead of querying for the complete information of all associated entities, you could specifically query only for the needed id property.
Possible queries could look e.g. like this:
// Spring-Data repository (requires an extra interface for the result):
interface IdOnly(){
Long getId();
}
interface EntityBRepository extends JpaRepository<EntityB, Long> {
List<IdOnly> getIdByEntityAId(Long enitityAId);
}
// alternative JPQL query (does not need the interface):
#Query("SELECT b.id FROM EntityB b JOIN b.entityAs as a WHERE a.id=:entityAId")
List<Long> getIdByEntityAIdJpaQuery(#Param("enitityAId") Long enitityAId);
This way, only the needed EntityB ids for an associated EntityA are loaded from the DB.
For even further tuning, one could also write a native query directly accessing only the join-table, which avoids all joins:
#Query(nativeQuery = true, //
value = "SELECT entityBId FROM entityA_entityB WHERE enitityAId=:enitityAId")
List<Long> getIdByEntityAIdNative(#Param("enitityAId") Long enitityAId);
For executing the query when mapping with mapstruct, you can use the spring repository bean e.g. as described here: https://stackoverflow.com/a/51292920
In addition to #Fladdimir's answer which is a great approach if you only need the list of values occasionally, JPA allows defining Entity Graphs that can specify what in an object graph you want loaded. This can allow you to define your entity and specific attributes from child/referenced entities in the graph, allowing objects to be returned but the bulk of the data unfetched. This can allow you to process Entity B instances, but without them being fully populated.
There are many tutorials but I've referenced https://www.baeldung.com/jpa-entity-graph more than once. As the tutorial referenced mentions though, Hibernate might have some issues with how it handles attributes that are normally eagerly fetched, so it might not work the way you want (but will with other JPA providers like EclipseLink, which is where I've used this).
Alternatively, if this is a collection of IDs you are going to want/need frequently, you can modify your object model to have them fetched differently.
public class EntityA {
..
#ElementCollection
#CollectionTable(name = "RELATION_TABLE_NAME", joinColumns = #JoinColumn(name = "A_ID", insertable=false, updatable=false))
#Column(name = "B_ID", insertable=false, updatable=false)
List<Long> bIds;
}
This allows fetching the foriegn keys automatically in your AEntity. I've made it read-only, assuming you'd keep the existing A->B relationship and use that to set things. Doing so though means that these two relationships are entirely separate, and so might result in different queries to fetch this same set of data.
If that is a concern, you can alter things again, and remove the existing A->B relationship, and stick it in an intermediary object AB.
public class EntityA {
..
#ElementCollection
#CollectionTable(name = "RELATION_TABLE_NAME", joinColumns = #JoinColumn(name = "A_ID"))
List<AB> listOfBs;
}
#Embeddable
public class AB {
#Column("B_ID", insertable=false, updatable=false)
Long bId;
#ManyToOne(fetch=LAZY)
#JoinColumn(name = "B_ID")
B b;
}
This would allow you to fetch As and use B's ID values without having to fetch from the B table. Note that I've marked the basic bId property as read-only, assuming that your existing app would be setting things by assigning a B reference to the relationship, but you could mark the relationship as read-only instead, and set the FK value using the bId. This might be more efficient long term, as you don't have to look up the B instance to set the relationship.
Alternatively again, you can make AB an entity instead of an embeddable, and allow it to exist and be queried upon outside of As and Bs. There are quite a few options though, and ways to map it, and not likely necessary for a simple model and use case.

Hibernate N+1 issue for Multiple children

I have a entity class which has multiple children with oneToMany association:
public class A{
private Long id;
private String name;
#OneToMany(mappedBy = "A", fetch = FetchType.LAZY, cascade = CascadeType.ALL,
orphanRemoval = true)
private List<B>bList= new ArrayList<>();
#OneToMany(mappedBy = "A", fetch = FetchType.LAZY, cascade = CascadeType.ALL,
orphanRemoval = true)
private List<C>cList= new ArrayList<>();
#OneToMany(mappedBy = "A", fetch = FetchType.LAZY, cascade = CascadeType.ALL,
orphanRemoval = true)
private List<D>dList= new ArrayList<>();
//getters and setters
}
For B,C and D I have set ManyToOne. In a word, they are in a bi-directional relationship.
Now, If I fetch A by id, I see a lot of queries get fired which turns out to be N+1 problem. To solve this, I added #Fetch(FetchMode.SUBSELECT) to all of the oneToMany relationships above which cause less queries to be fired.
My question is:
is it okay using #Fetch(FetchMode.SUBSELECT) or I can optimize it further?
what if I want to fetch all "As" by calling findAll() method? What should be the syntax for multiple children? Like
"select a from A a join fetch a.b then ??"
List< A > findAll()
Now, If I fetch A by id, I see a lot of queries get fired which turns out to be N+1 problem. To solve this, I added #Fetch(FetchMode.SUBSELECT) to all of the oneToMany relationships above which cause less queries to be fired.
You are not saying how/when these queries are fired, so a possible reason for the problem is that you are returning entities from your HTTP endpoints which are then serialized. Using #Fetch(FetchMode.SUBSELECT) is one way to "improve" the performance but will only work nicely if the base query which you use to fetch A is simple. If it gets too complex (pagination, complex predicates, etc.) you should stick to the default which is SELECT and instead configure a proper batch size via the #BatchSize( size = 32 ) annotation. A good value for the batch size would be the amount of A instances that you expect to be returned such that only a single query is executed per collection. If you allow a max page size of e.g. 50, you setting the value to 50 would be perfect.
List< A > findAll()
Do not ever do this if you care about performance and usability. It rarely makes sense to allow returning all elements, as no user can handle more than ~20 elements at a time anyway. As mentioned before, always have some kind of upper limit on the page size to prevent misuses that can cause performance issues.

Hibernate: What is the correct way to update collection of an entity?

I am trying to optimize our hibernate application. In many places, while add/updating a collection of an entity, we are clearing it first, and then add all of them again from the input. I feel somehow, there should be a better way but could not found yet.
How do you do in your applications.?
I guess with collection, you mean the "many" side of an #OneToMany relationship.
In this case, you can specify the value orphanRemoval in the #OneToMany annotation to the foreign reference in the owning class:
public class Parent {
// ...
#OneToMany(mappedBy = "whatever", orphanRemoval = true, cascade = CascadeType.ALL)
private List<Child> children;
}
orphanRemoval will instruct Hibernate to delete all existing child entities that have been removed from the collection when the parent entity is persisted.

Update/Save data in Spring JPA

I hope someone can help me solve this problem
I am using spring and JPA to save data.
When I try to save calling my DAO, not all the data gets saved.
Consider the following structure
class User
var name
var surname
#OneToMany(mappedBy = "rule",cascade = CascadeType.PERSIST)
#Fetch(FetchMode.SUBSELECT)
set<Address> address;
#OneToMany(mappedBy = "rule",cascade = CascadeType.PERSIST)
#Fetch(FetchMode.SUBSELECT)
set<Job>job;
class Address
List<AddressList>addressList;
class Job
List<JobList>jobList;
What basically happens is that name and surname changes but if I make any change related to the address or job class, these aren't committed.
However, if I delete the user, it works,if I retrieve the user information(addresses,jobs),these are retrieved correctly.
Any advice on what could be the issue?
You're only cascading the persist operation. When you update an already persisted entity, a merge operation will be performed. So, I suggest to cascade merge operation as well: CascadeType.MERGE if you want to save the related entities on update, too.
Try cascade type all --- CascadeType.ALL.
For orphan removals you should use :
CascadeType.DELETE_ORPHAN - if you are using hibernate
orphanRemoval = true - if you are using jpa 2.0
manual removal - if none of the above apply
Have decalre your transient object #Transient ? ok BTw --
follw below steps:
declare your teansient objects as #Transient
Use cascade type all --- CascadeType.ALL.
try to flush your entitymanager by using entityManager.flush() then perform persist() or merge() operation.

#Transactional(readOnly = true) leads to LazyInitializationException

I have a many-to-many relation with an additional column in the link table. I've configured it in a way that the owning side fetches children eager (so I don't get LazyInitializationException) and in the opposite direction it is lazy. This works.
I now wanted to fine-tune the transactions (before there was just #Transactional on class level of DAO and Service classes. I set method getById to readOnly = true:
#Transactional(readOnly = true)
public Compound getById(Long id) {
return compoundDAO.getById(id);
}
After this change I get a LazyInitializationException in following snippet:
Compound compound = compoundService.getById(6L);
Structure structure = compound.getComposition().get(0).getStructure();
System.out.println("StructureId: "+ structure.getId()); // LazyInitializationException
If I remove (readOnly = true) this works! Can anyone explain this behavior? I use Spring + Hibernate. Kind of confusing as I don't see any reason why this should affect which data is loaded?
EDIT:
Snippets of relationship definitions. This is a many-to-many with a column in the link table.
Owning side (eg Compound contains Structures):
#OneToMany(fetch = FetchType.EAGER, mappedBy = "pk.compound",
cascade = CascadeType.ALL, orphanRemoval = true)
#OrderBy("pk.structure.id ASC")
private List<CompoundComposition> composition = new ArrayList<>();
Belongs to side:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.structure",
cascade = CascadeType.ALL)
#OrderBy("pk.compound.id ASC")
private List<CompoundComposition> occurence;
Many-To-One in #Embeddable ID class
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Compound getCompound() {
return compound;
}
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
EDIT 2:
Stack Trace
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:165) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:272) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:185) ~[hibernate-core-4.1.7.Final.jar:4.1.7.Final]
at org.bitbucket.myName.myApp.entity.Structure_$$_javassist_0.getId(Structure_$$_javassist_0.java) ~[classes/:na]
at org.bitbucket.myName.myApp.App.main(App.java:31) ~[classes/:na]
EDIT 3:
Also see my comment:
Log is very different with readOnly and it is missing the part were the relations are loaded, eg. some selects are missing in the log.
EDIT 4:
So I tired with a basic DriverManagerDataSource and no Connection pool. The issue is exactly the same. For me looks like an issue in Hibernate.
This is just wow. I'm starting to understand why some people hate ORMs...Just feels like I'm constantly having to spend hours to solve a weird issue and the solution is a very specific set of annotations + some code to work around the limitations of said annotations.
First to why this happens (why meaning with which annotations, but not in terms of making logical sense, which is the actual problem here as using common-sense is useless. Only trial and error helps). In the owning side, in #OneToMany I have orphanRemoval = true (which I have found out is required for consistency. one would think database constraints should handle that...just one of the many things that can drive you crazy.). It seems that if the transaction is not read-only, then this setting leads to some data being fetched even so its lazy, namely here:
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
In a read-only transaction, this fetching does not happen. I would guess because if you can't change anything you will also not have to remove orphans and hence any data that the logic behind this setting requires is not needed in a read-only tx.
So the obvious solution would be in above relation to change to FetchType.EAGER. Wrong! If you do that you will not be able to update the owning side (Compound) using session.merge. This will lead to a StackOverFlowError.
The real solution was actually already mentioned. Just leave the config as is but explicitly load the desired relations in the Service layer:
#Transactional(readOnly = true)
#Override
public Compound getById(Long id) {
Compound compound = compoundDAO.getById(id);
for (CompoundComposition composition : compound.getComposition()){
Hibernate.initialize(composition.getStructure());
}
return compound;
}
I admit I'm tending to fall in the premature optimization trap. This doesn't look very efficient and also seems to break how SQL works in the first place. But then I'm in the lucky position that in most cases CompoundComposition will contain only 1 or 2 elements.
Perhaps you could put
value.getComposition().get(i).getStructure();
in the body of the getById() method, so that the lazy loading happens within the transaction. I realize in this case you'd have to loop over i which might be inconvenient.
Two things :-
Lazy fetch works on Collections Interface. Since ...
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
public Structure getStructure() {
return structure;
}
... this is not a collection interface (like List<Structure> would have been), it will be fetched in Eager fetch mode.
Make service method as transactional. It seems that after fetching from the dao layer, your structure is detached with NEVER flush mode. This is the underlying ORM issue I guess.

Resources