Page 1 of 2 12 LastLast
Results 1 to 10 of 17

Thread: @Transactional and 2 datasources

  1. #1
    Join Date
    Feb 2008
    Posts
    8

    Default @Transactional and 2 datasources

    Can and if so, how do you use the @Transactional annotation with more than one data source?

  2. #2
    Join Date
    Jul 2007
    Posts
    101

    Default

    Currently, you can't. You can vote on a Jira issue SPR-3844. In the meantime, I have created my own implementation which I am willing to share here if you want. It follows the basic premise set forth from discussions with Juergen. I would have submitted it, but the Spring people felt it was something they should build on their own. Post back here if you want me to provide you with the code. It is a little quirky because the Spring code goes against their own practices and casts to implementation code in their lower levels so you just can't extend all the underlying classes/interfaces.

  3. #3
    Join Date
    Feb 2008
    Posts
    8

    Default

    Darn, I liked how that works. Thanks for the offer of your code example, but I am in over my head already, I will stick to the AOP way.

    Thanks,

  4. #4
    Join Date
    Jul 2007
    Posts
    101

    Default

    It is actually quite simple, you would configure it like:

    Code:
    <bean id="allTransactionManager" class="AllTransactionManager">
           <property name="transactionManagers">
              <map>
                <entry key="a" value-ref="aTransactionManager"/>
                <entry key="b" value-ref="bTransactionManager"/>
              </map>
        </property>
    </bean>
    
    <tx:annotation-driven transaction-manager="id="allTransactionManager"/>
    and include the posted code on the classpath. It works the same as if you had a single dataSource/transaction manager.

  5. #5
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Hello,

    I would be very glad to see your code.

    Thanks in advance,

    Regards,
    Oleksandr

    Quote Originally Posted by ccanning View Post
    Currently, you can't. You can vote on a Jira issue SPR-3844. In the meantime, I have created my own implementation which I am willing to share here if you want. It follows the basic premise set forth from discussions with Juergen. I would have submitted it, but the Spring people felt it was something they should build on their own. Post back here if you want me to provide you with the code. It is a little quirky because the Spring code goes against their own practices and casts to implementation code in their lower levels so you just can't extend all the underlying classes/interfaces.

  6. #6
    Join Date
    Feb 2008
    Posts
    8

    Default

    OK, I will take a look at it. Do you use the same @Transactional annotation?

  7. #7
    Join Date
    Jul 2007
    Posts
    101

    Default

    Yes, you use the same annotations. The only thing that you have to do is add the code to your project and add that little snippet of configuration. I will post the code Monday when I get back in the office.

  8. #8
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Thank you, would wait somewhat impatiently


    Regards,

    Oleksandr

    Quote Originally Posted by ccanning View Post
    Yes, you use the same annotations. The only thing that you have to do is add the code to your project and add that little snippet of configuration. I will post the code Monday when I get back in the office.

  9. #9
    Join Date
    Jul 2007
    Posts
    101

    Default

    Okay fine, I logged in over VPN. Due to the slow performance of my IDEA over nomachine, I didn't update the documentation for public comsumption, but it should be reasonably straight forward.

    Code:
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.TransactionException;
    import org.springframework.transaction.support.AbstractPlatformTransactionManager;
    
    import java.util.*;
    
    
    /**
     * The group transaction manager will manage a group of transactional resources as if they were managed by a single
     * transaction manager.
     * @author Charles Canning
     */
    public class GroupTransactionManager implements PlatformTransactionManager {
        /**
         * The map of transaction managers.
         */
        private Map<String, PlatformTransactionManager> transactionManagers;
    
    
        /**
         * @return the names of the supported transaction managers.
         */
        public Set<String> getTransactionKeys() {
            return transactionManagers.keySet();
        }
    
        public void setTransactionManagers(Map<String, PlatformTransactionManager> transactionManagers) {
            PlatformTransactionManager transactionManager;
    
            this.transactionManagers = transactionManagers;
            //turn off synchronization
            for (String transactionKey : getTransactionKeys()) {
                //get the transaction manager
                transactionManager = transactionManagers.get(transactionKey);
                turnOffSynchronization((AbstractPlatformTransactionManager)transactionManager);
            }
        }
    
        public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
            Map<String, TransactionStatus> transactions;
            PlatformTransactionManager transactionManager;
    
            //create the map of transactions
            transactions = new LinkedHashMap<String, TransactionStatus>();
            try {
                //step through each transaction manager and get the transaction
                for (String transactionKey : getTransactionKeys()) {
                    //get the transaction manager
                    transactionManager = transactionManagers.get(transactionKey);
                    //get the transaction
                    transactions.put(transactionKey, transactionManager.getTransaction(definition));
                }
            } catch (TransactionException transactionException) {
                //rollback any we have created
                rollback(new GroupTransactionStatus(transactions));
            }
            //create and return the group status
            return new GroupTransactionStatus(transactions);
        }
    
        public void commit(TransactionStatus transaction) throws TransactionException {
            commit((GroupTransactionStatus)transaction);
        }
    
        public void commit(GroupTransactionStatus groupTransaction) throws TransactionException {
            GroupTransactionException exception = null;
            PlatformTransactionManager transactionManager;
            TransactionStatus transaction;
    
            //step through each transaction manager and get the transaction
            for (String transactionKey : getTransactionKeys()) {
                //get the transaction manager
                transactionManager = transactionManagers.get(transactionKey);
                //get the transaction for this transaction manager
                transaction = groupTransaction.getTransaction(transactionKey);
                try {
                    //commit the transaction
                    transactionManager.commit(transaction);
                } catch (TransactionException transEx) {
                    //have we created the exception
                    if (exception == null) {
                        exception = new GroupTransactionException("Error commiting transactions");
                    }
                    //put the exception
                    exception.put(transactionKey, transEx);
                }
            }
            //do we need to throw an exception
            if (exception != null) {
                throw (TransactionException)exception.fillInStackTrace();
            }
        }
    
        public void rollback(TransactionStatus transaction) throws TransactionException {
            rollback((GroupTransactionStatus)transaction);
        }
    
        public void rollback(GroupTransactionStatus groupTransaction) throws TransactionException {
            GroupTransactionException exception = null;
            PlatformTransactionManager transactionManager;
            TransactionStatus transaction;
    
            //step through each transaction manager and get the transaction
            for (String transactionKey : getTransactionKeys()) {
                //get the transaction manager
                transactionManager = transactionManagers.get(transactionKey);
                //get the transaction for this transaction manager
                transaction = groupTransaction.getTransaction(transactionKey);
                try {
                    //commit the transaction
                    transactionManager.rollback(transaction);
                } catch (TransactionException transEx) {
                    //have we created the exception
                    if (exception == null) {
                        exception = new GroupTransactionException("Error rolling back transactions");
                    }
                    //put the exception
                    exception.put(transactionKey, transEx);
                }
            }
            //do we need to throw an exception
            if (exception != null) {
                throw (TransactionException)exception.fillInStackTrace();
            }
        }
    
        private void turnOffSynchronization(AbstractPlatformTransactionManager transactionManager) {
            transactionManager.setTransactionSynchronization(AbstractPlatformTransactionManager.SYNCHRONIZATION_NEVER);
        }
    }
    Due to 10000 character limit, rest will follow.

  10. #10
    Join Date
    Jul 2007
    Posts
    101

    Default

    Last two classes

    Code:
    import org.springframework.transaction.TransactionStatus;
    import org.springframework.transaction.TransactionException;
    
    import java.util.Map;
    import java.util.LinkedHashMap;
    import java.util.Collection;
    import java.util.Collections;
    
    /**
     * The group transaction status.
     *
     * @author Charles Canning
     */
    public class GroupTransactionStatus implements TransactionStatus {
        /**
         * The map of transaction statuses keyed by transaction manager name.
         */
        private Map<String, TransactionStatus> transactions;
        /**
         * The group save point.
         */
        private GroupSavepoint savepoint;
    
    
        public GroupTransactionStatus(Map<String, TransactionStatus> transactions) {
            this.transactions = transactions;
        }
    
        /**
         * This will determine if the transaction is new.
         *
         * @return true if new, false otherwise
         * @TODO should we make a check to see if they are out of sync? do we support this or not??
         */
        public boolean isNewTransaction() {
            boolean isNewTransaction = true;
    
            //step through the transactions and AND the is new flag
            for (TransactionStatus transaction : getTransactions()) {
                isNewTransaction &= transaction.isNewTransaction();
            }
    
            return isNewTransaction;
        }
    
        /**
         * This will set the rollback only flag for all transactions in the group.
         */
        public void setRollbackOnly() {
            //step through the transactions and set roolback only
            for (TransactionStatus transaction : getTransactions()) {
                transaction.setRollbackOnly();
            }
        }
    
        public boolean isRollbackOnly() {
            boolean isRollbackOnly = true;
    
            //step through the transactions and OR the is new flag
            for (TransactionStatus transaction : getTransactions()) {
                isRollbackOnly &= transaction.isRollbackOnly();
            }
    
            return isRollbackOnly;
        }
    
        public boolean isCompleted() {
            boolean isCompleted = true;
    
            //step through the transactions and OR the is new flag
            for (TransactionStatus transaction : getTransactions()) {
                isCompleted &= transaction.isCompleted();
            }
    
            return isCompleted;
        }
    
        public boolean hasSavepoint() {
            return savepoint != null;
        }
    
        public Object createSavepoint() throws TransactionException {
            Map<String, Object> savepoints;
            TransactionStatus transaction;
    
            //create the savepoints and add each to the map keyed by transaction
            savepoints = new LinkedHashMap<String, Object>();
            //step through the transaction keys
            for (String transactionKey : getTransactionKeys()) {
                //get the transaction
                transaction = transactions.get(transactionKey);
                //create the save point and add to map of savepoints
                savepoints.put(transactionKey, transaction.createSavepoint());
            }
    
            //create the savepoint and return it
            savepoint = new GroupSavepoint(savepoints);
    
            return savepoint;
        }
    
        public void rollbackToSavepoint(Object savepoint) throws TransactionException {
            rollbackToSavepoint((GroupSavepoint) savepoint);
        }
    
        public void rollbackToSavepoint(GroupSavepoint savepoint) throws TransactionException {
            TransactionStatus transaction;
            Object transactionSavepoint;
    
            //step through each transaction and rollback to its savepoint
            for (String transactionKey : getTransactionKeys()) {
                //get the transaction
                transaction = transactions.get(transactionKey);
                //get the transaction savepoint
                transactionSavepoint = savepoint.getTransactionSavepoint(transactionKey);
                //rollback the transaction to the save point
                transaction.rollbackToSavepoint(transactionSavepoint);
            }
        }
    
        public void releaseSavepoint(Object savepoint) throws TransactionException {
            releaseSavepoint((GroupSavepoint) savepoint);
        }
    
        public void releaseSavepoint(GroupSavepoint savepoint) throws TransactionException {
            TransactionStatus transaction;
            Object transactionSavepoint;
    
            //step through each transaction and rollback to its savepoint
            for (String transactionKey : getTransactionKeys()) {
                //get the transaction
                transaction = transactions.get(transactionKey);
                //get the transaction savepoint
                transactionSavepoint = savepoint.getTransactionSavepoint(transactionKey);
                //release the transaction to the save point
                transaction.releaseSavepoint(transactionSavepoint);
            }
        }
    
        /**
         * @return the collection of transaction keys
         */
        protected Collection<String> getTransactionKeys() {
            return transactions.keySet();
        }
    
        /**
         * This will get the specified transaction.
         * @param transactionKey - the transaction key
         * @return the collection of transaction statuses
         */
        protected TransactionStatus getTransaction(String transactionKey) {
            return transactions.get(transactionKey);
        }
    
        /**
         * @return the collection of transaction statuses
         */
        protected Collection<TransactionStatus> getTransactions() {
            return transactions.values();
        }
    
        /**
         * This is the group save point if save points are supported. The group savepoint is an immutable object.
         *
         * @author Charles Canning
         */
        class GroupSavepoint {
            /**
             * The map of save points keyed by transaction manager name.
             */
            private Map<String, Object> savepoints;
    
    
            /**
             * This will create the group savepoint with all the individual transaction savepoints.
             *
             * @param savepoints - the map of savepoints keyed by transaction manager
             */
            GroupSavepoint(Map<String, Object> savepoints) {
                this.savepoints = savepoints;
            }
    
            /**
             * @param transactionKey - the transaction key
             * @return the savepoint for the specified transaction
             */
            public Object getTransactionSavepoint(String transactionKey) {
                return savepoints.get(transactionKey);
            }
    
            /**
             * @return the unmodifiable collection of transaction keys.
             */
            public Collection<String> getTransactionKeys() {
                return Collections.unmodifiableSet(savepoints.keySet());
            }
    
            /**
             * @return the unmodifiable collection of savepoints.
             */
            public Collection<Object> getSavepoints() {
                return Collections.unmodifiableCollection(savepoints.values());
            }
        }
    }
    Code:
    import org.springframework.transaction.TransactionException;
    
    import java.util.Map;
    import java.util.LinkedHashMap;
    import java.util.Collection;
    
    /**
     * The group transaction exception will contain the exceptions for each transaction keyed by transaction key.
     * @author Charles Canning
     */
    public class GroupTransactionException extends TransactionException {
        /**
         * The map of exceptions.
         */
        private Map<String, Exception> exceptions;
    
    
        public GroupTransactionException(String msg) {
            super(msg);
            //create the map.
            exceptions = new LinkedHashMap<String, Exception>();
        }
    
        /**
         * This will put the exception keyed by transaction key.
         * @param transactionKey - the transaction key
         * @param exception - the exception that occurred with the named transaction
         */
        public void put(String transactionKey, Exception exception) {
            exceptions.put(transactionKey, exception);
        }
    
        public Exception get(String transactionKey) {
            return exceptions.get(transactionKey);
        }
    
        public int size() {
            return exceptions.size();
        }
    
        public Collection<String> getTransactionKeys() {
            return exceptions.keySet();
        }
    
        public Collection<Exception> getExceptions() {
            return exceptions.values();
        }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •