having this classes:
User.java:
@Entity
@Setter
@Getter
@NoArgsConstructor
public class User {
@Id
private int id;
private String username;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Address address;
public User(String username) {
this.username = username;
}
}
Address.java:
@Entity
@Data
public class Address {
@Id
private int id;
private String country;
@OneToOne
private User user;
}
UserRepository.java:
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}
DemoApplcation.java:
@Bean
public CommandLineRunner loadData(UserRepository userRepo){
return new CommandLineRunner() {
@Override
public void run(String... args) throws Exception {
User u = new User("motherfucker");
Address a = new Address();
a.setCountry("CZ");
u.setAddress(a);
a.setUser(u);
userRepo.save(u);
//User newUser = userRepo.getById(0);
User newUser = userRepo.findById(0).orElse(null);
System.out.println(newUser.getUsername());
}
};
}
Now the findById(int: id)
works without problem (defined in CrudRepository
from which extends JpaRepository
). However the getById(int :id)
(defined in JpaRepository
) gives LazyInitializationException
even with fetch = Fetch.EAGER
attribute specified in mapping. In documentation it says
Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is implemented this is very likely to always return an instance and throw an EntityNotFoundException on first access. Some of them will reject invalid identifiers immediately.
EntityNotFoundException
but LazyInitializationException
from this description it seems for me this method be useless if it always throws exception. Why does this method exists?
What is the right way (method) for fetching data in hibernate?
Both findById() and getOne() methods are used to retrieve an object from underlying datastore. But the underlying mechanism for retrieving records is different for both these methods, infact getOne() is lazy operation which does not even hit the database.
The findById() method is used to retrieves an entity by its id and it is available in CrudRepository interface. The CrudRepository extends Repository interface. In Spring Data JPA Repository is top-level interface in the hierarchy. Here we are going to see the findById() method of CrudRepository.
PagingAndSortingRepository provides methods to do pagination and sort records. JpaRepository provides JPA related methods such as flushing the persistence context and delete records in a batch.
JPA uses EntityManager interface to create/read/delete operation and maintains the persistence context. Hibernate uses Session interface to create/read/delete operation and maintains the persistence context. JPA uses JPQL (Java Persistence Query Language) as Object Oriented Query language for database operations.
LazyInitializationException
exactly because you set fetch = Fetch.EAGER
. getById()
returns a lazily fetched entity and thus the exception.getById()
. But to be honest I don't understand that either.It is also important to highlight another detail: findById()
method uses EntityManager
find(Class<T> entityClass, Object primaryKey, LockModeType lockMode, Map<String,Object> properties))
method internally and getById()
uses EntityManager
getReference(Class<T> entityClass, Object primaryKey)
method. As a consequence, findById()
returns the actual object and getById
returns a reference of the entity.
Reference documentation:
Usually we can use either findById or getById. There's no issue in using either of it.
But if we want to understand more specific differences the those differences are as follows:
- getById: This is used only if we are sure about getting an entity we requested from the database. If we don't get any entity, it gives an exception. This is similar to a child who get's whatever it wants but starts screaming if it doesn't get what he requires.
- findById: This is used if we are not sure that whether the requested entity in the database is present or not. So even if the entity is not present in the database it returns null and doesn't throw any exception.
getById -> returns a reference proxy to for the actual Entity. Where only the id set (since you already passed it). This object's getters and setters may be called in the same @Transaction. But, once you get out of the transaction, then the proxy will return LazyInitializationException.
if you look at the getReference java doc. I believe this makes all clear. So, hibernate does not hit to the database if you call getById.
Get an instance, whose state may be lazily fetched. If the requested instance does not exist in the database, the EntityNotFoundException is thrown when the instance state is first accessed. (The persistence provider runtime is permitted to throw the EntityNotFoundException when getReference is called.) The application should not expect that the instance state will be available upon detachment, unless it was accessed by the application while the entity manager was open.
on the other hand, findById directly hit to DB and performs the select query.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With