未使用jpaFlowExecutionListener关闭数据库连接

时间:2014-05-10 18:45:23

标签: spring spring-webflow

我使用Spring Web Flow构建应用程序。我正在使用Flow Managed Persistence Context,因此实体经理会保持开放状态'在我的流程执行期间,我可以访问延迟加载的属性(类似于Spring {MVC的OpenEntityManagerInViewFilterOpenSessionInViewFilter)。当我使用它时,每次提交表单时,活动数据库连接的数量都会增加,如果我不使用FMPC,我打开连接的数量没有问题。)

我正在使用以下设置。

事务管理

@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,所以一切都好。

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

  
      
  • 注意:默认情况下,除最终提交之外的所有数据访问都是非事务性的。但是,如果基础JPA事务管理器支持此操作,则流可以调用事务服务层以在只读系统事务的上下文中在对话期间获取对象。例如,在使用Hibernate JPA提供程序时,Spring的JPA TransactionManager确实支持这一点。在这种情况下,Spring将处理将FlushMode设置为MANUAL以确保不会刷新对托管持久实体的任何进行中更改,而在事务上读取新对象。
  •   

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

<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,活动连接数也不会重置:

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

1 个答案:

答案 0 :(得分:2)

所以hibernate.connection.release_mode的默认hibernate属性似乎是on_close。考虑到EntityManager在整个流程期间保持打开状态,它永远不会关闭,并且会为流中的每个请求从池中提取新连接。

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

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的持久化上下文完成延迟初始化实体的加载时仍会泄漏连接,并且在转换到未配置的子流时执行此加载。in this bug report中所述(我也找到了这个解决方案)。