0

Imagine the following situation: We have two database tables, Tenant and House. Tenant references House with a @ManyToOne mapping.

Tenant tenant = tenantRepository.findById(id).orElseThrow();
House house = tenant.getHouse();

house.setPrice(340_000);
house = houseRepository.save(house); // A new instance is returned by the CrudRepository::save() method

// TODO Is this necessary for further use?
tenant.setHouse(house);

// Further use...
tenant.setAge(23);
tenant = tenantRepository.save(tenant); // Otherwise it is saved with the old reference where house's ID can be null?
...

Is it necessary to update the Tenant with the new reference of House?

EDIT: For clarification, you may assume the entities were loaded (therefore, in managed state) immediately before the above code. And because this "transaction" is a part of a Spring @RequestMapping function, the transaction will be implicitly committed in the end of it.

EDIT 2: The question is not whether I should or not save the house at all in the beginning to avoid this situation. It is about understanding better how the objects are managed.

--- But you may tell me also, should I just update everything first, and save in the end, as a common practice?

Snackoverflow
  • 5,332
  • 7
  • 39
  • 69
  • `A new instance is returned ...` is only partially correct. It should be `The saved instance is returned ...`. Then, `the saved instance` becomes dependent upon the JPA provider. Hibernate for example returns the same object that is passed to the `save` method (up to the current versions), whereas other providers may return a different object altogether. This is why the JPA spec recommends using the instance returned from the `save` call. If you want to be provider-agnostic or are tied to anything other than Hibernate, `tenant.setHouse(house)` should be called after `house` has been saved. – manish Feb 14 '19 at 03:14
  • The JPA spec actually refers to the `persist` and `merge` methods. There is no `save` in the JPA API –  Feb 14 '19 at 07:11
  • Sorry but why not post the complete code required to make sense of your question? Without knowing if these care existing or new entities no-one can give a sensible answer – Alan Hay Feb 14 '19 at 12:02
  • @BillyFrost I am talking about Spring Data JPA and CrudRepository, which are another layer on top of JPA. – Snackoverflow Feb 14 '19 at 12:09
  • evidently ... which aren't mentioned in the JPA spec. –  Feb 14 '19 at 13:01
  • @BillyFrost Take a look at https://stackoverflow.com/q/14014086/2237467 – Snackoverflow Feb 14 '19 at 15:44
  • why would I, they're nothing to do with the JPA spec –  Feb 14 '19 at 17:47

1 Answers1

1

The critical question is are house and tenant already managed entities?

If yes (because they got loaded in the same transaction that is still running) all the House instances involved are the same and you don't need to set the house in tenant. But in that case, you don't even need to call save anyway.

If they are detached instances, yes you need to call tenant.setHouse(house);. Without it, you will get either an exception or overwrite the changes to house, depending on your cascade setting on the relation.

The preferred way to do all this is:

Within a single transaction:

  • Load the entities
  • manipulate them as desired
  • commit the transaction

JPA will track the changes to the entities and flush them to the database before actually committing the database transaction.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • Yes, they are managed entities in the beginning, I have also added the clarification to the question. The thing about your solution that I am confused about is: *Why* would it not be necessary to call `save()` for managed entities after modifying them? Maybe I am wrong, but when reading Spring and Spring Boot tutorials, I remember that calling `save()` was always necessary after modifications, no? – Snackoverflow Feb 14 '19 at 12:22
  • Another comment about your proposed method. Would you mean something like this: 1. Load the entities -> 2. Manipulate them as desired -> 3. Call save on them -> 4. Either commit explicitly or let Spring do it implicitly? – Snackoverflow Feb 14 '19 at 12:26
  • 1
    There is no need to `save` managed instances. JPA does dirty checking, i.e. it keeps track of which entities you change and flushes them automatically. See https://stackoverflow.com/a/1785126/66686 – Jens Schauder Feb 14 '19 at 12:42
  • Okay, I understand the Entity lifecycle, but it is closely related to the EntityManager lifecycle - the entity is managed until its manager is closed, after which it becomes detached. But it's difficult to find any documentation on how Spring Boot manages the lifecycle of an EntityManager. How do I know and can be sure that the entity really *is* managed? – Snackoverflow Feb 14 '19 at 16:02
  • 1
    The `EntityManager` is basically managed by the transaction. See https://stackoverflow.com/a/25710391/66686 – Jens Schauder Feb 14 '19 at 17:05
  • So basically what you are saying is... If all of my data manipulation happens only in the `@RequestMapping` annotated methods inside the `@RestController` annotated class... I will only need to use `save()` if I create a new instance, and never when I modify an existing instance that was queried for during the processing of the same request? – Snackoverflow Feb 15 '19 at 12:27
  • What I am *really* asking is, are there any ambiguities? Because I never remember any tutorials or documentation mentioning that using `save()` is unnecessary. I also believe all of the examples show saving after modifications as well. – Snackoverflow Feb 15 '19 at 12:33
  • I guess I had the wrong idea on how Spring works. I thought the handling of a single request is automatically transactional. But each request is a separate transaction, unless the containing method is annotated by `@Transactional`. Therefore, unless I explicitly create a transaction, I must use `save()` also after modifications. https://stackoverflow.com/q/46708063/2237467 – Snackoverflow Feb 15 '19 at 12:42