Announcement Announcement Module
Collapse
No announcement yet.
JtaTransactionManager + MySql Replication Driver problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • JtaTransactionManager + MySql Replication Driver problem

    Hi,

    My environment is Resin 3.0.24 and Spring 2.0.8.

    I have the following configuration:

    Code:
    	
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    		<property name="driverClassName" value="com.mysql.jdbc.ReplicationDriver" />
    		<property name="url" value="jdbc:mysql://master,slave/test?" />
    		<property name="username" value="root" />
    		<property name="password" value="******" />
    	</bean>
    
     	<bean id="jtaTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>
    
    	<tx:annotation-driven transaction-manager="jtaTransactionManager"/>
    and my service and DAO classes are annotated as following:

    Code:
    public interface FooService {
        <at>Transactional
        void doSomething();
    }	
    
    <at>Transactional
    public interface BarDAO {
    
         int update(Object obj);
    
         <at>Transactional(readOnly=true, propagation=Propagation.REQUIRES_NEW)
         Object read();
    }
    
    public class FooServiceImpl implements FooService {
         private BarDAO _barDao;
    
         void doSomething(){
            Object obj = _barDAO.read();
            .......
            _barDAO.update(obj);
         }
    }

    When I use DataSourceTransactionManager instead of JtaTransactionManager it works as expected: read operation goes to the slave db and update operation goes to the master db. But when I switch to use JtaTransactionManager my read-only operation goes to master db. Am I missing something in configuration to make JtaTransactionManager work properly?
    The reason why I need to use a JtaTransactionManager is because I have multiple datasources defined in my spring context.

    I'm relatively new to Spring, so any help will be appreciated.

    Thanks.

  • #2
    Start by switching to use a XA capable datasource, else jta managed transactions aren't going to work.

    In regard to your configuration make sure that only your service layer is transactional, next to that you should define the @Transactional on the concrete implementation (that is the recommendation).
    Last edited by Marten Deinum; Sep 8th, 2008, 04:36 PM.

    Comment


    • #3
      Thank you for the reply. In tried to use xapool and wrap mysql jdbc driver into XA datasource did not seem to help:

      Code:
       	<bean id="dataSource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">
         		<property name="dataSource">
      			<bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">
      		   		<property name="transactionManager" ref="jotm"/>
      		   		<property name="driverName" value="com.mysql.jdbc.ReplicationDriver"/>
      		   		<property name="url" value="jdbc:mysql://master,slave/test?"/>
         		                <property name="user" value="root"/>
         		                <property name="password" value="*******"/>
      		 	</bean>
         		</property>
         		<property name="user" value="root"/>
         		<property name="password" value="*******"/>
         		<property name="maxSize" value="3"/>
       	</bean>
      	<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>
      Any ideas why?

      Thank you.

      Comment


      • #4
        Spring seems to not call conn.setReadOnly() when Transactional(readOnly = true)

        I have the same situation.
        I'm using XA datasource with Jboss.
        TransactionManager is JTA
        Using Spring aspect to apply transactions.
        SpringApplicatonContext:
        Code:
        <bean id="transactionManager"
        		class="org.springframework.transaction.jta.JtaTransactionManager">
        		</bean>
        
        
        	<tx:annotation-driven transaction-manager="transactionManager"
        		mode="aspectj" />
        
        	<bean
        		class="org.springframework.transaction.aspectj.AnnotationTransactionAspect"
        		factory-method="aspectOf" dependency-check="none">
        		<property name="transactionManager" ref="transactionManager" />
        	</bean>
        
        
        	<context:component-scan base-package="com.company.module" />
        	<context:spring-configured />
        	<context:annotation-config />
        Datasource config:
        Code:
        <xa-datasource>
          <xa-datasource- class>com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</xa-datasource-class>
           <jndi-name>DefaultDS</jndi-name> 
           <xa-datasource-property   name="URL">jdbc:mysql://masterHost:3306,slaveHost:3306/db</xa-datasource-property>
        	
           <driver-class>com.mysql.jdbc.ReplicationDriver</driver-class>  
           <autoReconnect>true</autoReconnect>
           <roundRobinLoadBalance>true</roundRobinLoadBalance> 
           <user-name>...</user-name>
           <password>...</password>
         
        </xa-datasource>
        The transactional method :
        Code:
        @Transactional(readOnly = true)
        	public void findValueFromSlaveDb(){
                //find object from slave db using hibernate
                ...
                }
        If that is the case, is there a reason why this feature has been removed from spring ?

        Comment


        • #5
          very good ,good

          Comment


          • #6
            JtaTransactionManager + MySql Replication Driver + Mybatis

            It's pretty old post but I'll answer that question anyway, so maybe i'll help to find an answer to someone else.
            The problem is that JtaTransactionManager does not have access to connection itself and is just not able to set readOnly flag. The only way to solve this problem is to write your own code which will set this flag on the connection. You may ask where to find such info about transaction from right? The answer is: TransactionSynchronizationManager which manages transaction synchronizations per thread. I can't give you solution for every case, but for example in case of MyBatis you can extend ManagedTransaction and TransactionFactory classes and put needed logic there:


            Your own factory is needed only to create your extended transaction.
            Code:
            public class YourManagedTransactionFactory implements TransactionFactory {
            
                private boolean closeConnection = true;
            
                public void setProperties(Properties props) {
                  if (props != null) {
                    String closeConnectionProperty = props.getProperty("closeConnection");
                    if (closeConnectionProperty != null) {
                      closeConnection = Boolean.valueOf(closeConnectionProperty);
                    }
                  }
                }
            
                public Transaction newTransaction(Connection conn) {
                  return new YourManagedTransaction(conn, closeConnection);
                }
            
                public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
                  // Silently ignores autocommit and isolation level, as managed transactions are entirely
                  // controlled by an external manager.  It's silently ignored so that
                  // code remains portable between managed and unmanaged configurations.
                  return new YourManagedTransaction(ds, level, closeConnection);
                }
              }
            All magic is done is getConnection method:
            Code:
            public class YourManagedTransaction extends ManagedTransaction {
            
                private final static Logger LOG = LoggerFactory.getLogger(YourManagedTransaction.class);
            
                public YourManagedTransaction(Connection connection, boolean closeConnection) {
                    super(connection, closeConnection);
                }
            
                public YourManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) {
                    super(ds, level, closeConnection);
                }
            
                @Override
                public Connection getConnection() throws SQLException {
                    Connection con = super.getConnection();
                    boolean readOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
                    if (readOnly) {
                        con.setReadOnly(readOnly); 
                    }
                    return con;
                }
            }

            Spring configuration for MyBatis:
            Code:
                <jee:jndi-lookup id="dataSource" jndi-name="java:jdbc/yourDS" expected-type="javax.sql.DataSource" />  
                
                <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
                    <property name="transactionManagerName" value="java:/TransactionManager" />
                    <property name="userTransactionName" value="java:comp/UserTransaction" />
                </bean>
                
                <tx:annotation-driven  transaction-manager="transactionManager" />
            
                <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
            		<property name="configLocation" value="classpath:mybatis-config.xml" />
            		<property name="dataSource" ref="dataSource" />
                	<property name="transactionFactory">
            			<bean class="com.xxx.YourManagedTransactionFactory" />
            		</property>  
                </bean>

            Regards,
            Maciek

            Comment

            Working...
            X