getOne 및 findOne 메소드를 사용하는 경우 Spring Data JPA
다음을 호출하는 유스 케이스가 있습니다.
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl getUserControlById(Integer id){
return this.userControlRepository.getOne(id);
}
(가) 관찰 @Transactional
이 Propagation.REQUIRES_NEW 및 저장소 사용 getOne을 . 앱을 실행하면 다음과 같은 오류 메시지가 나타납니다.
Exception in thread "main" org.hibernate.LazyInitializationException:
could not initialize proxy - no Session
...
하지만 난을 변경하는 경우 getOne(id)
에 의해 findOne(id)
모든 작품 벌금.
BTW, 유스 케이스가 getUserControlById 메소드를 호출하기 직전에 이미 insertUserControl 메소드를 호출했습니다 .
@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public UserControl insertUserControl(UserControl userControl) {
return this.userControlRepository.save(userControl);
}
간단한 감사 제어를 수행하기 때문에 두 방법 모두 전파입니다 .REQUIRES_NEW
JpaRepository 인터페이스에 getOne
정의되어 있기 때문에 메소드를 사용 하고 내 Repository 인터페이스가 거기에서 확장 되므로 물론 JPA와 함께 작업하고 있습니다.
JpaRepository 인터페이스에서 확장 CrudRepository . 이 findOne(id)
방법은에 정의되어 CrudRepository
있습니다.
내 질문은 :
- 왜
getOne(id)
방법이 실패 합니까? - 언제
getOne(id)
방법을 사용해야 합니까?
나는 다른 리포지토리와 함께 일하고 있으며 모두 Propagation.REQUIRES_NEW를getOne(id)
사용할 때만 메소드를 사용하고 정상적으로 작동 합니다.
getOne API 에 따르면 :
주어진 식별자를 가진 엔터티에 대한 참조를 반환합니다.
findOne API 에 따르면 :
ID로 엔티티를 검색합니다.
3) 언제 findOne(id)
방법을 사용해야 합니까?
4) 어떤 방법을 사용하도록 권장됩니까?
미리 감사드립니다.
1. 왜 getOne (id) 메소드가 실패합니까?
문서 에서이 섹션 을 참조하십시오 . 이미 진행중인 트랜잭션을 재정의하면 문제가 발생할 수 있습니다. 그러나 더 많은 정보가 없으면 대답하기가 어렵습니다.
2. getOne (id) 메소드는 언제 사용해야합니까?
Spring Data JPA의 내부를 파헤 치지 않으면 차이점은 엔티티를 검색하는 데 사용되는 메커니즘에있는 것으로 보입니다.
당신은 보면 JavaDoc을 위한 getOne(ID)
아래 참고 항목 :
See Also:
EntityManager.getReference(Class, Object)
이 방법은 JPA 엔티티 관리자의 구현에 위임하는 것 같습니다.
그러나, 문서 에 대한이 findOne(ID)
이 문제를 언급하지 않습니다.
단서는 리포지토리의 이름에도 있습니다. JpaRepository
JPA에 따라 다르므로 필요한 경우 엔티티 관리자에게 호출을 위임 할 수 있습니다. CrudRepository
사용 된 지속성 기술을 무시합니다. 여기를보십시오 . JPA, Neo4J 등과 같은 다중 지속성 기술의 마커 인터페이스로 사용됩니다 .
따라서 사용 사례에 대한 두 가지 방법에는 실제로 '차이'가 없으며 findOne(ID)
더 전문화 된 것보다 더 일반적입니다 getOne(ID)
. 어느 것을 사용 하느냐는 귀하와 귀하의 프로젝트에 달려 있지만 개인적으로 findOne(ID)
코드를 구현에 덜 구체화하고 너무 리팩토링하지 않고 MongoDB와 같은 것으로 이동할 수있는 문을 열었습니다. :)
기본적인 차이점은 getOne
게으른로드되고 findOne
그렇지 않다는 것입니다.
다음 예제를 고려하십시오.
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!!!
}
TL; DR
T findOne(ID id)
(이전 API의 Optional<T> findById(ID id)
이름 ) / (새 API의 이름)은 엔티티 열망 로딩EntityManager.find()
을 수행하는 데 의존 합니다 .
T getOne(ID id)
에 의존 EntityManager.getReference()
하는 수행하는 개체 지연로드 . 따라서 엔티티를 효과적으로로드하려면 메소드를 호출해야합니다.
findOne()/findById()
보다 더 명확하고 사용하기 쉽습니다 getOne()
.
따라서 대부분의 경우을 선호 findOne()/findById()
합니다 getOne()
.
API 변경
최소한 2.0
버전이 Spring-Data-Jpa
수정되었습니다 findOne()
.
이전에는 CrudRepository
인터페이스 에서 다음 과 같이 정의 되었습니다.
T findOne(ID primaryKey);
Now, the single findOne()
method that you will find in CrudRepository
is which one defined in the QueryByExampleExecutor
interface as :
<S extends T> Optional<S> findOne(Example<S> example);
That is implemented finally by SimpleJpaRepository
, the default implementation of the CrudRepository
interface.
This method is a query by example search and you don't want to that as replacement.
In fact, the method with the same behavior is still there in the new API but the method name has changed.
It was renamed from findOne()
to findById()
in the CrudRepository
interface :
Optional<T> findById(ID id);
Now it returns an Optional
. Which is not so bad to prevent NullPointerException
.
So, the actual choice is now between Optional<T> findById(ID id)
and T getOne(ID id)
.
Two distinct methods that rely on two distinct JPA EntityManager retrieval methods
1) The Optional<T> findById(ID id)
javadoc states that it :
Retrieves an entity by its id.
As we look into the implementation, we can see that it relies on EntityManager.find()
to do the retrieval :
public Optional<T> findById(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
Class<T> domainType = getDomainClass();
if (metadata == null) {
return Optional.ofNullable(em.find(domainType, id));
}
LockModeType type = metadata.getLockModeType();
Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
}
And here em.find()
is an EntityManager
method declared as :
public <T> T find(Class<T> entityClass, Object primaryKey,
Map<String, Object> properties);
Its javadoc states :
Find by primary key, using the specified properties
So, retrieving a loaded entity seems expected.
2) While the T getOne(ID id)
javadoc states (emphasis is mine) :
Returns a reference to the entity with the given identifier.
In fact, the reference terminology is really board and JPA API doesn't specify any getOne()
method.
So the best thing to do to understand what the Spring wrapper does is looking into the implementation :
@Override
public T getOne(ID id) {
Assert.notNull(id, ID_MUST_NOT_BE_NULL);
return em.getReference(getDomainClass(), id);
}
Here em.getReference()
is an EntityManager
method declared as :
public <T> T getReference(Class<T> entityClass,
Object primaryKey);
And fortunately, the EntityManager
javadoc defined better its intention (emphasis is mine) :
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.
So, invoking getOne()
may return a lazily fetched entity.
Here, the lazy fetching doesn't refer to relationships of the entity but the entity itself.
It means that if we invoke getOne()
and then the Persistence context is closed, the entity may be never loaded and so the result is really unpredictable.
For example if the proxy object is serialized, you could get a null
reference as serialized result or if a method is invoked on the proxy object, an exception such as LazyInitializationException
is thrown.
So in this kind of situation, the throw of EntityNotFoundException
that is the main reason to use getOne()
to handle an instance that does not exist in the database as an error situation may be never performed while the entity is not existing.
In any case, to ensure its loading you have to manipulate the entity while the session is opened. You can do it by invoking any method on the entity.
Or a better alternative use findById(ID id)
instead of.
Why a so unclear API ?
To finish, two questions for Spring-Data-JPA developers:
why not having a clearer documentation for
getOne()
? Entity lazy loading is really not a detail.why do you need to introduce
getOne()
to wrapEM.getReference()
?
Why not simply stick to the wrapped method :getReference()
? This EM method is really very particular whilegetOne()
conveys a so simple processing.
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 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
}
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.
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.
참고URL : https://stackoverflow.com/questions/24482117/when-use-getone-and-findone-methods-spring-data-jpa
'Programing' 카테고리의 다른 글
jQuery : 목록 요소의 수를 세시겠습니까? (0) | 2020.06.26 |
---|---|
팬더에서 데이터 프레임의 처음 세 행을 삭제하십시오. (0) | 2020.06.26 |
mysql에서 문자열을 날짜로 변환하는 방법은 무엇입니까? (0) | 2020.06.26 |
Android 사용자 정의보기를 위해 세 생성자가 모두 필요합니까? (0) | 2020.06.26 |
KnockoutJS에 의해 잡히지 않는 jQuery UI 날짜 선택기 변경 이벤트 (0) | 2020.06.26 |