首页 文章

是Spring @Controller rsp . @RestController与spring-data-jpa存储库的事务处理?它看起来像

提问于
浏览
1

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

这是一个简单的REST-Api,用于由spring-data-jpa支持的简单 Book 实体 Repository 我正在试验 @RestController 的delete-method的不同实现 . 调用序列时

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

在SpringBoot应用程序的 main() -method中,JPA / Hibernate将生成2个 select -statements,如日志中所示(为清晰起见,缩写):

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() 只接受附加/管理实体,因此在 find() 的结果上调用 EntityManager.remove() 之前,spring-data delete() -mplemple会执行 EntityManager-find() . 然而, @RestController 注释类中的相同序列只会调用 select 一次 . 此外,一个小实验强烈建议此方法在事务内部运行,并且某些EntityManager的持久性上下文在这里显然是活动的:

@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 -methods在事务之外运行 . 实际上,有人讨论@Controller应该或不应该是 @Transactional . 我的 @Controller 不是 .

那么这种观察到的行为怎么可能呢?是否有一些文件解释这个?

为了完整起见,这里是类定义:控制器:

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

    (...)

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

spring-data-jpa接口:

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

1 回答

  • 2

    @ M.Deinum感谢您指点我 OpenEntityManagerInViewInterceptor -issue . 我现在发现了明显臭名昭着的OSIV / OEMIV(在视图中打开会话/在视图中打开EntityManager)-discussion(即如果 EntityManager 仍然在控制器方法中打开,从而防止 LazyLoading -issues或者应该相反,这些问题暴露?

    AND:默认应该是什么?此链接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 ,它确实像宣传的那样工作 .

相关问题