we have an entity that has a lot of ManyToOne, OneToOne, etc relations which themself have again some relations.
For example
#OneToMany(targetEntity = Season.class, cascade = {
CascadeType.ALL
})
#JoinColumn(name = "SEASON_ID")
public List<Season> getSeasons(){...}
(I can not change this).
Those are loaded lazily (by default I think) which is good and we don’t want to change this.
Now we have this one case, where we want to find the whole entity eagerly by it's id to return it.
I found a lot of suggestions to change the entity and discussions about wether eager or lazy loading is better, which do not help me at this point, since the entity is out of reach for us.
Is there a way to do this, without changing the entity and without having to call every possible getter to initialize the lazy entities (because those are too many)?
So for example, the answer to Question 24573877 does not work for me.
Basically I want to say "load the entity eagerly, but just this once".
Currently I'm just doing return em.find(MyEntity.class, contractId) (I could change this).
Thanks and regards
Urr4
look at : http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html
chapter : 14.3. Associations and joins
A "fetch" join allows associations or collections of values to be initialized along with their parent objects using a single select. This is particularly useful in the case of a collection. It effectively overrides the outer join and lazy declarations of the mapping file for associations and collections. See Section 19.1, “Fetching strategies” for more information.
You've to write your query instead of use em.find.
exemple this query :
from Cat as cat
inner join fetch cat.mate
left join fetch cat.kittens
will return cat with mate and kittens already loaded (eager), even if they have been initialized in lazy
Edit :
Or you can use fetch profil :
In order to mitigate these, Hibernate propose a fetch strategy that works not on the mapping level, but on the request level. Thus, you can still have lazy loading mappings but eager fetching in some cases.
look at : https://blog.frankel.ch/hibernate-hard-facts-part-6
I didn't really solve it the way i wanted to, but it works now.
It seems, there really is no way to eagerly load an entity, you know nothing about.
Our problem was, that the entity came from a 3rd-party Maven dependency and was unchangeable and hard to look into.
In the end we build an entityProcessor, that took the lazily loaded entity and called every getter and all the getters of the child entities recursively.
If we have control over an entity, i will use FetchProfiles next time, otherwise, I'd recommend implementing a clone() function.
Related
I have a crufty old Java Enterprise program that I'm trying to bring into the current decade thanks to security audits flagging half the code it imports as being a security risk. As some of you know, somewhere in the mid 2010's Hibernate changed its defaults from LAZY loading for everything, to EAGER loading for almost everything.
Problem is, this breaks this program bad. It was annotated assuming everything was lazy loading, so the only annotations are for things that it wanted to fetch EAGER, which, uhm, wasn't much. The end result is a huge increase in the number of joins, which has caused performance to plummet disastrously because most of the joined entities aren't used in a typical batch operation. For example, the User field is necessary in order to query by user. But the user for a report is already fetched at the top of the loop that processes the user's records, so adding a JOIN to the query to eager fetch the user for each and every record just makes the report slower.
Most of my relationships aren't annotated for lazy fetching, and there's a lot of them. I could manually go in and, laboriously, one by one, annotate them for lazy fetching. Or I could change Hibernate's defaults back to what they were back when this program was written. I'd obviously much prefer the latter, for obvious reasons -- I really don't want to be spending any more time updating this antique code base than I have to, since we're in the process of writing its replacement.
There is a default fetch plan based on the mapping you provide while defining entities.
By default, the JPA #ManyToOne and #OneToOne annotations are fetched EAGERly, while the #OneToMany and #ManyToMany relationships are considered LAZY.
This is the default strategy, and Hibernate doesn’t magically optimize your object retrieval, it only does what is instructed to do
As pointed out in this answer https://stackoverflow.com/a/57586036/19351325 unfortunately there's no way to change the default fetch type, it will always be:
OneToMany: LAZY
ManyToOne: EAGER
ManyToMany: LAZY
OneToOne: EAGER
But you can use simple regex to automatically add Lazy fetching. In Intellij Idea use these combinations: ctrl+shft+r or cmd+shft+r, and the following regex: #ManyToOne$ or #OneToOne$ , for replace-section use: #ManyToOne(fetch = FetchType.LAZY) and #OneToOne(fetch = FetchType.LAZY) accordingly.
If you already have in these annotations some properties added you can just modify your search regex. Anyway it will be faster then doing it all manually
I searched around but I only get people asking the opposite of my question. So let's say we have:
#Entity
class Book {
...
#ManyToOne(fetch = FetchType.LAZY)
private Author author;
}
Is there a (preferably global) property/way in JPA/Hibernate to prevent from ever lazily loading the author (or any entity)?
Don't get me wrong, I don't want to use EAGER fetch. I want to prevent juniors from ever accidentally calling book.getAuthor().getName() and making another DB call. A lot of people are looking to fix their LazyInitializationException, but I basically want to force such an exception to be thrown even if there is an active session (which when using #Transactional is quite an easy mistake to make). However I also still want Author to be fetched if you properly use "JOIN FETCH Author" in your JPQL query.
My particular use case is with Spring and GraphQL.
#Transactional quite easily hides when a session is open and avoids the LazyInitializationException.
And with GraphQL you can specify which fields to get so I don't want unnecessarily joins when such fields aren't requested (here we use a Field Resolver with a DataLoader).
Would a sufficient workaround be to instead use a projection (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) of the Book entity without the reference to the author? And by working with different projections guarantee that related entities are not unintentionally loaded?
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've been using JPA 2.0 for a while but, sad to admit, I haven't had enough time to learn it properly. It seems like I lack the basics of how to work with Entity Manager.
Moving one step at a time, I'd like to first ask you about maintaining relationships between mapped entities. Of course I know how to create mappings between entities, different types of available associations (OneToOne, etc.) and how databases work in general. I'm purely focused on maintaining it via Entity Manager, so please do not send me to any kind of general knowledge tutorial :-).
The questions are:
Am I right that as a programmer I'm responsible for maintaining (creating/updating/removing) relationships between instances of entities?
Do I have to always update (set to null, remove from collection, etc.) instances by hand?
Plain SQL can set entities to NULL on deleting, but it seems like JPA can't do such a simple thing. It also seems like a burden to do it manually. Is there a way to achieve that with JPA?
If I have OneToMany relationship and set to NULL the entity on the Many side of the relationship. Then I persist the changes in a Set by saving the entity on the One side. Do I then have to update the entities in the Many side and set association to NULL in each instance? Seems pure silliness for one-directional bindings!
Thanks in advance!
The main thing you need to investigate is the different options you have when mapping on entity. For example in the next piece of code the cascade all option will instruct jpa to delete the child list when the parent is deleted.
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "parent")
private Set<Child> events = new HashSet<Child>();
Yes. You maintain the object tree and modify it to look like what
you want.
Yes and no. If you want the entity to reference null, then yes.
For instance, if you are removing one Entity, then you should clean
up any references to it held by other entities that you are not
removing. A practical example: its good practice to let an Employee
know his/her Manager has been let go. If the Employee is going to
stay, it should either have its manager reference nulled out or set
to a different manager, before the current manager can be removed.
If the employee is going to be removed as well, then cascade remove
can cascade to all the Manager's subordinates, in which case you do
not need to clean up their references to the manager - as they are
going away too.
I don't quite understand what SQL is setting to null. Deleting
removes the row in the database, so there isn't anything to set to
null. Cleaning up a reference shouldn't be that difficult in the
object model, as JPA has a number of events to help such as
preremove preupdate etc. In the end though, the problem is with
your java objects. They are just java objects, so if you want
something done, your application will need to do it for the most
part. JPA handles building them and pushing them to the database,
not changing the state for you.
Yes and no. If you set up a bidirectional relationship, you must
maintain both sides as mentioned above. If you set the child's
parent reference to null, you should let the parent know it no
longer has a child, wouldn't you? Your parent will continue to
reference its child for as long as that Parent instance exists. So
even though the database is updated/controlled through the side that
owns a relationship, the object model will be out of synch with the
database until it is refreshed or somehow reloaded. JPA allows for
multiple levels of caching, so it all depends on your provider setup
how long that Parent instance will exist referencing a Child that no
longer exists in the database.
I have one to many association in my dto,
Parent DTO : Question
Child DTO : History
Question 1:
Means One question record has many history records, I am using JPA2.x with hibernate,
I bound the entities with lazy fetching method/mode. some places I want the lazy fetching...
and some places I don't want the lazy fetching not even egar. Means I don't want the data collection itself. I need only the parent class list, even if I call getHistories() method, it should not do lazy fetch in few places.... How to use entityManager to avoid lazy fetching.....even though the collection was set to lazy fetch mode.....?
Question 2:
I need to encrypt the question using mysql encode/decode functions. when inserting encrypt the value and save. when selecting decode the content.....
Solution can be in mapping or criteria query...?
Thanks In Advance....!
Question 1:
Your requirement makes no sense. If you don't want to access the history, don't call getHistories(). If you access the collection of History, either your Question is attached, and the collection will be loaded, or it's detached, and you'll get a lazy loading exception.
Question 2:
See http://docs.jboss.org/hibernate/core/3.6/javadocs/org/hibernate/annotations/ColumnTransformer.html. The example in the javadoc is precisely what you want to do.
For question 1, you need to do two things,
If you want your parent-child entities to be selected 'lazily', then annotate the bir-directional fields with fetch = lazy. This should work well in the hibernate session, as long as you dont load or 'touch' one of the lazy loaded fields.
If you want your parent-child entities to be selected 'eagerly', then anotate the fields in the bi-directional association as fetch = eager.
If you want 'some places I don't want the lazy fetching not even egar', then you have to decide at code time, whether to have a relationship between the parent-child entity, if you don't, then just dont map the bi-directional association. You CAN create more than one entity class to map different assoications, even entities with no association.