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);
}
}
@M.Deinum Thank you for pointing me to the OpenEntityManagerInViewInterceptor
-issue. I now found the apparently infamous OSIV/OEMIV (open session in view/open EntityManager in view)-discussion (ie should the EntityManager
be still open in the controller-methods and thus prevent LazyLoading
-issues or should, on the contrary, these issues be exposed?
AND: What should the default be? This link https://github.com/spring-projects/spring-boot/issues/7107 has the discussion. Therein, a blog-entry arguing against OSIV/OEMIV is discussed: https://vladmihalcea.com/the-open-session-in-view-anti-pattern/ I was pointed to this by this stackoverflow-question: What is this spring.jpa.open-in-view=true property in Spring Boot? To sum up: The default is OSIV/OEMIV but it can be easily toggled using the application.properties property spring.jpa.open-in-view=false
The discussion comes to the conclusion that OSIV/OEMIV should remain the default for SpringBoot. It should however be documented better (its existence is hard to find; only in an appendix of a documentation)
I now have experimented with spring.jpa.open-in-view=false
and it does indeed work as advertised.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.