Have an entity with a OneToMany relationship in Spring Data that I would need to filter by a specific column. I've been unable to find a satisfying solution to get it with a single query (both parent entity and the filtered OneToMany in one go) so I'm now thinking in having it lazy fetched and after getting the parent entity, have a separate query to retrieve the linked entities and just using a setter to link them together (ie: parent.setChildren(childrenRepository.findAllBy...)).
Question is: would this effectively disable any future trigger to the lazy fetch so my manually set children would remain? Use case here is to add these children manually in certain cases, and using the default lazy fetch when the conditions don't apply.
I tested this and seems to be working as intended, but could not find information on when the lazy fetch should be triggered as to make sure my logic would work in any scenario (is it disabled as soon as the setter is invoked? is this documented anywhere?)
Thanks in advance.
I have two tables. Transactions and Errors. There exists a One-To-Many relationship between Transactions and Errors. This is a bi-directional relationship and Errors is the owning side as #JoinColumn is specified in the Errors class. I want to understand what exactly does it mean to "OWN" the relationship. Say at the moment I have,
PROCESSED (column in TRANSACTIONS) set to N
ACTIVE (column in ERRORS) set to 1
Scenario 1:
Now lets suppose we execute the below code.
transactions.setProcessed("Y");
errors.setActive(0);
transactions.setErrors(errors);
entityManager.merge(transactions);
I understand that the PROCESSED field will get set to "Y" in TRANSACTIONS but will the ACTIVE field in ERRORS also get set to 0 or not given that transactions IS NOT the OWNING side of the relationship?
Scenario 2:
On the other hand, if we execute the below:
errors.setActive(0);
transactions.setProcessed("Y");
errors.setTransactions(transactions);
entityManager.merge(errors);
I understand that the ACTIVE filed in ERRORS will be set to 0 but will the PROCESSED field in TRANSACTIONS also be set to "Y" given that ERRORS IS the OWNING side of the relationship?
How do JPA cascade types tie into scenarios like this?
In a non-bidirectional relationship, you define a mapping. When you make a change to that mapping, it is obvious what will happen - a foreign key will get updated. Because there is only one mapping, there can be no conflict (many JPA providers will throw errors if they detect you have more then one writable mapping for a field).
With a bidirectional relationship, this control is less obvious. In your transaction-Error bidirectional relationship, assume it is a OneToOne bidirectional mapping, and Transaction1 is set to point to Error1 and vis versa. Lets say your application determines that Transaction1 should be pointed to Error2 instead, and changes the reference. If Error1's reference to Transaction1 isn't corrected to reflect this situation, there is a problem for JPA in determining what value to put into the foreign key. This is where ownership comes into play. The owning side is considered the writeable mapping, and changes to it control the foreign key fields. In a OneToMany, the owning side is usually the ManyToOne back reference because it is more natural since the foreign key is in the table holding the ManyToOne anyway. If you make a change to add an Error to a Transaction but do not change the Error to also reference that Transaction, your object model will be out of sync with what goes into the database - The foreign key in Error will not be changed, but the transaction object will show an error in its list until it is refreshed or reloaded from the database.
Cascading is something unrelated to ownership. It just means the operation (persist, merge, delete, refresh) applies to the entity referenced by the relationship. If using cascade.all an call em.refresh(transaction), the transaction and all referenced Errors will be refreshed from the database. Any relationships Error then has that have a cascade setting of ALL or REFRESH will also get refreshed and so on. JPA should detected that it has already refreshed the referenced Transaction instance if you place it on the back references, but why risk it. Generally, cascade options should only be placed on mappings where they are required to avoid unintended consequences. If you aren't sure if it is needed, leave it off until you are sure. Things like lazy fetching and other optimizations can cause all sorts of strange and hard to find bugs when someone goes and puts a cascade refresh everywhere.
In your example, you might put a cascade merge on the root entity that your application will be passing around. Any changes made to that graph then are easily picked up with a single merge call without having to call merge on each individual leaf. How your model is built and serialized though will affect the merge, so generally cascade options are put only on the root->Leaf relationships to avoid issues where the root -> leaf -> root' where root != root'. If you have cascade merge on both sides, the state of root' might overwrite your changes in root.
When we say that Errors is the owning side, that means foreign key of the relationship lies within the Errors table(which you are doing via #JoinColumn). Thus, the owning side of the relationship is the one in which reference column of the other entity will be present.
You can define the inverse side of relationship by specifying #OneToMany in the Transactions entity.
Now comes the second part of your question regarding the update of transactions and errors. In my view you can update List associated with a transaction by applying appropriate mode of cascade(persist,delete etc) which means you can specify in your Transaction entity #OneToMany(cascade=CASCADETYPE.MERGE) while specifying the inverse relationship. In this way, if whenever you will update a transaction row, corresponding error rows can also be updated.
However, I don't think it is a good practice to cascade in the other way ie if you update child entity the parent entity should also get updated as it may lead to many data inconsistencies
I'm fetching a long list of entities which refer to others which refer to... and, at the end, usually of all them refer to a single user as their owner. Not really surprising as what's queried are entities belonging to a single user. There are more parts duplicated in many rows; actually, just a small percentage are unique data. As the query seems to be slow, I though I could gain a bit by fetching things separately using
criteria.setFetchMode(path, FetchMode.SELECT);
This works in my above case, but when querying over many users (as admin), it gets terrible, as hibernate issues a separate query for every user, instead of something like
SELECT * FROM User WHERE id IN (?, ?, ..., ?)
or not fetching them at all (which can't get any worse than one query per entity). I wonder what am I missing?
So instead of fetching a lot of redundant data, I ran into the 1+N problem, where obviously 1+1 queries would do.
Is there a way to instruct Hibernate to use the right query?
Is there a way to prevent Hibernate from fetching the owners by specifying it in the criteria itself (rather than putting fetch=FetchType.LAZY on the field; the laziness should be query-specific)?
I don't think it matters, but my classes are like
class Child {
#ManyToOne Father father;
#ManyToOne Mother mother;
...
}
class Father {
#ManyToOne User owner;
...
}
class Mother {
#ManyToOne User owner;
...
}
and the query is like
createCriteria(Child.class)
.add(Restrictions.in("id", idList))
.add(Restrictions.eq("isDeleted", false))
.createAlias("Father", "f")
.add(Restrictions.eq("f.isDeleted", false))
.setFetchMode("f.owner", FetchMode.SELECT)
.createAlias("Mother", "m")
.add(Restrictions.eq("m.isDeleted", false))
.setFetchMode("m.owner", FetchMode.SELECT)
.list();
The important part is that owner does not get used and can be proxied. The javadoc for FetchMode.SELECT says
Fetch eagerly, using a separate select
so it basically promises 1+1 querying which I want rather than "using a separate select per entity".
Fetch profiles are meant to help you achieve what you want, but are very limited at the time being and you can override the default fetch plan/strategy only with the join-style fetch profiles (you can make a lazy association eager, but not vice versa). However, you could use this trick to invert that behaviour: Make the association lazy by default and enable the profile by default for all sessions/transactions. Then disable the profile in transactions in which you want lazy loading.
IMHO the solution above looks too cumbersome, and the approach I use in most use cases to avoid both loading of redundant data and N+1 selects problem is to make associations lazy and define batch size.
unless the property is declared with
#ManyToOne(fetch=FetchType.LAZY), you can't change anything
True, at least for the time being, until fetch profile capabilities are extended to provide the ability to change eager loading to lazy.
the default is FetchType.EAGER, which is stupid, as it can't be
overridden
True, and I agree that it is bad, but in Hibernate native API everything is lazy by default; it is JPA that mandates to-one associations to be eager unless explicitly specified otherwise.
using criteria.setFetchMode(path, FetchMode.SELECT) is pointless as
it's always a no-op (either it gets ignored because of the
non-overridable eagerness of the property or the property is lazy
already)!
With it you should be able to override other lazy fetch modes. See HHH-980 and this comment from one of the lead Hibernate contributors about the javadoc confusion.
fetching lazily leads by default to the 1+N problem
It has nothing to do with lazy loading specifically, it is the default for eager loading as well if you don't fetch the eagerly loaded association in the same query.
it can be controlled via a class-level #BatchSize annotation
You have to place it on class-level for it to take effect on to-one associations with that entity; this answer is helpful. For collection associations (to-many associations with that entity defined in other entities) you have the flexibility to define it separately for each association.
To summarize my frustration... Hibernate is full of surprises (bugs?) in this respect:
unless the property is declared with #ManyToOne(fetch=FetchType.LAZY), you can't change anything
the default is FetchType.EAGER, which is stupid, as it can't be overridden
using criteria.setFetchMode(path, FetchMode.SELECT) is pointless as it's always a no-op (either it gets ignored because of the non-overridable eagerness of the property or the property is lazy already)!
fetching lazily leads by default to the 1+N problem
it can be controlled via a class-level #BatchSize annotation
placing a #BatchSize annotation on a scalar field gets silently ignored
In order to get what I wanted (two SQL queries), I need just two things:
declare the property with #ManyToOne(fetch=FetchType.LAZY)
place #BatchSize(size=aLot) on the class of the property
That's simple, but a bit hard to find out (because of all the ignored things above). I haven't looked into fetch profiles yet.
I wrote a small project to demonstrate the behavior. The SQL generated from your criteria is as follows:
select
this_.id as id1_0_4_,
this_.father_id as father_i3_0_4_,
this_.isDeleted as isDelete2_0_4_,
this_.mother_id as mother_i4_0_4_,
f1_.id as id1_1_0_,
f1_.isDeleted as isDelete2_1_0_,
f1_.owner_id as owner_id3_1_0_,
user5_.id as id1_3_1_,
user5_.isDeleted as isDelete2_3_1_,
m2_.id as id1_2_2_,
m2_.isDeleted as isDelete2_2_2_,
m2_.owner_id as owner_id3_2_2_,
user7_.id as id1_3_3_,
user7_.isDeleted as isDelete2_3_3_
from
Child this_
inner join
Father f1_
on this_.father_id=f1_.id
left outer join
User user5_
on f1_.owner_id=user5_.id
inner join
Mother m2_
on this_.mother_id=m2_.id
left outer join
User user7_
on m2_.owner_id=user7_.id
where
this_.id in (
?, ?
)
and this_.isDeleted=?
and f1_.isDeleted=?
and m2_.isDeleted=?
Changing the FetchMode in the criteria API did not affect the query. The owner data is still queried.
The Ids are in the "in" clause and Hibernate did not issue separate queries for each Id.
As mentioned in other answers, if the entity relation is set to EAGER, ie JPA default, then you can't change the fetch mode in the Criteria API. The fetch mode needs to be changed to LAZY.
You can see it here
I would like to keep the Parent/Child relationship but when I parse through the Parent Object I don't want to fetch the child elements.
Proxies are generated so that Hibernate can intercept calls to uninitialized associations and try fetching them on-demand.
The LazyInitializationException is a code smell. You get it because you haven't properly initialized all the required entity associations prior to closing a Session. Switching to EAGER associations is also a bad idea because the fetching policy is a query responsibility.
Try to reduce the number of associations if you don't need them and use queries instead. You can build an application with just many-to-one associations (mirroring the FK relations) and instead of one-to-many associations you can have DAO methods.
As for this statement:
I would like to keep the Parent/Child relationship but when I parse
through the Parent Object I don't want to fetch the child elements.
If you keep the Parent/Child relationship you always need to load the association prior to using it. Trying to remove the proxies sounds like you are trying to hack a solution instead of properly design your application layers.
So Proxies are fine and they allow you to improve the application performance, because you don't always fetch all associations when you try to access a root entity.
To disable proxies you just have to annotate your entities with the Proxy annotation:
#Proxy(lazy=false)
Please explain difference between hibernate Eager fetching and cascade-type all.
In both configuration we can load child object associated with its parent, then what is difference between in.
Its simple :Consider two entities 1. Department and 2. Employee and they have one-to-many mappings.That is one department can have many employee
cascade=CascadeType.ALL and it essentially means that any change happened on DepartmentEntity must cascade to EmployeeEntity as well. If you save an Department , then all associated Employee will also be saved into database. If you delete an Department then all Employee associated with that Department also be deleted.Cascade-type all is combination of PERSIST, REMOVE ,MERGE and REFRESH cascade types. Example for Cascade type All
Fetch type Eager is essentially the opposite of Lazy.Lazy which is the default fetch type for all Hibernate annotation relationships. When you use the Lazy fetch type, Hibernate won’t load the relationships for that particular object instance. Eager will by default load ALL of the relationships related to a particular object loaded by Hibernate.Click here for an example.
Cascading and fetching are orthogonal concerns.
Cascading is about propagating an entity state transition from a Parent entity to a Child, simplifying the data access code by allowing the ORM tool to persist/merge/remove dependent associations on out behalf.
EAGER fetching is a mapping-time association loading decision, because it instructs Hibernate to always retrieve depended associations whenever a root entity gets loaded. Query-time fetching is preferred, because it gives you better flexibility and while the LAZY fetching mapping policy can be overridden by the FETCH directive. With EAGER fetching your are stuck, because you can't override it at query time and Hibernate will always fetch the association, even if on some use cases you don't need it.