I am following the Spring-boot tutorial at http://www.baeldung.com/spring-boot-start which uses the spring-boot-starter-web
as well as the spring-boot-starter-data-jpa
without customization. My spring-boot-starter-parent
-version is 1.5.10.RELEASE
.
It is a simple REST-Api for a simple Book
-entity backed by a spring-data-jpa Repository
I am experimenting with different implementations of the delete-method of the @RestController
.
When calling the sequence
Book book = repo.findOne((Long)1L);
repo.delete(book);
in the main()
-method of the SpringBoot-application there will be 2 select
-statements generated by JPA/Hibernate as can be seen in the log (abbreviated for clarity):
select book0_.id (...) from book book0_ where book0_.id=?
select book0_.id (...) from book book0_ where book0_.id=?
delete from book where id=?
This is the expected behaviour: Outside of a transaction these 2 calls will trigger a transaction each. Furthermore, since EntityManager.remove()
only accepts attached/managed entities, the spring-data delete()
-implementation does an EntityManager-find()
before calling EntityManager.remove()
on the result of the find()
.
The same sequence in the @RestController
-annotated class however will only call select
once. Furthermore, a little experiment strongly suggests that this method runs inside a transaction and that the persistence-context of some EntityManager is apparently active here:
@DeleteMapping("{id}")
void delete(@PathVariable long id) {
Book book = repo.findOne((Long)id);
repo.delete(book);
System.out.println(book);
book.setTitle("XXX");
Book book2 = repo.findOne((Long)id);
System.out.println(book2);
repo.delete(id);
}
The log-output when called with a valid id
is (again, abbreviated for clarity):
select book0_.id as id1_0_0_(...) from book book0_ where book0_.id=?
Book [id=1, title=Spring Boot, author=Chris]
Book [id=1, title=XXX, author=Chris]
delete from book where id=?
It is my understanding (as well as the result of extensive seach on stackoverflow and the rest of the internet) that @Controller
-methods run outside of a transaction. Indeed, there is discussion whether a @Controller should or should not be @Transactional
. My @Controller
isn't.
So how is this observed behaviour possible? And is there some documentation explaining this?
For completeness' sake, here are class-definitions: The controller:
@RestController
@RequestMapping("/api/books/")
public class BookController {
@Autowired
BookRepository repo;
(...)
@DeleteMapping("{id}")
void delete(@PathVariable long id) {
(...) see above
}
The spring-data-jpa interface:
public interface BookRepository extends CrudRepository<Book, Long>{
List<Book> findByTitle(String title);
Optional<Book> findOne(long id);
}
The SpringBoot-application:
@SpringBootApplication(scanBasePackageClasses= {SimpleController.class})
@EntityScan(basePackageClasses={Book.class})
@EnableJpaRepositories(basePackageClasses= {BookRepository.class})
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(Application.class, args);
}
}