数据库连接未使用jpaFlowExecutionListener关闭 [英] Database connections not being closed with jpaFlowExecutionListener

查看:110
本文介绍了数据库连接未使用jpaFlowExecutionListener关闭的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Spring Web Flow来构建应用程序.我正在使用Flow Managed Persistence Context,因此实体管理器在流程执行期间处于打开状态",并且可以访问延迟加载的属性(类似于Spring MVC的OpenEntityManagerInViewFilterOpenSessionInViewFilter).使用此功能时,每次提交表单时,活动数据库连接的数量都会增加,如果不使用FMPC,打开的连接数量也不会出现问题.

I'm using Spring Web Flow to build an application. I am making use of the Flow Managed Persistence Context so the entity manager is 'kept open' during the execution of my flow and I can access lazy loaded properties (similar to OpenEntityManagerInViewFilter or OpenSessionInViewFilter for Spring MVC). When I use this, every time I submit a form, the number of active database connections increases, if I don't use the FMPC, I have no problems with the number of open connections).

我正在使用以下设置.

TransactionManager :

@Bean
@Autowired
public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
    return new JpaTransactionManager(entityManagerFactory);
}

数据源:

@Bean
public DataSource dataSource() {
    final BasicDataSource dataSource = new BasicDataSource();
    dataSource.setDriverClassName(environment.getRequiredProperty(PROPERTY_DATABASE_DRIVER));
    dataSource.setUrl(environment.getRequiredProperty(PROPERTY_DATABASE_URL));
    dataSource.setUsername(environment.getProperty(PROPERTY_DATABASE_USERNAME, ""));
    dataSource.setPassword(environment.getProperty(PROPERTY_DATABASE_PASSWORD, ""));
    return dataSource;
}

EntityManagerFactory :

@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
    final LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
    factoryBean.setDataSource(dataSource());
    factoryBean.setPackagesToScan(environment.getRequiredProperty(PROPERTY_ENTITYMANAGER_PACKAGES_TO_SCAN));

    final JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter() {
        {
            setDatabase(Database.valueOf(environment.getRequiredProperty(PROPERTY_DATABASE_TYPE)));
            setDatabasePlatform(environment.getRequiredProperty(PROPERTY_HIBERNATE_DIALECT));
        }
    };
    factoryBean.setJpaVendorAdapter(vendorAdapter);

    final Properties jpaProperties = new Properties();
    jpaProperties.put(PROPERTY_HIBERNATE_FORMAT_SQL, environment.getRequiredProperty(PROPERTY_HIBERNATE_FORMAT_SQL));
    jpaProperties.put(PROPERTY_HIBERNATE_NAMING_STRATEGY, environment.getRequiredProperty(PROPERTY_HIBERNATE_NAMING_STRATEGY));
    jpaProperties.put(PROPERTY_HIBERNATE_SHOW_SQL, environment.getRequiredProperty(PROPERTY_HIBERNATE_SHOW_SQL));
    jpaProperties.put(PROPERTY_HIBERNATE_HB2DDL_SQL, environment.getRequiredProperty(PROPERTY_HIBERNATE_HB2DDL_SQL));

    factoryBean.setJpaProperties(jpaProperties);

    return factoryBean;
}

JpaFlowExecutionListener :

@Bean
@Autowired
public JpaFlowExecutionListener jpaFlowExecutionListener(EntityManagerFactory entityManagerFactory, JpaTransactionManager transactionManager) {
    return new JpaFlowExecutionListener(entityManagerFactory, transactionManager);
}

BasicDataSource默认情况下将maxActive设置为 8 ,当我达到8个活动连接时,该页面只是挂起.请求完成后,为什么没有关闭连接?我已使用Chrome调试工具(网络窗格)来确保没有正在运行的AJAX请求或其他任何内容,我的页面提交(HTTP POST)触发了301重定向,然后为我提供了新的HTTP GET,并导致出现状态200,一切都很好.

The BasicDataSource has maxActive set to 8 by default and when I reach 8 active connections, the page just hangs. Why are the connections not being closed after the request is complete? I have used the Chrome debugging tools (the network pane) to make sure there are not AJAX requests running or anything, my page submit (an HTTP POST) triggers a 301 redirect which then gives me a new HTTP GET and that results in a status 200, so all good.

从一页转到下一页时,会调用一个服务层,但是正如您从我的bean中所看到的那样,我正在使用JpaTransactionManager,SWF文档中说明了以下内容:

