Refresh and fetch an entity after save (JPA/Spring Data/Hibernate)

ε祈祈猫儿з 提交于 2019-12-02 16:42:06

Instead of defining EntityManager in each of your resource, you can define it once by creating a Custom JpaRepository. Reference

Then use the refresh of your EntityManager in each of your repository directly.

Refer the below example:

CustomRepository Interface

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.NoRepositoryBean;

import java.io.Serializable;

@NoRepositoryBean
public interface CustomRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
  void refresh(T t);
}

CustomRepository Implementation

import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import java.io.Serializable;

public class CustomRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID>
    implements CustomRepository<T, ID> {

  private final EntityManager entityManager;

  public CustomRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
  }

  @Override
  @Transactional
  public void refresh(T t) {
    entityManager.refresh(t);
  }
}

Enable Custom JPARepository in Spring Boot Application Class

@SpringBootApplication
@EnableJpaRepositories (repositoryBaseClass = CustomRepositoryImpl.class)
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

Your Something Repository

public interface SomethingRepository extends CustomRepository<Something, Long> {

}

Use Refresh directly in SomethingResource (Assuming Something is an Entity)

@RestController
@RequestMapping("/api")
@Transactional
public class SomethingResource {

    private final SomethingRepository somethingRepository;

    public SomethingResource(SomethingRepository somethingRepository) {
        this.somethingRepository = somethingRepository;
    }

    @PostMapping("/somethings")
    public Something createSomething(@RequestBody Something something) throws URISyntaxException {
        Something result = somethingRepository.save(something);
        somethingRepository.refresh(result);
        return result;
    }
}

That's not enough:

Something result = somethingRepository.save(something);

You need to manually merge the incoming entity:

Something dbSomething = somethingRepository.findOne(
    Something.class, something.getId()
);
dbSomething.setName(something.getName());
dbSomething.setOwner(something.getOwner());

somethingRepository.save(dbSomething);

Since the property attribute is using the default FetchType.EAGER, the entity should have the property attribute initialized.

But, that's strange to call the Repository twice from the REST controller. You should have a Service layer that does all that in a @Transactional service method. That way, you don't need to resave the entity since it's already managed.

@Transactional
public Something mergeSomething(Something something) {
    Something dbSomething = somethingRepository.findOne(
        Something.class, something.getId()
    );
    dbSomething.setName(something.getName());
    dbSomething.setOwner(something.getOwner());

    return dbSomething;
}

Now, you need to carefully merge every property you sent. In your case, if you send null for property you should decide whether you should nullify the @ManyToOne reference or not. So, it depends on your current application business logic requirements.

Update

If you make sure you always send back the same entity you previously fetched, you could just use merge.

em.refresh(result);

But your property attribute is just an id, and not an actual child entity, so you have to resolve that yourself in the Service layer.

By the time you persist the entity it will be in managed state so if you just call something.getProperty(); it loads from the database and fills the property value of the something entity

public Something save(Something something) {
    em.persist(something);
    something.getProperty();
    return something;
}

so normally when you have many-to-one relationship that should be fetched automatically. If not calling the getters of the objects in entity will fill them too by firing a new DB Find request.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!