JPA: lazy load slick moment - java

As I understand, lazyLoadedObject in JPA might be not loaded even after using of it getter method. So in example below, after stroke 1, lazilyLoadededPerson might be still null, and only after stroke 2 the container will run the query to the db.
lazilyLoadedPerson = runQueryToLoadPerson();
lazilyLoadedPerson.getName();
My question is: Does the code below contain an error in stroke 2?
lazilyLoadedPerson = runQueryToLoadPerson();
if (lazilyLoadedPerson != null) { //if lazilyLoadedPerson == null suppose that such person doesn't exists in database
return lazilyLoadedPerson.getName();
}
Why I consider that it will be an error:
after the stroke 1 in the example above, the lazilyLoadedPerson will be null even the lazilyLoadedPerson exists in the database. We have the situation when the entity exists in the database but the getName() method will be never invoked.

Short answer
A lazily loaded object is never null.
Longer answer
The explanation is valid for Hibernate but other JPA-providers should be similar. See the official documentation for a detailed explanation of how lazy loading works or this short article for a short one. But the core facts are:
If for some reason or another Hibernate decides to lazily load an object, you don't get the object you wanted but a proxy to that object instead (i.e. not a null-reference). As soon as you call something on that proxy that can only be known by accessing the database, the proxy does exactly that. Provided you have an active session (and some other conditions are met) Hibernate executes the necessary query and the call returns.
Your Example
Let's take your example and extend it with a declaration
MyPerson lazilyLoadedPerson = runQueryToLoadPerson();
if (lazilyLoadedPerson != null) { //if lazilyLoadedPerson == null suppose that such person doesn't exists in database
return lazilyLoadedPerson.getName();
}
Assuming you did not define a proxy class for the class MyPerson, Hibernate has no other option but to actually load the object you wanted from the database. One of the reasons is exactly what you're doing: if it would return null, you could not check, whether the object even exists in the database.
Advice
Worrying about lazy loading can be quite confusing at the beginning. My advice is, not to do that too early. If you're just getting to know JPA, simply annotate all Collections with fetch = FetchType.EAGER and don't define any proxies and you'll never come across lazy loading.
If you got to know the whole concept a little better, you can read up on lazy loading and change your xml-file or annotation accordingly.

Related

Does the most optimal way to update an entity by ensuring that it's existing is the combination of getOne() and save() in Spring Data JPA?

I know there are several ways that we can follow to update an existing entity. I will mention two ways below.I need to clarify if my opinion regarding these two methods is correct. The ultimate goal is to find the most optimal way that we can follow to update an entity by ensuring the primary key is valid before the updation. So feel free to state any other mechanism.
Method 1:
First get and proxy object related to updating entity with the aid of getOne() method.
Then setting the necessary fields to be updated by setters to that proxy object.
Use save method to update the entity.
In here I am using getOne() method before the save to ensure that I am updating an existence entity.Otherwise according to my knowledge if the entity's primary key is not an auto generated field any new primary key inserted to the save() method will create a new entity in the database. So by following the getOne() method I can have an EntityNotFound exception in the end of the save() method call if the inserted id is not an existing one.
So basically following this way I can omit a database hit which will trigger generally to find() the given id is existing before saving the entity.According to my opinion this is the most optimal way that we can follow to update a given entity by ensuring the given id is always existing .The problem is I didn't see this method in any tutorial or website before.This was implemented by self.So I need to know if there any disadvantage that we can have by following this mechanism over the method two.
try {
CustomerCategory customerCategory = customerCategoryRepository.getOne(customerCategoryRequestDto.getCode());
customerCategory.setStatus(customerCategoryRequestDto.getStatus());
CustomerCategory savedCustomerCategory = customerCategoryRepository.save(customerCategory);
CustomerCategoryResponseDto customerCategoryResponseDto = modelMapper.map(savedCustomerCategory, CustomerCategoryResponseDto.class);
return customerCategoryResponseDto;
} catch (EntityNotFoundException e) {
throw new EntityNotFoundException(ExceptionMessage.MSG_ENTITY_NOT_FOUND);
}
Method 2:
First see if the given id is existing in the database.Otherwise inform the end user that the given id is not existing in the database.
Then perform the updation. In here I am using an query to update the entity .But this can be easily achieve by setting the necessary fields to the found entity from the previous find() method call and by calling the save() method.
But the problem here I see is we need one additional query to ensure that the given id is valid.So I think this will definitely decrease the database performance.But in most websites and tutorials most of the authors follow this mechanism.I don't see any practical use case or need that we need this mechanism over the first one.
Optional<CustomerCategory> searchedCustomerCategory = customerCategoryRepository.findById(customerCategoryRequestDto.getCode());
if (!searchedDbpCustomerCategory.isPresent()) {
throw new EntityNotFoundException(ExceptionMessage.MSG_ENTITY_NOT_FOUND);
}
customerCategoryRepository.updateCustomerCategory(CustomerCategoryStatus.DELETED.toString(), customerCategoryRequestDto.getCode());
Actually, there are cases and cases.
When you only need a reference (e.g. to maintain a relationship), then use JpaRepository#getOne(ID id).
When you need to update the entity, use CrudRepository#findById(ID id).
But why?
Be aware that doing this:
Entity ref = repo.getOne(1l);
ref.setAttribute("value");
has only one difference from doing this:
Entity entity = repo.findById(1l);
entity.setAttribute("value");
The difference is that the load operation is Lazy in getOne and Eager in findById.
But there's no performance gain when you need to update a column from the entity.
When you call a setter or a getter on the reference, the persistence provider DOES hit the database in order the bring the data. This is what Lazy load mean - only load WHEN YOU NEED. By calling a setter, you do need the data, then the persistence provider will perform a select on the database.
So there's no real gain on using getOne over findById when your goal is to update the entity.
Also, be aware that getOne is deprecated in favor of getById, which does just the same.

