java - Spring @Controller rsp. @RestController 与 spring-data-jpa 存储库进行事务处理?看起来像这样

标签 java spring spring-mvc spring-boot spring-data-jpa

我正在关注 http://www.baeldung.com/spring-boot-start 上的 Spring-boot 教程它使用 spring-boot-starter-web 以及 spring-boot-starter-data-jpa 而无需自定义。我的 spring-boot-starter-parent 版本是 1.5.10.RELEASE。

它是一个简单的 REST-Api,用于由 spring-data-jpa Repository 支持的简单 Book 实体 我正在尝试 @RestController 的删除方法的不同实现。 调用序列时

Book book = repo.findOne((Long)1L);
repo.delete(book);

在 SpringBoot 应用程序的 main() 方法中,将有 2 个由 JPA/Hibernate 生成的 select 语句,如日志中所示(缩写为为了清楚起见):

select book0_.id (...) from book book0_ where book0_.id=?
select book0_.id (...) from book book0_ where book0_.id=?
delete from book where id=?

这是预期的行为:在事务之外,这两个调用将分别触发一个事务。此外,由于 EntityManager.remove() 仅接受附加/托管实体,因此 spring-data delete() 实现会执行 EntityManager-find() 在对 find() 的结果调用 EntityManager.remove() 之前。 然而,@RestController 注释类中的相同序列只会调用 select 一次。此外,一个小实验强烈表明该方法在事务内运行,并且某些 EntityManager 的持久上下文显然在这里处于 Activity 状态:

@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);
    }

使用有效的 id 调用时的日志输出为(再次为了清晰起见进行缩写):

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=?

据我了解(以及在 stackoverflow 和互联网其他部分进行广泛搜索的结果),@Controller 方法在事务之外运行。事实上,存在关于 @Controller 是否应该是 @Transactional 的讨论。我的 @Controller 不是。

那么这种观察到的行为是如何可能的呢? 有没有一些文档对此进行解释?

为了完整起见,以下是类定义: Controller :

@RestController
@RequestMapping("/api/books/")
public class BookController {
    @Autowired
    BookRepository repo;

    (...)

 @DeleteMapping("{id}")
        void delete(@PathVariable long id) {
(...) see above
    }

spring-data-jpa 接口(interface):

public interface BookRepository extends CrudRepository<Book, Long>{
    List<Book> findByTitle(String title);
    Optional<Book> findOne(long id);
}

SpringBoot 应用程序:

@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 感谢您向我指出 OpenEntityManagerInViewInterceptor -问题。我现在发现了明显臭名昭著的 OSIV/OEMIV(在 View 中打开 session /在 View 中打开 EntityManager)讨论(即 EntityManager 是否应该在 Controller 方法中保持打开状态,从而防止 LazyLoading -问题或应该在相反,这些问题是否会暴露出来?

并且:默认值应该是什么?此链接https://github.com/spring-projects/spring-boot/issues/7107有讨论。其中讨论了反对 OSIV/OEMIV 的博客文章: https://vladmihalcea.com/the-open-session-in-view-anti-pattern/ 这个 stackoverflow 问题指出了这一点: What is this spring.jpa.open-in-view=true property in Spring Boot? 总结一下:默认为 OSIV/OEMIV,但可以使用 application.properties 属性 spring.jpa.open-in-view=false 轻松切换。 讨论得出的结论是 OSIV/OEMIV 应该保留为 SpringBoot 的默认值。然而,应该更好地记录它(它的存在很难找到;仅在文档的附录中)

我现在已经尝试过 spring.jpa.open-in-view=false它确实像宣传的那样有效。

关于java - Spring @Controller rsp. @RestController 与 spring-data-jpa 存储库进行事务处理?看起来像这样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48728551/

相关文章:

java - 具有不总是使用的通用字段的 Bean

java - java 中命名空间 xpath 的输出

java - Spring 3 和 Spring 4 的 Swagger 版本

Hibernate内存数据+数据库

java - Spring MVC 4 : Interceptor not being able to set Response header

java - Spring框架连接多个数据库xml JAVA

java - 如何将 JFreechart 添加到面板?

java - 下面这两个有什么区别?

spring - hibernate 环境 : How to capture who deleted an entity in audit table

java - REQ : Retrieving properties in my java app. 由 "PropertyPlaceholderConfigurer"收集 - 属性作为键/值存储在数据库中