Открытые соединения с Hibernate + Spring + C3P0 + Многопоточность

У меня есть следующий абстрактный класс конфигурации:

@Configuration
@ComponentScan("my.app")
@EnableTransactionManagement
public abstract class AbstractApplicationConfig {
protected HibernateTransactionManager newHibernateTransactionManager(final LocalSessionFactoryBean localSessionFactoryBean) {
    final HibernateTransactionManager hibernateTransactionManager = new HibernateTransactionManager();
    hibernateTransactionManager.setSessionFactory(localSessionFactoryBean.getObject());
    return hibernateTransactionManager;
}

protected LocalSessionFactoryBean newLocalSessionFactoryBean(final DataSource dataSource, final String packagesToScan) {
    final LocalSessionFactoryBean localSessionFactoryBean = new LocalSessionFactoryBean();
    localSessionFactoryBean.setPackagesToScan(packagesToScan);
    localSessionFactoryBean.setDataSource(dataSource);

    final Properties properties = new Properties();
    properties.setProperty("hibernate.dialect", env.getRequiredProperty("databasePlatform"));
    properties.setProperty("hibernate.show_sql", env.getRequiredProperty("showSql"));
    properties.setProperty("hibernate.jdbc.batch_size", 10);
    properties.setProperty("hibernate.order_inserts", true);
    properties.setProperty("hibernate.order_updates", true);
    properties.setProperty("hibernate.jdbc.batch_versioned_data", true);

    properties.setProperty("hibernate.c3p0.min_size", 10);
    properties.setProperty("hibernate.c3p0.max_size", 50);
    properties.setProperty("hibernate.c3p0.timeout","300");
    properties.setProperty("hibernate.c3p0.max_statements", 50);
    properties.setProperty("hibernate.c3p0.idle_test_period", "3000");

    localSessionFactoryBean.setHibernateProperties(properties);
    return localSessionFactoryBean;
}

protected DataSource newDataSource() {
    final ComboPooledDataSource dataSource = new ComboPooledDataSource();
    try {
        dataSource.setDriverClass(env.getRequiredProperty("dataSource.driverClassName"));
        dataSource.setJdbcUrl(env.getRequiredProperty("dataSource.url"));
        dataSource.setUser(env.getRequiredProperty("dataSource.username"));
        dataSource.setPassword(env.getRequiredProperty("dataSource.password"));
        dataSource.setPreferredTestQuery("SELECT 1 FROM DUAL");
        dataSource.setMaxPoolSize(Integer.valueOf(50));
        dataSource.setMinPoolSize(Integer.valueOf(10));
        dataSource.setMaxStatements(Integer.valueOf(50));
    } catch (final IllegalStateException e) {
        e.printStackTrace();
    } catch (final PropertyVetoException e) {
        e.printStackTrace();
    }
    return dataSource;
}
}

И я использую это так:

@Configuration
public class SchemaApplicationConfig extends AbstractApplicationConfig {

/** DATASOURCES **/

@Bean
public DataSource dsSchema() {
    final DataSource dataSource = newDataSource(DataSourcesNames.SCHEMA1);
    return dataSource;
}

/** SESIONFACTORIES **/

@Bean
public LocalSessionFactoryBean sfSchema() {
    final LocalSessionFactoryBean localSessionFactoryBean = newLocalSessionFactoryBean(dsSchema(), "es.miapp.schema");
    return localSessionFactoryBean;
}

/** TRANSACTIONMANAGERS **/

@Bean
public HibernateTransactionManager txSchema() {
    final HibernateTransactionManager hibernateTransactionManager = newHibernateTransactionManager(sfSchema());
    return hibernateTransactionManager;
}
}

В моем приложении я запускаю процесс с несколькими потоками:

public static void main(String... args) {
    AbstractApplicationContext context =  new AnnotationConfigApplicationContext(SchemaApplicationConfig.class);
    MainProcess mainProcess = context.getBean(MainProcess.class)
    mainProcess.execute();
    context.close();
    System.out.println("ENDS");
}

@Service
@Scope("prototype")
public class MainProcessImpl implements MainProcess{

    @Autowired
    protected ApplicationContext applicationContext;

    public void execute() {
        final ExecutorService executorService = Executors.newFixedThreadPool(5);
        final List<Future<?>> futures = new ArrayList<Future<?>>();
        for (int i = 0; i < 5; i++) {
            final MyService runnable = (MyService) applicationContext.getBean(MyServiceBean.class);
            final Future<?> future = executorService.submit(runnable);
            futures.add(future);
        }

        for (final Future<?> future : futures) {
            try {
                future.get();
            } catch (final InterruptedException e) {
                e.printStackTrace();
            } catch (final ExecutionException e) {
                e.printStackTrace();
            }
        }

        executorService.shutdown();
        try {
            executorService.awaitTermination(2, TimeUnit.MINUTES);
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }
    }
}

MyServiceBean выглядит так:

@Service
@Scope("prototype")
public class MyServiceBean implements Runnable {

    @Autowired
    private MyDao myDao;

    @Override
    public void run() {
        // Do Stuff and create entity
        myDao.insert(entity);
    }
}

И мой Дао выглядит так:

@Repository
public class MyDaoImpl implements MyDao {

    @Autowired
    @Qualifier("sfSchema")
    private SessionFactory sessionFactory;

    @Override
    @Transactional("txSchema")
    public void insert(Entity entity) {
        sessionFactory.getCurrentSession().persist(entity);
        sessionFactory.getCurrentSession().flush();
    }
}

Проблема в том, что Hibernate не закрывает все соединения, и после нескольких запусков этого процесса я получаю следующую ошибку:

Caused by: java.net.BindException: Address already in use: connect

Если я продолжаю выполнять экземпляры, в какой-то момент программа зависает, пытаясь выполнить вставку:

[09-08-2018] [11:15:43,961] [DEBUG] [TEST] checkoutStatement: com.mchange.v2.c3p0.stmt.GlobalMaxOnlyStatementCache stats -- total size: 18; checked out: 6; num connections: 6; num keys: 18
[09-08-2018] [11:15:43,961] [DEBUG] [TEST] insert into ENTITY... COMPLETE SQL
[09-08-2018] [11:15:43,961] [DEBUG] [TEST] Executing batch size: 1

Проверяя Oracle, я вижу, что у моего пользователя уже открыто 46 соединений, и они продолжают расти с каждым прогоном...

Идеи?

0 ответов

Другие вопросы по тегам