Using direct field access instead of getters in a copy constructor leads to null pointer exception

It must be Java 101 but I can't figure why I can't use direct field access and why I'm forced to use getters in a copy constructor.
I have a bunch of entities. They are organised like a tree. Linked entities are fetched eagerly.
I'm using Hibernate, Lombok and IntelliJ for the debugger.
When I pull one of the entity trees by the root I get a tree of objects. Let's call it "the original". For some reason related to business requirements I need to copy it (let's call this "the copy"). I do it using a copy constructor.
I first wrote a version of the copy constructor using direct field access.
this.someField= original.someField
It didn't work. When I checked the debugger I saw that original.someField (as well as the other fields) were always null.
Nevertheless, it works using the getters.
this.setSomeField(original.getSomeField())
In the debugger, I can see the fields are "set" in original.handler.target. (I've no idea what handler.target is).
Could someone explain to me why a direct field access doesn't work ?
(I'm asking about the technical reason not the philosophical one like "you should always use getters" etc).
I'd also be glad to know what is "handler.target".
Thanks in advance.
What you have encountered is not at all Java 101 problem. Hibernate has a feature called lazyloading, that allows the framework to defer the loading of an (potentially heavy) object to a later point in time, only when it is needed. This comes handy when you are for example loading an account object just to check an active flag, but absolutely do not need all the login history fetched with this account.
Now the "only when it is needed" part: getters.
Hibernate knows that you do need that lazily loadable object when you do call the getter on the parent object in the object graph. Until you do so, the lazily referred object remains null. Direct variable access bypasses the proxylogic that performs this "trick", and that is how you get to the unexpected null values. when the field is accessed via its getter, the proxied code kicks in, loading happens, and you get your object back.
The handler/target/etc are just the extra references you need to hvae due to the proxying. (your account will not have an direct accounthistory variable anymore, but instead an accounthistory_proxy, which in turn will have an accounthistory_real)
As per my understanding you are getting proxy object, once you are calling getter method you will get actual object.Please can you check once you are calling gettter method after that still the fields of object are null ? Try to use . operator after calling getter, I think values in fields should come.

Hibernate : Force lazy-loadding on eager field

One of our model object in our application has many fields configured to be eagerly fetched like so:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "field")
public Field getField() {
return this.field;
}
However I sometime do not need these information, which slow down my queries for nothing. I cannot change the behaviour and use FetchType.LAZY instead as I've no idea what will be the impact on the whole application (legacy...). Is there a way to simply tell hibernate to fetch nothing, except if it is specified in the query?
Last time I checked there was no proper solution provided by hibernate, so I ended up with this solution:
Configured the problematic references as LAZY.
All affected service methods (that used these models) got an overloaded version with boolean forceEager
by default all existing functions were refactored to call the new ones with forceEager=true
and here comes the trick: as a means of "forcing the eager fetching" I found nothing better than actually accessing the proxied (lazy-fetched) objects. In case for example a lazily referenced list doing list.size() will force Hibernate to load the full list, hence the service returns with fully fetched object.
In case of more than one layer in your objectstructure is affected, you need to traverse through the whole hierarchy and access every lazily loaded object from top to bottom.
This is a bit error-prone solution, so you need to handle it with care.
If its possible to switch to Criteria for this query, you could use FetchMode.SELECT for the field property
crit.setFetchMode("field", FetchMode.SELECT);

Hibernate, fetch, HQL and hashCode()

I have a HQL query something ala'
SELECT myclass
FROM
MyClass myclass JOIN FETCH
myclass.anotherset sub JOIN FETCH
sub.yetanotherset
...
So, class MyClass has a property "anotherset" , which is a set containing instance of another class, lets call it MyClassTwo. And, class MyClassTwo has a property yetanotherset which is a set of a third type of class (with no further associations on it).
In this scenario, I'm having trouble with the hashCode implementation. Basically, the hashCode implementation of MyClassTwo, uses the "yetanotherset" property, and on the exact line it accesses the yetanothertest property, it fails with a LazyInitializationException.
org.hibernate.LazyInitializationException: illegal access to loading collection
I'm guessing, this is because the data from "yetanotherset" hasn't been fetched yet, but how do I fix this? I don't particularly like the idea of dumbing down the hashCode to ignore the property.
Additional question, does HQL ignore fetch=FetchType.EAGER as defined in XML or annotations, it seems like it does. But I cant verify this anywhere.
Implementing hashCode() using a mutable field is a bad idea: it makes storing the entity in a HashSet and modifying the mutable property impossible.
Implementing it in terms of a collection of other entities is an even worse idea: it forces the loading of the collection to compute the hashCode.
Choose a unique, immutable property (or set of properties) in your entity, and implement hashCode based on that. On last resort, you have the option of using the ID, but if it's autogenerated, you must not put it in a Set before the ID is generated.
This is hibernate's most famous exception and it is exactly as you described it. The session has been disconnected, transaction closed, and you are attempting to access this collection. JOIN FETCH in your HQL should force EAGER loading to occur regardless of whether than annotation is present.
I suspect that your annotations are malformed, you have missing or out of date jars, or some other problem of that type.
Bump your Hibernate logging level up to generate the SQL hibernate.SQL=debug and investigate exactly what SQL is being executed up to where you see this exception. This should indicate to you whether your hibernate configuration is behaving the way you think its configured.
Post more of your code and the logs and someone might be able to help you spot the error.

In Hibernate, is there any difference between session.get() and session.load() besides how bad IDs are handled?

An application I'm working on does all its queries by identifier using session.load(). Given the chaos it causes when it can't find the identifier and throws an exception, I'm thinking of swapping it over to session.get(). But before I do that, it's such a fundamental part of the system that I want to make sure there's absolutely no other difference between the two methods. Is there any reason you can think of why the original developers would have chosen load() over get() aside from the handling of invalid IDs?
EDIT: As stated above, I'm fully aware that get returns false and load throws an exception. I'm asking if there's any OTHER way that they differ.
Isn't it so that Get never returns a proxy whereas Load does ?
http://ayende.com/Blog/archive/2009/04/30/nhibernate-ndash-the-difference-between-get-load-and-querying-by.aspx
I think that this is important:
Why is this useful? Well, if you know
that the value exist in the database,
and you don’t want to pay the extra
select to have that, but you want to
get that value so we can add that
reference to an object, you can use
Load to do so:
The code above will not result in a
select to the database, but when we
commit the transaction, we will set
the CustomerID column to 1. This is
how NHibernate maintain the OO facade
when giving you the same optimization
benefits of working directly with the
low level API.
From the NH 2.0 ref documentation:
Note that Load() will throw an
unrecoverable exception if there is no
matching database row. If the class is
mapped with a proxy, Load() returns an
object that is an uninitialized proxy
and does not actually hit the database
until you invoke a method of the
object. This behaviour is very useful
if you wish to create an association
to an object without actually loading
it from the database.
If you are not certain that a matching
row exists, you should use the Get()
method, which hits the database
immediately and returns null if there
is no matching row.
As usual, the best reference for this is the documentation:
Session.get():
Return the persistent instance of the given entity class with the given identifier, or null if there is no such persistent instance. (If the instance, or a proxy for the instance, is already associated with the session, return that instance or proxy.)
Session.load():
Return the persistent instance of the given entity class with the given identifier, assuming that the instance exists.
You should not use this method to determine if an instance exists (use get() instead). Use this only to retrieve an instance that you assume exists, where non-existence would be an actual error.
So, the difference is the way that non-existing instances are treated.
A good approach is shown as follows
If you need to call a getter method, then use get method. get method hits the database.
public class AccountServiceImpl implements AccountService {
private SessionFactory sessionFactory;
public BigDecimal getBalance(Integer acountId) {
// You need to know your balance
// So you need to use get method to access the database
Account account = (Account) sessionFactory.getCurrentSession().get(Account.class, accountId);
return account.getBalance();
}
}
If you need to call both getter and setter method, use get method.
In response to ChssPly's comment:
JPA with Hibernate book says about load method
The load() method always tries to return a proxy, and only returns an initialized object instance if it’s already managed by the current persistence context.
And
It hits the database as soon as you try to access the returned placeholder and force its initialization
Thus, he is right when you set up a single property.
But There is the following scenario shown in JPA with Hibernate book
It’s common to obtain a persistent instance to assign it as a reference to another instance. For example, imagine that you need the item only for a single purpose: to set an association with a Comment: aComment.setForAuction(item).
If this is all you plan to do with the item, a proxy will do fine; there is no need to
hit the database. In other words, when the Comment is saved, you need the foreign key value of an item inserted into the COMMENT table.
The proxy of an Item provides just that: an identifier value wrapped in a placeholder that looks like the real thing.
regards,

Categories