При работе с базами данных из Java-приложения одной из проблем, с которой сталкиваются многие, является производительность. Когда требуется много соединений, пул соединений является почти обязательным. Есть довольно много вариантов, чтобы упомянуть несколько:
Трудным путем мы усвоили одну вещь: пул соединений XA не так прост. Большинство пулов соединений не поддерживают соединения XA. Некоторые реализации в документации говорят, что они предоставляют поддержку, но либо не работают, либо есть какие-то скрытые причудливые вещи, которые нам никогда не удавалось найти. В некоторых случаях вы можете обойтись без объединения, но что, если это действительно необходимо? XA транзакции уже тяжелые. Открытие и закрытие соединений каждый раз делает их еще хуже.
Поэтому нам нужен пул соединений, который поддерживает соединения XA. К счастью, есть несколько, но я думаю, что наиболее интересным является XAPool: http://xapool.ow2.org . Однако не слишком волнуйтесь, хотя это звучит легко, чтобы заставить его работать, это не так.
Все тесты, упомянутые в этом блоге, были выполнены с использованием Mule 3.3.2 с использованием JBoss TS в качестве менеджера транзакций для XA. В качестве базы данных мы использовали H2 и Postgresql. Для простоты этого блога мы показываем только конфигурации для базы данных H2. Мы создали очень простой поток Mule, который запускает транзакцию в очереди виртуальных машин Mule и продолжается через JDBC.
Сначала давайте рассмотрим пул соединений Tomcat JDBC. В документации мы можем обнаружить, что эта библиотека обеспечивает поддержку транзакций XA. Однако нам так и не удалось заставить его работать. Мы перепробовали много разных конфигураций, но конечный результат всегда был один и тот же, он на самом деле не работал. Если кто-то может предложить решение, пожалуйста.
Попробуйте номер один, установите внутренний источник данных с помощью источника данных jdbcx H2 и настройте Tomcat Pool XADataSource. Обратите внимание, что в пуле мы ссылаемся на источник данных, это необходимо для создания соединений XA:
<spring:bean id="internalDataSource" class="org.h2.jdbcx.JdbcDataSource" > <spring:property name="URL" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" /> <spring:property name="user" value="" /> <spring:property name="password" value="" /> </spring:bean> <spring:bean id="pooledDataSource" class="org.apache.tomcat.jdbc.pool.XADataSource" > <spring:property name="dataSource" ref="internalDataSource" /> </spring:bean>
Проблема в том, что каждый раз, когда мы пытались получить транзакцию XA из Мула, мы сталкивались с исключением «объект уже закрыт»:
Root Exception stack trace: org.h2.jdbc.JdbcSQLException: The object is already closed [90007-172] at org.h2.message.DbException.getJdbcSQLException(DbException.java:329) at org.h2.message.DbException.get(DbException.java:169) at org.h2.message.DbException.get(DbException.java:146)
Чтобы попытаться это исправить, мы попытались обернуть источник данных H2 в XAPool StandardXADatasource на тот случай, если для XA не было правильно создано соединение. Это конфигурация:
<spring:bean id="internalDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <spring:property name="driverName" value="org.h2.Driver" /> <spring:property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" /> <spring:property name="user" value="" /> <spring:property name="password" value="" /> </spring:bean> <spring:bean id="pooledDataSource" class="org.apache.tomcat.jdbc.pool.XADataSource" > <spring:property name="dataSource" ref="internalDataSource" /> </spring:bean>
Это никак не помогло, возникла та же проблема, только немного другое исключение:
java.sql.SQLException: Connection is closed at org.enhydra.jdbc.standard.StandardConnectionHandle.preInvoke(StandardConnectionHandle.java:117) at org.enhydra.jdbc.core.CoreConnection.getAutoCommit(CoreConnection.java:104) at org.mule.transport.jdbc.xa.ConnectionWrapper.getAutoCommit(ConnectionWrapper.java:113)
Важно отметить, что обе конфигурации работают, если мы вычтем пул JDBC Tomcat. Ясно, что этот пул на самом деле не работает для транзакций XA, по крайней мере с Mule.
Последний вариант — попробовать напрямую с XAPool. XAPool предоставляет класс StandardXAPoolDataSource, поэтому это выглядело довольно многообещающе. На их сайте у них даже есть пример: здесь . Подожди, извини. Несмотря на то, что пример называется StandardXAPoolDataSource, он фактически использует StandardXADataSource без объединения в пул! Приятно!! (Я совершенно саркастичен на тот случай, если вы не заметили.)
Итак, давайте углубимся и попробуем настроить StandardXAPoolDataSource:
<spring:bean id="internalDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <spring:property name="driverName" value="org.h2.Driver" /> <spring:property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" /> <spring:property name="user" value="" /> <spring:property name="password" value="" /> </spring:bean> <spring:bean id="pooledDataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown"> <spring:property name="user" value="" /> <spring:property name="password" value="" /> <spring:property name="dataSource" ref="internalDataSource" /> </spring:bean>
Это выглядит многообещающе. Однако здесь есть огромная проблема. StandardXAPoolDataSource не реализует javax.sql.XADataSource. Он реализует только javax.sql.DataSource. Это означает, что когда Mule пытается получить транзакцию XA, он не справится и выдает следующее исключение:
org.mule.api.transaction.TransactionException: Endpoint is transactional but transaction does not support it at org.mule.transport.AbstractConnector.getTransactionalResource(AbstractConnector.java:2015) at org.mule.transport.jdbc.JdbcMessageDispatcher.doSend(JdbcMessageDispatcher.java:71) at org.mule.transport.AbstractMessageDispatcher.process(AbstractMessageDispatcher.java:81)
При внимательном рассмотрении источника, кажется, что все на месте. Только отсутствует реализация javax.sql.XADataSource и некоторые его методы. Но они нигде не появляются в библиотеке XAPool.
Установив это, мы реализовали класс, который расширяет StandardXAPoolDataSource и реализует javax.sql.XADataSource. Реализация метода getXaConnection () — это просто вызов метода getConnection () суперкласса, который выполняет всю работу по созданию пула.
package com.ricston.jdbc.xapool; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.logging.Logger; import javax.sql.XAConnection; import javax.sql.XADataSource; import org.enhydra.jdbc.pool.StandardXAPoolDataSource; import org.enhydra.jdbc.standard.StandardXAConnectionHandle; public class RicstonStandardXAPoolDataSource extends StandardXAPoolDataSource implements XADataSource { /** * */ private static final long serialVersionUID = -6060990263159819182L; @Override public XAConnection getXAConnection() throws SQLException { return ((StandardXAConnectionHandle) this.getConnection()).xacon; } @Override public XAConnection getXAConnection(String user, String password) throws SQLException { return ((StandardXAConnectionHandle) this.getConnection(user, password)).xacon; } public Logger getParentLogger() throws SQLFeatureNotSupportedException{ throw new SQLFeatureNotSupportedException(); } }
Теперь нам нужно только сконфигурировать его в Spring и подключить его к разъему JDBC нашего Mule, и все готово.
<spring:bean id="internalDataSource" class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown"> <spring:property name="driverName" value="org.h2.Driver" /> <spring:property name="url" value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1" /> <spring:property name="user" value="" /> <spring:property name="password" value="" /> </spring:bean> <spring:bean id="pooledDataSource" class="com.ricston.jdbc.xapool.RicstonStandardXAPoolDataSource" destroy-method="shutdown"> <spring:property name="user" value="" /> <spring:property name="password" value="" /> <spring:property name="dataSource" ref="internalDataSource" /> </spring:bean>
В следующем посте мы собираемся более подробно рассмотреть, как настроить размер пула и другие интересные атрибуты.
Наслаждаться.