Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two or more PlatformTransactionManager cannot live together in one application [SPR-6237] #10905

Closed
spring-issuemaster opened this issue Oct 13, 2009 · 5 comments

Comments

Projects
None yet
1 participant
@spring-issuemaster
Copy link
Collaborator

commented Oct 13, 2009

Jara Cesnek opened SPR-6237 and commented

Two or more PlatformTransactionManager cannot live together in one application,
because of static reference to single TransactionSynchronizationManager.

We need in one thread access two database in the same time.
For example one HibernateTransactionManager a second DataSourceTransactionManager.

All seems to working, but in the transaction end

  1. HibernateTransactionManager invoke TransactionSynchronizationManager.clearSynchronization()
  2. DataSourceTransactionManager throw exception java.lang.IllegalStateException: Cannot deactivate transaction synchronization - not active

Affects: 2.5.6

2 votes, 7 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Oct 15, 2009

Jara Cesnek commented

Sorry for opening this issue.
It is maily my misunderstanding of transaction synchronization.

I trying implement simple but working BestEffort1PCTransactionManager
based on Dave Syer (http://www.javaworld.com/javaworld/jw-01-2009/jw-01-spring-transactions.html?page=6)
His version is not working.

This approach is also mentioned in #8524

Bellow is our fully functional "ChainedTransactionManager" working on more than one datasource and @Transactional

<bean id="transactionManager" class="cz.marbes.daisy.sysmodules.besteffort1pc.ChainedTransactionManager">
    <property name="transactionManagers">
        <list>
            <bean class="org.springframework.orm.hibernate3.HibernateTransactionManager">
                <property name="sessionFactory" ref="sessionFactory" />
            </bean>
            <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="histDataSource" />
            </bean>
        </list>
    </property>
</bean>
public class ChainedTransactionManager implements PlatformTransactionManager{

    protected Log logger = LogFactory.getLog(getClass());

    private List<PlatformTransactionManager> transactionManagers = new ArrayList<PlatformTransactionManager>();


    public void setTransactionManagers(List<PlatformTransactionManager> transactionManagers) {
	this.transactionManagers = transactionManagers;
    }



    @Override
    public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {

        MultiTransactionStatus mts = new MultiTransactionStatus(transactionManagers.get(0)/*First TM is main TM*/);

        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
            TransactionSynchronizationManager.initSynchronization();
            mts.setNewSynchonization();
        }

        for (PlatformTransactionManager transactionManager : transactionManagers) {
            mts.getTransactionStatuses().put(transactionManager, transactionManager.getTransaction(definition));
	}

        return mts;
    }

    @Override
    public void commit(TransactionStatus status) throws TransactionException {

        boolean commit = true;
        Exception commitException = null;
        PlatformTransactionManager commitExceptionTransactionManager = null;

        for (int i = transactionManagers.size()-1; i >= 0 ; i--) {
            PlatformTransactionManager transactionManager = transactionManagers.get(i);

            if (commit) {
                try {
                    transactionManager.commit(((MultiTransactionStatus) status).getTransactionStatuses().get(transactionManager));
                } catch (Exception ex) {
                    commit = false;
                    commitException = ex;
                    commitExceptionTransactionManager = transactionManager;
                }
            } else {
                //after unsucessfull commitu we must try to rollback rest of datasouces
                try {
                    transactionManager.rollback(((MultiTransactionStatus) status).getTransactionStatuses().get(transactionManager));
                } catch (Exception ex) {
                    logger.warn("Rollback exception (after commit) (" + transactionManager + ") " + ex.getMessage(), ex);
                }
            }
        }

        if (((MultiTransactionStatus)status).isNewSynchonization()){
            TransactionSynchronizationManager.clear();
        }

        if (commitException != null) {
           throw new FrameworkRTException("Commit exception ("+commitExceptionTransactionManager+") "+
              commitException.getMessage(), commitException);
        }

    }

    @Override
    public void rollback(TransactionStatus status) throws TransactionException {

        Exception rollbackException = null;
        PlatformTransactionManager rollbackExceptionTransactionManager = null;


        for (int i = transactionManagers.size()-1; i >= 0 ; i--) {
             PlatformTransactionManager transactionManager = transactionManagers.get(i);

            //bez ohledu na exception se musim pokusit rollbacknout vsechny datasource
            try {
                transactionManager.rollback(((MultiTransactionStatus) status).getTransactionStatuses().get(transactionManager));
            } catch (Exception ex) {
                if (rollbackException == null) {
                    rollbackException = ex;
                    rollbackExceptionTransactionManager = transactionManager;
                } else {
                    logger.warn("Rollback exception (" + transactionManager + ") " + ex.getMessage(), ex);
                }
            }
        }

        if (((MultiTransactionStatus)status).isNewSynchonization()){
            TransactionSynchronizationManager.clear();
        }

        if (rollbackException != null) {
            throw new FrameworkRTException("Rollback exception ("+rollbackExceptionTransactionManager+") "+
              rollbackException.getMessage(), rollbackException);
        }
    }

}
public class MultiTransactionStatus implements TransactionStatus {


