我有一个带有3个分布式dataSources的应用程序(com.atomikos.jdbc.AtomikosDataSourceBean).我正在使用Atomikos事务管理器作为JTA实现.每个dataSource都与 PostgreSQL 数据库一起使用.
现在,我正在调用我对每个dataSource的查询,一切正常.
我想知道,如果有可能,使用JTA,并行调用我的查询(多线程,并发)?
我尝试使用jdbcTemplate(Spring)简单地在新创建的线程中调用查询.首先,我遇到了一个春天问题. Spring将事务上下文存储在ThreadLocal字段中,因此在我的新线程( Spring transaction manager and multithreading
)中无法正确解析.我已经通过将相同的事务上下文设置为新创建的线程的ThreadLocal来解决了这个问题.
但是我在Atomikos代码中面临同样的问题.它们还将CompositeTransactionImp存储在线程范围映射(BaseTrancationManager#getCurrentTx)中.但在Atomikos案例中,不可能为新线程设置值.
所以我不能同时执行我的查询,因为似乎Atomikos不支持这种方法.
但我也查看了JTA规范并发现了以下内容:“多个线程可能同时与同一个全局事务关联.” (“3.2 TransactionManager Interface”, http://download.oracle.com/otndocs/jcp/jta-1.1-spec-oth-JSpec/?submit=Download
)
问题:如何在一个全局事务的范围内使用JTA(2阶段提交)同时向不同的dataSource调用两个或多个查询?
在tomcat上下文中配置DataSources:
factory="com.company.package.AtomikosDataSourceBeanFactory"
xaDataSourceClassName="org.postgresql.xa.PGXADataSource"
xaProperties.serverName="localhost"
xaProperties.portNumber="5451"
xaProperties.databaseName="db1"
uniqueResourceName="jdbc/db1"
xaProperties.user="secretpassword"
xaProperties.password="secretpassword"
minPoolSize="5"
maxPoolSize="10"
testQuery="SELECT 1" />
factory="com.company.package.AtomikosDataSourceBeanFactory"
xaDataSourceClassName="org.postgresql.xa.PGXADataSource"
xaProperties.serverName="localhost"
xaProperties.portNumber="5451"
xaProperties.databaseName="db2"
uniqueResourceName="jdbc/db2"
xaProperties.user="secretpassword"
xaProperties.password="secretpassword"
minPoolSize="5"
maxPoolSize="10"
testQuery="SELECT 1" />
factory="com.company.package.AtomikosDataSourceBeanFactory"
xaDataSourceClassName="org.postgresql.xa.PGXADataSource"
xaProperties.serverName="localhost"
xaProperties.portNumber="5451"
xaProperties.databaseName="db3"
uniqueResourceName="jdbc/db3"
xaProperties.user="secretpassword"
xaProperties.password="secretpassword"
minPoolSize="5"
maxPoolSize="10"
testQuery="SELECT 1" />
Spring环境中的事务管理器配置:
init-method="init" destroy-method="close" lazy-init="true">
码:
final SqlParameterSource parameters = getSqlParameterSourceCreator().convert(entity);
// Solving Spring's ThreadLocal issue: saving thread local params
final Map resourceMap = TransactionSynchronizationManager.getResourceMap();
final List synchronizations = TransactionSynchronizationManager.getSynchronizations();
final boolean actualTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();
final String currentTransactionName = TransactionSynchronizationManager.getCurrentTransactionName();
final AtomicReference exceptionHolder = new AtomicReference();
// Running query in a separate thread.
final Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
// Solving Spring's ThreadLocal issue: setting thread local values to newly created thread.
for (Map.Entry entry : resourceMap.entrySet()) {
TransactionSynchronizationManager.bindResource(entry.getKey(), entry.getValue());
}
if (synchronizations != null && !synchronizations.isEmpty()) {
TransactionSynchronizationManager.initSynchronization();
for (TransactionSynchronization synchronization : synchronizations) {
TransactionSynchronizationManager.registerSynchronization(synchronization);
}
}
TransactionSynchronizationManager.setActualTransactionActive(actualTransactionActive);
TransactionSynchronizationManager.setCurrentTransactionName(currentTransactionName);
// Executing query.
final String query = "insert into ...";
NamedParameterJdbcTemplate template = new NamedParameterJdbcTemplate(dataSourceOne);
template.update(query, parameters);
} catch (final Throwable ex) {
exceptionHolder.set(ex);
}
}
});
thread.start();
// ... same code as above for other dataSources.
// allThreds.join(); - joining to all threads.