Issue
I am using spring boot and hibernate over jpa. I am using JpaRepository interface to implement my repositories. As with following UserRepository
public interface UserRepository extends JpaRepository<User, Long> {
}
- Load a User entity.
- Change the state of entity object e.g. user.setName("foo")
- Do an external system webservice call. Save the call result in DB
- Only on successful response of this webservice call, save the new state of user in repository.
All above steps are not happening in one transaction i.e. the external service call is out of transaction.
When I save my webservice result in DB via its repository, my changes in User entity are also saved. As per my understanding this is due to the flushing of underlaying persistence context at step # 3. After some google, I think I can achieve my purpose, if I can detach my user entity at step one and reattach it at step 4. Please confirm if my understanding is correct and how I can achieve this? There is not method in JpaRepository interface to detach an entity.
Following is the code to illustrate
public void updateUser(int id, String name, int changeReqId){
User mUser = userRepository.findOne(id); //1
mUser.setName(name); //2
ChangeRequest cr = changeRequestRepository.findOne(changeReqId);
ChangeResponse rs = userWebService.updateDetails(mUser); //3
if(rs.isAccepted()){
userRepository.saveAndFlush(mUser); //4
}
cr.setResponseCode(rs.getCode());
changeRequestRepository.saveAndFlush(cr); //this call also saves the changes at step 2
}
Thanks
Solution
If you are using JPA 2.0, you can use EntityManager#detach() to detach a single entity from persistence context. Also, Hibernate has a Session#evict() which serves the same purpose.
Since JpaRepository
doesn't provide this functionality itself, you can add a custom implementation to it, something like this
public interface UserRepositoryCustom {
...
void detachUser(User u);
...
}
public interface UserRepository extends JpaRepository<User, Long>, UserRepositoryCustom {
...
}
@Repository
public class UserRepositoryCustomImpl implements UserRepositoryCustom {
...
@PersistenceContext
private EntityManager entityManager;
@Override
public void detachUser(User u) {
entityManager.detach(u);
}
...
}
I haven't tried this code, but you should be able to make it work. You might even try to get a hold on EntityManager
in your service class (where updateUser()
is) with @PersistenceContext
, and avoid the hustle of adding custom implementation to repository.
Answered By - Predrag Maric
Answer Checked By - Willingham (JavaFixing Volunteer)