1

If I have a repository:

public interface ThingRepository extends JpaRepository<Thing, UUID> {
  @Query(/* query to get some Things */)
  Collection<Thing> getSomeThings(/* some arguments */);
}

Which is autowired in by Spring/Hibernate, what state will the Thing entity objects that are returned be in (persistent/detached/transient/etc)?

Context - if we make a change to a returned Thing (E.G. thing.setThingString("stuff!")), is there ever a situation where these changes will be persisted back to the database without explicitly calling thingRepository.save(thing);?

Sparky
  • 2,694
  • 3
  • 21
  • 31
  • No. It will not be persisted unless you trigger save explicitly – pvpkiran Aug 13 '18 at 11:14
  • Thanks! So does that mean that each call to a JpaRepository creates a new Session/EntityManager? – Sparky Aug 13 '18 at 11:15
  • answer here https://stackoverflow.com/questions/25709976/spring-boot-spring-data-how-are-hibernate-sessions-managed can help you. do checkout some comments for the accepted answer as well – pvpkiran Aug 13 '18 at 11:17
  • 1
    @pvpkiran Umm... Your first comment is misleading at best. **Of course** it will be persisted without triggering save explicitly, provided that the modification is made within transaction boundaries. The answer to OP's question: 'is there ever a situation...' is a most definite yes – crizzis Aug 13 '18 at 12:31
  • @crizzis Thanks! Just before I commit an answer here, the case is that: As long as its outside of a transaction (either by explicitly creating a transaction object or using the `@Transactional` annotation) then it will be in a detached state and therefore won't be persisted back? – Sparky Aug 13 '18 at 12:35
  • Yes, that's exactly the case – crizzis Aug 13 '18 at 12:36

2 Answers2

1

Simply have a look at the SimpleJpaRepository:

https://github.com/spring-projects/spring-data-jpa/blob/master/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

This is the basis for the concrete class that will be implemented for your ThingRepository.

And there you see:

@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> 

That means that every method is executed in read only transaction unless annotated with a write Transaction like it's done in the save* methods:

@Transactional
public <S extends T> S save(S entity) 

So in your case the entities are read only in your case.

Also read the Spring Data JPA doc:

https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#transactions

5.7. Transactionality

By default, CRUD methods on repository instances are transactional. For read operations, the transaction configuration readOnly flag is set to true. All others are configured with a plain @Transactional so that default transaction configuration applies. For details, see JavaDoc of SimpleJpaRepository. If you need to tweak transaction configuration for one of the methods declared in a repository, redeclare the method in your repository interface

Simon Martinelli
  • 34,053
  • 5
  • 48
  • 82
0

All is related to transaction boundaries.

If your method is called outside of any transaction, the underlying entityManager is already closed and the returned entities are detached.

If your method is called within an existing transaction, then entityManager is still open and the returned entities are in managed state. Note that in this case if the transaction is marked as readOnly the entityManager will never be flushed and no modification will be persisted even if entities are managed.

Note also that the readOnly flag is not overridden by inner logical transaction properties (in opposite to rollbackFor)

public class A {
    @Transactional(propagation = Propagation.REQUIRED)
    public void performA() {
        // b transaction scope is not read only, but TransactionManager will rollback for checkedException thrown from here
        b.performB(); 
    }
}

public class B {
    @Transactional(readOnly = true, propagation = Propagation.REQUIRED, rollbackFor = CheckedException.class)
    public void performB() {

    }
}
Gab
  • 7,869
  • 4
  • 37
  • 68