Как я могу запросить конкретное соединение у com.mchange.v2.c3p0.ComboPooledDataSource?

#java #datasource #connection-pooling

#java #источник данных #объединение пулов соединений

Вопрос:

Проблема:

  • Программа используется com.mchange.v2.c3p0.ComboPooledDataSource для подключения к серверу Sybase
  • Программа выполняет 2 метода runSQL1() и runSQL2() последовательно
  • runSQL1() выполняет SQL, который создает #temptable

     SELECT * INTO #myTemp FROM TABLE1 WHERE X=2
      
  • runSQL2() выполняет SQL, который считывает из этого #temptable

     SELECT * FROM #myTemp WHERE Y=3
      
  • ПРОБЛЕМА: runSQL2() из пула передается другое соединение с БД, отличное от того, которое было передано runSQL1() .

    Однако Sybase #temptables зависит от соединения, поэтому runSQL2() сбой происходит, когда он не может найти таблицу.

Наиболее очевидное решение, которое я могу придумать (помимо вырожденного решения о создании пула размером 1, после чего нам даже не нужен пул), — это каким-то образом запомнить, какое конкретное соединение из пула использовалось runSQL1() , и runSQL2() запросить то же соединение.

Есть ли способ сделать это в com.mchange.v2.c3p0.ComboPooledDataSource ?

Если возможно, я хотел бы получить ответ, который безопасен для параллелизма (другими словами, если соединение, используемое в runSQL1(), используется другим потоком, вызов runSQL2() для получения соединения будет ждать, пока это соединение не будет освобождено другим потоком).

Однако, если это невозможно, я согласен с ответом, который предполагает, что все соединения с БД (те, о которых я забочусь) происходят в одном потоке, и поэтому любое соединение, запрошенное runSQL2(), будет доступно на 100%, если оно было доступно для runSQL1() .

Я также приветствую любые решения, которые решают проблему каким-либо другим способом, если они не включают «прекратить использование #temptables» как часть решения.

Комментарии:

1. Почему вы не можете запросить соединение перед запуском part1, а затем передать его в качестве параметра для обоих вызовов?

2. @ivan сделал это. Но мне не нравится это решение, поэтому я хотел бы знать, как получить конкретное соединение вместо этого

3. Я также считаю, что решение, предложенное @Ivan, является правильным решением. Чтобы получить его любым другим способом, вам необходимо сохранить хотя бы идентификатор соединения при его первом получении. После этого вам нужно использовать отражение в менеджерах пулов и найти соответствующее соединение. Вместо этого просто хранить ссылку на конкретное соединение более чисто. Если вам действительно нужно сделать это, запросив идентификатор, вы можете написать свой собственный менеджер соединений-оболочек и использовать его.

4. Что вы делаете со своим ComboPooledDataSource ? Он должен оставаться открытым на время использования базы данных. Используйте его, чтобы получить из него ваш объект подключения. Если #temptable зависит от определенного соединения, вы можете повторно использовать только то же соединение, с которым вы выполнили runSQL1() . Кроме того, вы пытались создать разделяемую временную таблицу ?

Ответ №1:

Самый простой и очевидный способ сделать это — просто запросить соединение из пула, а затем запустить runSQL1() и runSQL2() с этим соединением. Шаблон использования, предлагаемый в вопросе, противоречит общим принципам проектирования менеджеров пулов соединений, поскольку он эффективно продвигает их к какому-то менеджеру транзакций.

Существуют Java-фреймворки, которые могут помочь в этом. Например, весной @Transaction или TransactionTemplate может использоваться для разграничения границ транзакций, и это гарантирует, что одно соединение используется одним потоком (или, точнее, в соответствии с аннотациями распространения транзакций). Spring может использовать множество менеджеров транзакций, но, вероятно, проще всего было бы использовать DataSourceTransactionManager, и его также можно настроить для использования c3p0 как DataSource .