在使用Spring JPA data访问数据库时, 我们要清楚何时业务线程从数据库连接池获取连接,何时释放。

简单说,当Open Session In View 启用时(spring默认配置),在整个http request处理期间,一个JPA session都会绑定到该处理线程,直到该请求处理完成。但是JPA session并不是一开始就对应一个真正的数据库连接,只有当JPA的Repository接口方法被执行时,也就是sql语句要执行时,JPA session才会从数据库连接池申请一个连接。当SQL(JPA Repository方法)执行完成之后,此时连接不会被归还到连接池,不管在你的Service方法上是否加了@Transactional注解。

这样就有一个问题,假设你的service方法调用完了JPA Repository/Dao方法,完事之后又调用了其他方法,比如比较耗时间的remote call。而这些remote call又不应该跟之前的数据库访问在同一个事务中,这时就会导致连接迟迟不会被释放,即使你的service方法上没有加@Transactional注解,或者@Transactional注解只是加在了JPA Repository/Dao方法上,当数据库访问完成之后,连接也不会被释放。这时,如果客户端同一类型并发请求量比较大的时候,就很容易导致数据库连接池连接不够用。

这是我们可以将OSIV禁用,可以通过在springboot的applicaiton.properties添加下边的配置实现。


spring.jpa.open-in-view=false


当禁用了OSIV之后,又分两种情况:

如果你的service方法没有加@Transactional注解, 则当service方法里边调用的JPA Repository/Dao方法执行完成之后,连接会马上归还给连接池。

如果是加了@Transactional注解,则必须等整个service方法执行完成之后,才能释放。

另外,需要注意的是,当禁用了OSIV之后,在一个没有加@Transactional注解的service方法中,如果需要多次访问数据库,则每次都要从连接池申请新的连接,包括JPA的lazy association。

总结:

如果你的程序只是进行基本的数据库CRUD操作,没有大量的其他远程操作,则可以启用OSIV,毕竟这样可以增加程序员的开发效率,不用多次频繁的像链接池申请连接。

但是如果是数据库操作与其他执行比较慢的操作处于同一个http request的处理周期中,比如上边提到的除了访问数据库,还要进行其他耗时的远程调用操作或者本地磁盘IO操作,这时如果这些慢的操作不影响数据库的事务,则应该仅用OSIV,不然无论我们怎么操作,数据库连接都不能在访问数据库之后立即释放,而是等整个HTTP请求处理完成之后才能释放。