39

I present the question with an example.

Assert that we have a Repository such as the below:

public interface ExampleObjectRepository extends CrudRepository<ExampleObject, Long> {

}

By extending the JpaRepository interface, the ExampleObject repository inherits the following method:

T findOne(ID id);

Now, I have observed that, if I receive a reference to an ExampleObject after a call to this method, any manipulations that I make to this method are automatically saved to the database, for example:

ExampleObject pointInCase = exampleObjectRepository.findOne(1L);
pointInCase.setName("Something else");

Reading around the subject, I understand this this signfies that ExampleObject instance is not detached.

This goes against my expectations. I would have expected that I would need to use the save method inherited from CrudRepository in order to save the changes:

T save(T entity);

Would anyone be kind enough to confirm that objects returned from a Spring Data JPA Repository remain attached as standard, and explain how to use the API to mark a method in the repository such that it only returns detached references?

I imagine that changing the entity's state may also change its definition when it is used with said save(T entity) method, so I would also appreciate an understanding of how identity for updates are handled.

8bitjunkie
  • 12,793
  • 9
  • 57
  • 70
  • 1
    Maybe this was a behavior in old versions of Spring Data JPA, in recent versions all read operations in the default implementation of CrudRepository are annotated with @Transactional(readOnly = true) which makes them detached, you need to do repository.save for the changes to be persisted – raspacorp Aug 20 '20 at 17:24

3 Answers3

31

That's a fundamental principle of JPA. You work with attached (managed) entities, and every modification made on these managed entities is automatically made persistent.

If you don't want your changes to be persistent, then don't make changes, or rollback the transaction.

Working on detached entities would be a nightmare, because it would prevent lazy-loading all the associations. You can always call EntityManager.detach() on your entities, but I really wouldn't do that. Just try to understand how it works and deal with it. There are much more benefits than disadvantages. One of them being that you don't even have to think about saving all the changes that a complex business logic might do, since it's all done for you by JPA, transparently.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thanks for your comments - they are certainly consistent with other opinions around the office! However in the case that several POJO manipulations are made as part of a transaction, would this behaviour break the transactional nature of the method? – 8bitjunkie Nov 27 '12 at 17:10
  • 2
    Why would it? You start a transaction, make any number of modifications you want to any number and kinds of entities you want, and then commit the transaction. Either all the changes are committed, or all the changes are rollbacked. The transactional aspect of a method also applies to every internal method it calls: the transaction context is propagated. – JB Nizet Nov 27 '12 at 17:12
  • so... the calling `save` method is unnecessary unless you detach? Or I should call some another method just to make sure JPA closes the transaction instead of rollback? – kiedysktos Feb 20 '17 at 10:30
  • @kiedysktos Yes, saving a managed entity is useless. How to commit the transaction depends on your environment (Java EE? Java SE? Spring-managed transactions?). But whether you call save() ornot, the transaction must be committed to have the changes really applied to the database. – JB Nizet Feb 20 '17 at 11:52
7

You can make your Repository return detached entities if your Transaction is scoped to the repository. I.e. your transaction starts with entrance into the repository and gets closed when you code exits the repository.

If you work like this you have to take care that all necessary data gets loaded in one go, because otherwise you will get lazy initialization exceptions. But in many circumstances I consider this desirable because it gives you some control back when and what data is loaded, that otherwise slips easily through your fingers when using JPA.

For modifications I use a different Transaction scope which wraps the complete process of loading, changing (and implicitly persisting).

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
3

I also faced the same problem.
But for me, the issue came on custom repository implementation then i added @Transactional(readOnly = true).

Sandy
  • 309
  • 4
  • 13
  • This is useful when you want to use @Transactional to lazy load entities but don't want any persistence – nclord Mar 21 '19 at 09:58