When going from one page to the next, a service layer is called but as you can see from my beans, I am using the JpaTransactionManager and the SWF documentation says the following:

  • 注意:默认情况下,除最终提交外,所有数据访问都将是非事务性的.但是,如果基础JPA事务管理器支持,则流程可以调用事务服务层以在只读系统事务的上下文中的会话期间获取对象.例如,当与Hibernate JPA提供程序一起使用时,Spring的JPA TransactionManager确实支持此功能.在那种情况下,Spring会处理将FlushMode设置为MANUAL的情况,以确保在刷新新对象的操作时,不会刷新对托管持久性实体的任何正在进行的更改.
  • Note: All data access except for the final commit will, by default, be non-transactional. However, a flow may call into a transactional service layer to fetch objects during the conversation in the context of a read-only system transaction if the underlying JPA Transaction Manager supports this. Spring's JPA TransactionManager does support this when working with a Hibernate JPA provider, for example. In that case, Spring will handle setting the FlushMode to MANUAL to ensure any in-progress changes to managed persistent entities are not flushed, while reads of new objects occur transactionally.

为了完整起见,我的spring-web-flow配置:

For the sake of completeness, my spring-web-flow config:

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd">

    <!-- Flow executor, repsonsible for creating and executing flows -->
    <webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
        <webflow:flow-execution-listeners>
            <webflow:listener ref="jpaFlowExecutionListener"/>
        </webflow:flow-execution-listeners>
    </webflow:flow-executor>

    <!-- Flow registry, responsible for loading all flows so executor can execute them -->
    <webflow:flow-registry id="flowRegistry" base-path="/WEB-INF/webflow/flows" flow-builder-services="flowBuilderServices">
        <webflow:flow-location-pattern value="/**/*-flow.xml"/>
    </webflow:flow-registry>

    <!-- Flow builder services -->
    <webflow:flow-builder-services id="flowBuilderServices" view-factory-creator="mvcViewFactoryCreator"/>

    <!-- MvcViewFactoryCreator -->
    <bean id="mvcViewFactoryCreator" class="org.springframework.webflow.mvc.builder.MvcViewFactoryCreator">
        <property name="viewResolvers">
            <list>
                <ref bean="viewResolver"/>
            </list>
        </property>
    </bean>

    <!-- Flow handler adapter, responsible for answering request for a flow -->
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerAdapter">
        <property name="flowExecutor" ref="flowExecutor"/>
    </bean>

    <!-- Flow handler mapping, lets Spring MVCs DispatcherServlet know to send flow request to SWF -->
    <bean class="org.springframework.webflow.mvc.servlet.FlowHandlerMapping">
        <property name="flowRegistry" ref="flowRegistry"/>
        <property name="order" value="0"/>
        <property name="interceptors">
            <list>
                <ref bean="localeChangeInterceptor" />
            </list>
        </property>
    </bean>
</beans>

我的流程顶部定义了<persistence-context />.

我具有以下结束状态(重新启动流程),即使我调用此状态并且URL参数更改为e2s1,活动连接的数量也不会重置:

I have the following end-state (which restarts the flow), even when I invoke this and the URL params change to e2s1, the number of active connections is not reset:

<end-state id="restart" commit="true" view="redirect:/main"/>

推荐答案

因此,看来hibernate.connection.release_mode的默认休眠属性是on_close.考虑到EntityManager在整个流程中始终保持打开状态,因此它永远不会关闭,并且针对该流程中的每个请求都会从池中获取一个新的连接.

So it seems that the default hibernate property for hibernate.connection.release_mode is on_close. Considering the EntityManager is kept open during the whole flow, it never closes and a new connection is fetched from the pool for every request within the flow.

将属性更改为after_transaction可解决此问题.但是,在获取延迟加载的集合的情况下,它仍然不起作用,每个惰性属性都将从池中获取一个新的连接.为了解决这个问题,我扩展了JpaFlowExecutionListener:

Changing the property to after_transaction solves this issue. However, in the case of fetching lazily loaded collections, it still doesn't work, each lazy property will fetch a new connection from the pool. In order to solve this I extended the JpaFlowExecutionListener with this:

public class AvoidLeakJpaFlowExecutionListener extends JpaFlowExecutionListener {

    public AvoidLeakJpaFlowExecutionListener(EntityManagerFactory entityManagerFactory, PlatformTransactionManager transactionManager) {
        super(entityManagerFactory, transactionManager);
    }

    @Override
    public void paused(RequestContext context) {
        super.paused(context);
        EntityManager entityManager = (EntityManager) context.getFlowScope().get(PERSISTENCE_CONTEXT_ATTRIBUTE);
        if (entityManager != null && entityManager instanceof HibernateEntityManager) {
            HibernateEntityManager hibernateEntityManager = (HibernateEntityManager) entityManager;
            hibernateEntityManager.getSession().disconnect();
        }
    }
}

这种方法解决了延迟加载的集合问题,但是当使用WebFlow的持久性上下文完成延迟初始化的实体的加载并且在过渡到未配置的子流期间执行此加载时,仍然会泄漏连接. em>(如在此错误报告中的 中所述)(我也在其中找到了此解决方案).

This approach solves the lazily loaded collections problem but will still leak connections when loading of lazy-initialized entities is done using WebFlow's persistence context and this loading is performed during the transition to subflow that does not have configured. as described in in this bug report (where I found this solution as well).

这篇关于数据库连接未使用jpaFlowExecutionListener关闭的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