I have an use case where it calls the following:
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}
Observe the @Transactional
has Propagation.REQUIRES_NEW and the repository uses getOne. When I run the app, I receive the following error message:
Exception in thread "main" org.hibernate.LazyInitializationException:
could not initialize proxy - no Session
...
But If I change the getOne(id)
by findOne(id)
all works fine.
BTW, just before the use case calls the getUserControlById method, it already has called the insertUserControl method
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}
Both methods are Propagation.REQUIRES_NEW because I am doing a simple audit control.
I use the getOne
method because it is defined in the JpaRepository interface and my Repository interface extends from there, I am working with JPA of course.
The JpaRepository interface extends from CrudRepository.
The findOne(id)
method is defined in CrudRepository
.
My questions are:
getOne(id)
method?getOne(id)
method?I am working with other repositories and all use the getOne(id)
method and all work fine, only when I use the Propagation.REQUIRES_NEW it fails.
According with the getOne API:
Returns a reference to the entity with the given identifier.
According with the findOne API:
Retrieves an entity by its id.
When I should use the findOne(id)
method?
What method is recommended to be used?
This question is related to
jpa
spring-data
spring-data-jpa
The basic difference is that getOne
is lazy loaded and findOne
is not.
Consider the following example:
public static String NON_EXISTING_ID = -1;
...
MyEntity getEnt = myEntityRepository.getOne(NON_EXISTING_ID);
MyEntity findEnt = myEntityRepository.findOne(NON_EXISTING_ID);
if(findEnt != null) {
findEnt.getText(); // findEnt is null - this code is not executed
}
if(getEnt != null) {
getEnt.getText(); // Throws exception - no data found, BUT getEnt is not null!!!
}
while spring.jpa.open-in-view was true, I didn't have any problem with getOne but after setting it to false , i got LazyInitializationException. Then problem was solved by replacing with findById.
Although there is another solution without replacing the getOne method, and that is put @Transactional at method which is calling repository.getOne(id). In this way transaction will exists and session will not be closed in your method and while using entity there would not be any LazyInitializationException.
1. Why does the getOne(id) method fail?
See this section in the docs. You overriding the already in place transaction might be causing the issue. However, without more info this one is difficult to answer.
2. When I should use the getOne(id) method?
Without digging into the internals of Spring Data JPA, the difference seems to be in the mechanism used to retrieve the entity.
If you look at the JavaDoc for getOne(ID)
under See Also:
See Also:
EntityManager.getReference(Class, Object)
it seems that this method just delegates to the JPA entity manager's implementation.
However, the docs for findOne(ID)
do not mention this.
The clue is also in the names of the repositories.
JpaRepository
is JPA specific and therefore can delegate calls to the entity manager if so needed.
CrudRepository
is agnostic of the persistence technology used. Look here. It's used as a marker interface for multiple persistence technologies like JPA, Neo4J etc.
So there's not really a 'difference' in the two methods for your use cases, it's just that findOne(ID)
is more generic than the more specialised getOne(ID)
. Which one you use is up to you and your project but I would personally stick to the findOne(ID)
as it makes your code less implementation specific and opens the doors to move to things like MongoDB etc. in the future without too much refactoring :)
I really find very difficult from the above answers. From debugging perspective i almost spent 8 hours to know the silly mistake.
I have testing spring+hibernate+dozer+Mysql project. To be clear.
I have User entity, Book Entity. You do the calculations of mapping.
Were the Multiple books tied to One user. But in UserServiceImpl i was trying to find it by getOne(userId);
public UserDTO getById(int userId) throws Exception {
final User user = userDao.getOne(userId);
if (user == null) {
throw new ServiceException("User not found", HttpStatus.NOT_FOUND);
}
userDto = mapEntityToDto.transformBO(user, UserDTO.class);
return userDto;
}
The Rest result is
{
"collection": {
"version": "1.0",
"data": {
"id": 1,
"name": "TEST_ME",
"bookList": null
},
"error": null,
"statusCode": 200
},
"booleanStatus": null
}
The above code did not fetch the books which is read by the user let say.
The bookList was always null because of getOne(ID). After changing to findOne(ID). The result is
{
"collection": {
"version": "1.0",
"data": {
"id": 0,
"name": "Annama",
"bookList": [
{
"id": 2,
"book_no": "The karma of searching",
}
]
},
"error": null,
"statusCode": 200
},
"booleanStatus": null
}
The getOne
methods returns only the reference from DB (lazy loading).
So basically you are outside the transaction (the Transactional
you have been declare in service class is not considered), and the error occur.
I had a similar problem understanding why JpaRespository.getOne(id) does not work and throw an error.
I went and change to JpaRespository.findById(id) which requires you to return an Optional.
This is probably my first comment on StackOverflow.
Source: Stackoverflow.com