entity bean 必须CMT 容器管理事务
session bean 和message bean  可以CMT 也可以BMT
你可以实现UserTransaction接口 来手动事务管理 
你可以自己设计 什么时候提交 什么时候回滚

解决方案 »

  1.   

    仅供参考!TransactionManager.java 
    package org.mockejb;
    import java.io.Serializable;
    import java.lang.reflect.Method;
    import javax.naming.*;
    import javax.transaction.*;
    import javax.ejb.*;
    import org.apache.commons.logging.*;
    import org.mockejb.interceptor.*;
    /**
     * Provides the support for the container-managed transactions 
     * according to EJB spec (chapter 18).
     * <p>
     * Note that RequiredNew is not fully supported since <code>TransactionManager</code>
     * does not know how to suspend transactions. 
     * <p>
     * Transaction policy must be provided in the invocationContext in the "transactionPolicy" field.
     * If it is not provided the Supprts policy is used.
     * @author Alexander Ananiev
     */
    public class TransactionManager implements Interceptor, Serializable {    // logger for this class
        private static Log logger = LogFactory.getLog( TransactionManager.class.getName() );
      

  2.   

    public final static 
            String USER_TRANSACTION_JNDI="javax.transaction.UserTransaction";    public final static 
            String POLICY_CONTEXT_KEY="transactionPolicy";
        
        /**
         * "Cached" instance of the UserTransaction 
         */
        private static UserTransaction sharedUserTransaction;    private TransactionPolicy policy = TransactionPolicy.SUPPORTS;    /**
         * Creates a new instance of the <code>TransactionManager</code> with the  
         * given policy.
         * @param policy transaction policy
         */
        public TransactionManager( TransactionPolicy policy ){
            super();
            setPolicy( policy );
        }
        
        /**
         * Creates a new instance of the <code>TransactionManager</code> with the
         * default (Supports) policy.
         */
        public TransactionManager( ){
            super();
        }    /**
         * Returns the currently set transaction policy.    
         * @return transaction policy
         */    
        public TransactionPolicy getPolicy() {
            return policy;    
        }
        
        /**
         * Sets the transaction policy.
         * @param policy policy to set.
         */
        
        public void setPolicy( TransactionPolicy policy ) {        this.policy = policy;
        }    /**
         * Begins, commits and rolls back the transaction according to the currently
         * set policy and EJB spec.
         */
        public void intercept( InvocationContext invocationContext ) throws Exception {
            
            
            UserTransaction newTran = null;
            
            TransactionPolicy thisCallPolicy = 
                (TransactionPolicy) invocationContext.getOptionalPropertyValue( POLICY_CONTEXT_KEY );        if ( thisCallPolicy == null)
                thisCallPolicy = this.policy;
            Method method = invocationContext.getTargetMethod();
            if ( handlePolicy( thisCallPolicy, invocationContext.getTargetObject(), 
                    method, invocationContext.getParamVals() ) ) {
            
                newTran = getUserTransaction();
                log( method, "Begin transaction" );
                newTran.begin();
                            
            }
                
            try {
                invocationContext.proceed();
                
                commitOrRollback( newTran );
                
            }        /* Here we try to follow the guidelines of chapter 18 of EJB spec
             * with the exception of javax.transaction.TransactionRolledback
             * Note that ExceptionHandler already provides logging and wrapping services 
             * so we don't need to do it here
             */ 
            catch( Exception exception ) {
                
                if ( MockContainer.isSystemException( exception )) {   
                    // If this transaction was started immediately before this invocation
                    if ( newTran != null && ( newTran.getStatus()==Status.STATUS_ACTIVE ||
                        newTran.getStatus()==Status.STATUS_MARKED_ROLLBACK ) ) { 
                        newTran.rollback();
                        log( method, "Rollback because of system exception" );                }
                }
                // if it is application exception
                // we should rollback/commit only if it was new transaction context
                else { 
                    // TODO: according to the spec we must ignore the exceptions from the 
                    // rollback if we try to rollback in the catch block of the business 
                    // exception. We should re-throw business exception
                    try {
                        commitOrRollback( newTran );
                    }
                    catch ( RollbackException rollbackEx ) {
                        logger.error(
                            "There has been rollback exception trying to rollback the transaction set for rollback. Ignoring. ", rollbackEx);
                    }
                }
                    
                throw exception;
            }
                
        }    private void commitOrRollback( UserTransaction tran ) throws
            SystemException, RollbackException, HeuristicMixedException,
            HeuristicRollbackException {
            
            if ( tran != null ) {
            
                if ( tran.getStatus()==Status.STATUS_ACTIVE ) {
                    tran.commit();
                    log( "Committing transaction" );
                }
                else if ( tran.getStatus()==Status.STATUS_MARKED_ROLLBACK ) {
                    tran.rollback();
                    log( "Rollling back transaction" );
                }
            }        
        }
        
        /**
         * Performs the actions necessary to handle the transaction policy 
         * according to the spec. 
         * Determines whether the new transaction has to begin for the given method.
         * @param policy policy of this invoker
         * @param targetObj bean being called  
         * @param method method being called
         * @param args parameter values of the method being called
         * @return true if the new transaction must begin for the given method
         */
        // TODO: We use TransactionRequiredLocalException even for remote EJBs 
        protected boolean handlePolicy( TransactionPolicy policy, Object targetObj, 
            Method method, Object[] args) throws SystemException, NamingException  {
            
            boolean newTranRequired = false;
            
            if ( policy == TransactionPolicy.REQUIRED ){            UserTransaction tran = getUserTransaction();
                newTranRequired=( tran == null || 
                    tran.getStatus()==Status.STATUS_NO_TRANSACTION ||
                    tran.getStatus()==Status.STATUS_COMMITTED ||            
                    tran.getStatus()==Status.STATUS_ROLLEDBACK ||
                    tran.getStatus()==Status.STATUS_UNKNOWN );
            }
            else if ( policy == TransactionPolicy.REQUIRED_NEW ){
                newTranRequired = true;        
            }
            else if ( policy == TransactionPolicy.MANDATORY ){            UserTransaction tran = getUserTransaction();
                
                if ( tran==null ||
                    tran.getStatus()==Status.STATUS_NO_TRANSACTION ||
                    tran.getStatus()==Status.STATUS_COMMITTED ||            
                    tran.getStatus()==Status.STATUS_ROLLEDBACK ||
                    tran.getStatus()==Status.STATUS_UNKNOWN ) {
                    
                    throw new TransactionRequiredLocalException(
                        "Attempt to invoke method with Mandatory policy without transaction context");
                }
            }
            else if ( policy == TransactionPolicy.NEVER ){            UserTransaction tran = getUserTransaction();
                if ( tran != null ||
                    tran.getStatus()==Status.STATUS_ACTIVE ) {
                    throw new EJBException(
                        "Attempt to invoke method with Never policy inside transaction context");
                }            
            }
            // For Supports we don't need to do anything
            // NotSupported is the same deal since we can't suspend the transaction            
                
            return newTranRequired;        
        }
        
      

  3.   

    /**
         * Returns UserTransaction object. If <code>setUserTransaction()</code>
         * was called, will return the object that was set by this method.
         * Otherwise, tries to obtain UserTransaction object from JNDI. 
         * 
         * @return UserTransaction object
         */
        public static UserTransaction getUserTransaction() {
            
            UserTransaction userTransaction = null;
            
            if ( sharedUserTransaction != null ) {
                userTransaction = sharedUserTransaction;
            }
            else {
                // obtain tran from the JNDI                         try {                           Context context = new InitialContext();
                    userTransaction = (UserTransaction) context.lookup( USER_TRANSACTION_JNDI );
                }
                catch ( NamingException namingEx ){
                    throw new MockEjbSystemException(
                        "Errors while trying to obtain javax.transaction.UserTransaction from JNDI", namingEx );
                }
            }        
            
            return userTransaction;        
        }    /**
         * Sets the shared instance of UserTransaction that will be used by MockEJB.
         * This is convenient when the remote JNDI is used and the cost of obtaining 
         * UserTransaction object from JNDI every time is too high. TransactionManager tries to 
         * get UserTransaction for every EJB method call. 
         *
         */
        public void setUserTransaction( UserTransaction userTransaction ) {
            sharedUserTransaction = userTransaction;
            
        }
        private void log( Method method, String message ){        log( message+" for \n"+method );
        }
        
        protected void log( String message ){
            logger.debug(message);
        }
        
        /**
         * Returns true if the given object is of the same type and 
         * it has the same transaction policy.
         */
        public boolean equals( Object obj ){
            if ( ! (obj instanceof ClassPatternPointcut) )
                return false;
            
            TransactionManager transactionManager = (TransactionManager) obj;
            
            return ( this.policy == transactionManager.policy );
        }    public int hashCode() {
            return  policy.hashCode(); 
        }    
        }