Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

People also ask

What does save and flush do in JPA?

The saveAndFlush() Method Unlike save(), the saveAndFlush() method flushes the data immediately during the execution. This method belongs to the JpaRepository interface of Spring Data JPA.

How do you flush in JPA?

The way you can configure a query-specific flush mode depends on the FlushModeType you want to set. If you want to use the FlushModeTypes AUTO or COMMIT, which are defined by the JPA specification, you can call the setFlushMode method on your Query or TypedQuery interface. Query q = em.

What is difference between save and Saveflush?

On saveAndFlush , changes will be flushed to DB immediately in this command. With save , this is not necessarily true, and might stay just in memory, until flush or commit commands are issued.


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


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