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

JavaHibernateJpaSpring Data-JpaPersistence

Java Problem Overview


I've these two simple entities Something and Property. The Something entity has a many-to-one relationship to Property, so when I create a new Something row, I assign an existing Property.

Something:

@Entity
@Table(name = "something")
public class Something implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @Column(name = "owner")
    private String owner;

    @ManyToOne
    private Property property;

    // getters and setters

    @Override
    public String toString() {
        return "Something{" +
            "id=" + getId() +
            ", name='" + getName() + "'" +
            ", owner='" + getOwner() + "'" +
            ", property=" + getProperty() +
            "}";
    }

Property:

@Entity
@Table(name = "property")
public class Property implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "shape")
    private String shape;

    @Column(name = "color")
    private String color;

    @Column(name = "dimension")
    private Integer dimension;

    // getters and setters

    @Override
    public String toString() {
        return "Property{" +
            "id=" + getId() +
            ", shape='" + getShape() + "'" +
            ", color='" + getColor() + "'" +
            ", dimension='" + getDimension() + "'" +
            "}";
    }
}

This is the SomethingRepository (Spring):

@SuppressWarnings("unused")
@Repository
public interface SomethingRepository extends JpaRepository<Something,Long> {
    
}

Through a REST controller and a JSON, I want to create a new Something:

@RestController
@RequestMapping("/api")
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);
        return result;
    }
}

This is the JSON in input (the property id 1 is an existing row in the database):

{
  "name": "MyName",
  "owner": "MySelf",
  "property": {
    "id": 1
  }

}

The problem is: after the method .save(something), the variable result contains the persisted entity, but without the fields of field property, validated (they are null):

Output JSON:

{
  "id": 1,
  "name": "MyName",
  "owner": "MySelf",
  "property": {
    "id": 1,
    "shape": null,
    "color": null,
    "dimension": null
  }
}

I expect that they are validated/returned after the save operation.

To workaround this, I have to inject/declare the EntityManager in the REST controller, and call the method EntityManager.refresh(something) (or I have to call a .findOne(something.getId()) method to have the complete persisted entity):

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

    private final SomethingRepository somethingRepository;
    
    private final EntityManager em;

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

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

With this workaround, I've the expected saved entith (with a correct JSON):

{
  "id": 4,
  "name": "MyName",
  "owner": "MySelf",
  "property": {
    "id": 1,
    "shape": "Rectangle",
    "color": "Red",
    "dimension": 50
  }
}

Is there an automatic method/annotation, with JPA or Spring or Hibernate, in order to have the "complete" persisted entity?

I would like to avoid to declare the EntityManager in every REST or Service class, or I want avoid to call the .findOne(Long) method everytime I want the new refreshed persisted entity.

Java Solutions


Solution 1 - Java

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

Solution 2 - Java

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.merge(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.

Solution 3 - Java

In Spring Boot JpaRepository:

If our modifying query changes entities contained in the persistence context, then this context becomes outdated.

In order to fetch the entities from the database with latest record.

Use @Modifying(clearAutomatically = true)

@Modifying annotation has clearAutomatically attribute which defines whether it should clear the underlying persistence context after executing the modifying query.

Example:

@Modifying(clearAutomatically = true)
@Query("UPDATE NetworkEntity n SET n.network_status = :network_status WHERE n.network_id = :network_id")
    	int expireNetwork(@Param("network_id") Integer network_id,  @Param("network_status") String network_status);

Solution 4 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndrea BevilacquaView Question on Stackoverflow
Solution 1 - JavaSahil ChhabraView Answer on Stackoverflow
Solution 2 - JavaVlad MihalceaView Answer on Stackoverflow
Solution 3 - JavaNarasimha AView Answer on Stackoverflow
Solution 4 - JavaHarish BarmaView Answer on Stackoverflow