    private PlatformTransactionManager mainTransactionManager;

    private Map<PlatformTransactionManager, TransactionStatus> transactionStatuses = 
             Collections.synchronizedMap(new HashMap<PlatformTransactionManager,TransactionStatus>());

    private boolean newSynchonization;

    public Map<PlatformTransactionManager, TransactionStatus> getTransactionStatuses() {
        return transactionStatuses;
    }


    public MultiTransactionStatus(PlatformTransactionManager mainTransactionManager) {
        this.mainTransactionManager = mainTransactionManager;
    }

    private TransactionStatus getHlavniTM() {
        return transactionStatuses.get(mainTransactionManager);
    }


    public void setNewSynchonization() {
        this.newSynchonization = true;
    }

    public boolean isNewSynchonization() {
        return newSynchonization;
    }


    @Override
    public boolean isNewTransaction() {
        return getHlavniTM().isNewTransaction();
    }

    @Override
    public boolean hasSavepoint() {
        return getHlavniTM().hasSavepoint();
    }

    @Override
    public void setRollbackOnly() {
        for(TransactionStatus ts : transactionStatuses.values() ){
            ts.setRollbackOnly();
        }
    }

    @Override
    public boolean isRollbackOnly() {
        return getHlavniTM().isRollbackOnly();
    }

    @Override
    public boolean isCompleted() {
        return getHlavniTM().isCompleted();
    }

    @Override
    public Object createSavepoint() throws TransactionException {
        throw new FrameworkRTException("Savepoint not supported");
        //return getHlavniTM().createSavepoint();
    }

    @Override
    public void rollbackToSavepoint(Object savepoint) throws TransactionException {
        for(TransactionStatus ts : transactionStatuses.values() ){
            ts.rollbackToSavepoint(savepoint);
        }
    }

    @Override
    public void releaseSavepoint(Object savepoint) throws TransactionException {
        for(TransactionStatus ts : transactionStatuses.values() ){
            ts.releaseSavepoint(savepoint);
        }
    }
}
@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 13, 2010

Min Cha commented

I wonder why this patch is being not accepted. There is any reason? Defect?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jun 18, 2012

Rossen Stoyanchev commented

This issue has been resolved through a selective bulk update, as part of a larger effort to better manage unresolved issues. To qualify for the update, the issue was either created before Spring 3.0 or affects a version older than Spring 3.0 and is not a bug.

There is a good chance the request was made obsolete, or at least partly outdated, by changes in later versions of Spring including deprecations. It is also possible it didn't get enough traction or we didn't have enough time to address it. One way or another, we didn't get to it.

If you believe the issue, or some aspects of it, are still relevant and worth pursuing at present you may re-open this issue or create a new one with a more up-to-date description.

We thank you for your contributions and encourage you to become familiar with the current process of managing Spring Framework JIRA issues that has been in use for over a year.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Apr 3, 2013

Giovanni Botta commented

Isn't this patch a good candidate to resolve this issue: https://jira.springsource.org/browse/SPR-3844 ?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 9, 2015

Amey Jadiye commented

Oliver Drotbohm no one gave any clear explaination here that why this ticket is resolved without accepting the patch? i see the resolution present in spring-data
so using this as a resolution ChainedTransactionManager.java . thanks for your efforts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.