@RestController 方法似乎默认是事务性的,为什么?

@RestController methods seem to be Transactional by default, Why?

使用Spring引导1.3.1

我不明白为什么@RestController 默认是事务性的。 我没有在文档中找到任何这样的说法。

推动以下控制器中的方法 findOne() 是事务性这一事实的示例:

@RestController
@RequestMapping("/books")
public class BookController {

    @RequestMapping("/{id}")
    public Book findOne(@PathVariable Long id) {
        Book book = this.bookDao.findOneBookById(id);
        // following line
        // => triggers a select author0_.id as id1_0_0_ etc... // where author0_.id=?
        System.out.println(book.getAuthor().getFirstname()); 
        return book;
    }
}

行与System.out.println(book.getAuthor().getFirstname());应该引发 LazyInitiaizationFailure 但是在这里它是成功的并触发了作者的 select。 所以方法 findOne 似乎是事务性的。 通过 eclipse 调试器,我可以确定触发互补 select 的确实是这一行。 但为什么该方法是事务性的?

@Configuration
@ComponentScan(basePackageClasses = _Controller.class)
@Import(BusinessConfig.class)
public class WebConfig extends WebMvcConfigurerAdapter {
   // ... here the conf to setup Jackson Hibernate4Module
}

@Configuration
@EnableAutoConfiguration
@EnableTransactionManagement
@EntityScan(basePackageClasses = _Model.class)
@ComponentScan(basePackageClasses = { _Dao.class })
public class BusinessConfig {
}

@SpringBootApplication
public class BookstoreStartForWebEmbedded {

    public static void main(String[] args) {
        SpringApplication.run(BookstoreStartForWebEmbedded.class, args);
    }

}

libs : 
spring-boot-starter 1.3.1.RELEASE
spring-boot-starter-test : 1.3.1.RELEASE
spring-boot-starter-valisation : 1.3.1.RELEASE
spring-boot-starter-web : 1.3.1.RELEASE
spring-boot-starter-data-jpa : 1.3.1.RELEASE
postgresql: 9.4-1206-jdbc41
querydsl-jps:3.7.0
jackson-annotations:2.6.4
jackson-datatype-hibernate4:2.6.4

有什么想法吗?

如果它是一项功能,我想将其关闭...

One-to-one关系总是急切的。从方法名book.getAuthor().getFirstname()来看,book->author和author->firstName是这样的关系。 LazyInitializationException 只会出现在惰性集合中。

除了 MirMasej 的回答之外,还有一件事:Spring当满足以下条件时,Boot 将自动注册一个 OpenEntityManagerInViewInterceptor

  • 您有一个网络应用程序
  • 您使用 JPA

在您的情况下,这两个条件都成立。该拦截器在整个请求期间保持实体管理器处于打开状态。自动配置发生在 class JpaBaseConfiguration.

如果您不希望出现这种情况,可以将以下 属性 添加到您的 application.properties 文件中:

spring.jpa.open-in-view=false

顺便说一句。这种行为完全独立于事务,它只与实体管理器的生命周期有关。如果两个事务具有相同的底层实体管理器实例,您仍然可以有两个单独的事务并且没有 LazyInitializationException。

@dunni 的回答绝对精彩而且有效。在我的例子中,它会影响性能,在从 @Controller 调用 @Service@Transactional[ 的方法中=25=] 处理只读 JPA 操作,另一方面,从 @WebServlet 调用相同的方法处理它的速度是原来的两倍。但是设置 spring.jpa.open-in-view=false 来自 @Controller 的处理速度一样快比来自 @WebServlet.

但除此之外,您还可以统一执行以下操作,并以相同的方式注释从 @Controller@Transactional[=25= 调用的方法] 这样,例如:

@GetMapping("/test")
@Transactional(propagation = Propagation.NEVER, readOnly = true)
public void test()