Spring data - entity not updated

5.2k Views Asked by At

I have an entity Customer and Spring data interface CustomerRepository shown below:

public interface CustomerRepository extends JpaRepository<Customer,Long> {
    Customer findCustomerByName(String name);
}

I save Customer object in the database and then update one field like this:

customerRepository.save(new Customer("John", "Smith"));
Customer john = customerRepository.findCustomerByName("John");
john.setSurname("Barton");
customerRepository.flush();
customerRepository.findAll().forEach(System.out::println);

I don't understand why it prints: Customer(id=1, name=John, surname=Smith).

As far as I know, Hibernate uses dirty checking mechanism to update entities in persistent state. So changed surname should be propagated to the database during end of transaction (but it does not - even if I separate this code into two @Transactional methods). Am I doing something wrong? Do I really need to save object manually after each change? Why surname field is not updated in the database?

1

There are 1 best solutions below

0
On BEST ANSWER
@RunWith(SpringRunner.class)
@SpringBootTest
public class CustomerRepoTest {
    @Autowired
    private CustomerRepository customerRepository;

    @Test
    //NOTE: No @Transactional
    public void testSaveFails() throws Exception {
        customerRepository.save(new Customer("John", "Smith"));
        Customer john = customerRepository.findCustomerByName("John");
        john.setSurname("Barton");
        customerRepository.flush();
        customerRepository.findAll().forEach(System.out::println);
    }

    @Test
    @Transactional
    public void testSaveWorks() throws Exception {
        customerRepository.save(new Customer("John", "Smith"));
        Customer john = customerRepository.findCustomerByName("John");
        john.setSurname("Barton");
        //customerRepository.flush(); Flush is not necessary
        customerRepository.findAll().forEach(System.out::println);
    }

}

To explain: Hibernate keeps a cache of objects it loaded during a transaction. When a find method is executed, the id of a newly loaded object is compared to the id of the object in this cache, if found, the cached version is used.

  • This is why the version with @Transactional works.
  • Also, it explains why flush is not necessary - it only forces the values to be written before the transaction ends.

If you miss the @Transactional (assuming auto-commit on the underlying transaction, which is most likely the case):

  • You save the entity in one transaction
  • Reload it with findCustomerByName, but it immediately gets detached
  • you modify the detached entity - no save
  • you reload the entries in another transaction, and you don't see your update