Atomikos Forum |
|
I am using Atomikos 3.6.5 with Spring, Hibernate and Derby. Derby is used as a temporary database in embedded mode. The purpose is to provide each user session with a temp copy of the app's permanent db. The temp db contains a base schema, which holds the tables, from which temp schemas are created, each temp schema holding a copy of the base schema. Each user is assigned a temp schema when a session starts. A SchemaAwareDataSourceProxy class, extension of another Spring proxy class, overrides the getConnection() method on the data source. The temp schema is first assigned, and when getConnection() is called, it calls super.getConnection(), uses it to set the temp schema, and returns the conn. I had this working once, and somewhere along the line in adding various services to this app it stopped working. I thought I had it working after I added Atomikos to the mix.
I hope this expl is clear. The proxying is apparently not reaching deep enough into Atomikos's doings. I need some equivalent way of intercepting a connection for an XA data source, among the suite of XAs Atomikos is keeping track of. Here is the overridden method. Below that is the Spring config, and below that the driver for my test. The sample config has only the one XA data source; the full app has two other XA DSes. Thanks for any advice. === /** * override of getConnection */ public Connection getConnection() throws SQLException { Connection con = super.getConnection(); String schemaName = (String) schemaHolder.getSchemaName(); if (schemaName == null) throw new IllegalStateException("SchemaName not set. Call setSchemaName before doing any database access"); setSchemaOnConnection(con, schemaName); return con; } === <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <!--<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">--> <beans> <bean id="daoGetter" class="com.kve.vanguard.model.orm.dao.GetDao"/> <bean id="tempDBInitializer" class="com.kve.vanguard.model.orm.temp.TempDBInitDriver"/> <bean id="vanguardTempDataSource" class="com.kve.vanguard.model.orm.dao.SchemaAwareDataSourceProxy"> <property name="targetDataSource"><ref local="vanguardTempDataSourceTarget"/></property> <property name="schemaName"><value>APP</value></property> </bean> <bean id="vanguardTempDataSourceTarget" class="com.atomikos.jdbc.AtomikosDataSourceBean"> <property name="uniqueResourceName" value="VANGUARDTEMP" /> <property name="xaDataSourceClassName"> <value>org.apache.derby.jdbc.EmbeddedXADataSource40</value> </property> <property name="xaProperties"> <props> <prop key="databaseName">vanguard</prop> <prop key="connectionAttributes">create=true;</prop> </props> </property> <property name="poolSize" value="3" /> </bean> <bean id="vanguardTempInitializer" class="org.springframework.jdbc.datasource.init.DataSourceInitializer"> <property name="dataSource" ref="vanguardTempDataSource"/> <property name="enabled" value="true"/> <property name="databasePopulator"> <bean class="org.springframework.jdbc.datasource.init.ResourceDatabasePopulator"> <property name="continueOnError" value="true"/> <property name="ignoreFailedDrops" value="true"/> <property name="sqlScriptEncoding" value="UTF-8"/> <property name="scripts"> <list> <value type="org.springframework.core.io.Resource"> classpath:init_tempdb.sql </value> </list> </property> </bean> </property> </bean> <bean id="tempSessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"> <property name="packagesToScan" value="com.kve.vanguard.model.orm.domain, com.kve.vanguard.model.arrays" /> <property name="dataSource" ref="vanguardTempDataSource" /> <property name="configLocation"> <value>classpath:temp-hibernate.cfg.xml</value> </property> <property name="configurationClass"> <value>org.hibernate.cfg.AnnotationConfiguration</value> </property> <property name="hibernateProperties"> <props> <prop key="hibernate.connection.url">jdbc:derby:vanguard</prop> <prop key="hibernate.default_schema">vanguardtemp</prop> <prop key="hibernate.transaction.factory_class">com.atomikos.icatch.jta.hibernate3.AtomikosJTATransactionFactory</prop> <prop key="hibernate.transaction.manager_lookup_class">com.atomikos.icatch.jta.hibernate3.TransactionManagerLookup</prop> <prop key="hibernate.current_session_context_class">jta</prop> <prop key="hibernate.dialect">org.hibernate.dialect.DerbyDialect</prop> <prop key="hibernate.show_sql">false</prop> <prop key="hibernate.cache.use_second_level_cache">false</prop> </props> </property> </bean> <bean id="userTransactionService" class="com.atomikos.icatch.config.UserTransactionServiceImp" init-method="init" destroy-method="shutdownForce"> <constructor-arg> <props> <prop key="com.atomikos.icatch.service"> com.atomikos.icatch.standalone.UserTransactionServiceFactory </prop> <prop key="com.atomikos.icatch.automatic_resource_registration">true</prop> <prop key="com.atomikos.icatch.console_log_level">DEBUG</prop> </props> </constructor-arg> </bean> <bean id="AtomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close" depends-on="userTransactionService"> <property name="forceShutdown" value="false" /> </bean> <bean id="AtomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp" depends-on="userTransactionService"> <property name="transactionTimeout" value="300" /> </bean> <bean id="JtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" depends-on="userTransactionService"> <property name="transactionManager" ref="AtomikosTransactionManager" /> <property name="userTransaction" ref="AtomikosUserTransaction" /> </bean> <import resource="temp-dao-config.xml"/> </beans> ======= package com.kve.vanguard.main; import org.springframework.context.ApplicationContext; import org.springframework.transaction.jta.JtaTransactionManager; import org.springframework.context.support.ClassPathXmlApplicationContext; import com.atomikos.icatch.jta.UserTransactionImp; import com.kve.vanguard.model.orm.domain.*; import com.kve.vanguard.model.orm.dao.*; public class Main { /** * @param args */ public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"}); JtaTransactionManager txmgr = (JtaTransactionManager) context.getBean("JtaTransactionManager"); /*SchemaAwareDataSourceProxy sads = (SchemaAwareDataSourceProxy) context.getBean("vanguardTempDataSource"); sads.setSchemaName("TEMP01");*/ UserTransactionImp tx = (UserTransactionImp) txmgr.getUserTransaction(); try { tx.begin(); SchemaAwareDataSourceProxy sads = (SchemaAwareDataSourceProxy) context.getBean("vanguardTempDataSource"); sads.setSchemaName("TEMP01"); Dao locDaoTemp = GetDao.LocationTemp(); Location locTemp = new Location() ; locTemp.setLocationId("MAINL"); locTemp.setCurrencyCodeMaster_FK("CD$"); String locId = (String) locDaoTemp.create(locTemp); tx.commit(); System.out.println("Location created: " + locId); } catch (Exception e) { System.out.println("Main Exception: " + e.getMessage() + " " + e.getCause()); }; } }
PS--under debug, the overridden getConnection() method is called as expected, when the tx is committed. The schema is duly set, from the value passed to setSchema(), in setSchemaOnConnection(). It just doesn't take with Atomikos somehow. Is the schema setting stmt actually executed or is it deferred because of JTA and executed with the SQL comprising the tx, and thus the schema is somehow not in effect?
private void setSchemaOnConnection(Connection con, String schemaName) throws SQLException { if (logger.isDebugEnabled()) logger.debug("Setting schema to " + schemaName + " on connection " + con); Statement stmt = con.createStatement(); try { stmt.execute("set schema = '" + schemaName + "'"); } catch (Exception e) { System.out.println("Exception in setSchema: " + e.getMessage() + e.getCause()); } finally { stmt.close(); } }
Here is the call stack at the setSchemaOnConnection() breakpoint. It look like Hibernate until near the top (?)
Thread [main] (Suspended (breakpoint at line 63 in SchemaAwareDataSourceProxy)) SchemaAwareDataSourceProxy.setSchemaOnConnection(Connection, String) line: 63 SchemaAwareDataSourceProxy.getConnection() line:47 TransactionAwareDataSourceConnectionProvider(LocalDataSourceConnectionProvider).getConnection() line: 81 ConnectionManager.openConnection() line: 446 ConnectionManager.getConnection() line: 167 NonBatchingBatcher(AbstractBatcher).prepareStatement(String, boolean) line: 116 NonBatchingBatcher(AbstractBatcher).prepareStatement(String) line: 109 NonBatchingBatcher(AbstractBatcher).prepareBatchStatement(String) line: 244 SingleTableEntityPersister(AbstractEntityPersister).insert(Serializable, Object[], boolean[], int, String, Object, SessionImplementor) line: 2252 SingleTableEntityPersister(AbstractEntityPersister).insert(Serializable, Object[], Object, SessionImplementor) line: 2688 EntityInsertAction.execute() line: 79 ActionQueue.execute(Executable) line: 279 ActionQueue.executeActions(List) line: 263 ActionQueue.executeActions() line: 167 DefaultFlushEventListener(AbstractFlushingEventListener).performExecutions(EventSource) line: 321 DefaultFlushEventListener.onFlush(FlushEvent) line: 50 SessionImpl.flush() line: 1027 SessionImpl.managedFlush() line: 365 CacheSynchronization.beforeCompletion() line: 88 Sync2Sync.beforeCompletion() line: 73 TxActiveStateHandler(TransactionStateHandler).commit() line: 253 CompositeTransactionImp.doCommit() line: 511 CompositeTerminatorImp.commit() line: 138 TransactionImp.commit() line: 298 TransactionManagerImp.commit() line: 612 UserTransactionImp.commit() line: 168 Main.main(String[]) line: 50
I fixed this, my fault. I had somehow put a default schema on the temporary data source config, the base temp schema from which the other temp schemas were created. That is where the record was inserted, despite selecting a different schema. When I removed the default schema from the config, the schema selection worked and the record was inserted in the correct table in the correct temp schema.
The SchemaAwareDataSource I used was from the Spring forums, useful way of distinguishing multiple schemas in a single data source, by overriding the getConnection() method and setting the schema, using a value stored in a ThreadLocal variable set on previous call. |