amique amique - 24 days ago 9
Java Question

Spring JpaRepository - Detach and Attach entity

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> {
}


I want to achieve following


  1. Load a User entity.

  2. Change the state of entity object e.g. user.setName("foo")

  3. Do an external system webservice call. Save the call result in DB

  4. 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

Answer

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.