Откат с помощью диспетчера транзакций Spring - PullRequest
0 голосов
/ 06 декабря 2010

Я использую класс TaskExecutor Springs для выполнения асинхронного удаления нескольких записей в базе данных. Поэтому в основном метод execute базового класса запускается как поток для выполнения асинхронного удаления.

В этом методеЯ вызываю bean-методы, которые выполняют удаление записей базы данных.Я использую атрибут Transaction по умолчанию, который PROPAGATION.REQUIRED в методах bean. Подразумевается метод execute, о котором я говорю. В основном мне нужно откатить всю транзакцию базы данных, как только произойдет любое исключение после методов callig bean или если кто-то отменит задачу удаления.

 package com.ibm.security.modeling.async.tasks;

import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;

import com.ibm.security.modeling.async.AsynchronousTask;
import com.ibm.security.modeling.async.AsynchronousTaskType;
import com.ibm.security.modeling.data.analysis.ModelingManager;
import com.ibm.security.modeling.data.analysis.ProjectManager;
import com.ibm.security.modeling.data.entity.Model;
import com.ibm.security.modeling.data.entity.ModelStatus;
import com.ibm.security.modeling.data.entity.Project;
import com.ibm.security.modeling.i18n.msgs.LogMessages;


public class DeleteProjectTask extends AbstractDeletionTask implements AsynchronousTask
{
    private static final String CLASS_NAME = DeleteProjectTask.class.getName();
    private static final Logger LOG = Logger.getLogger(CLASS_NAME);


    private String projectName;
    private Collection<Model> modelCol;
    private ProjectManager pMgr = null;
    private long projectId = 0L;
    private Project project=null;
    private PlatformTransactionManager txManager;

    public DeleteProjectTask(ProjectManager pMgr, ModelingManager mMgr, long pid)
    {
     super(pMgr.getProject(pid).getName(), mMgr);
     this.project =pMgr.getProject(pid);
     this.projectName = project.getName();
        this.pMgr = pMgr;
        this.projectId = pid;
        this.modelCol=project.getModels();
        this.txManager=(PlatformTransactionManager)SpringUtils.getFactory().getBean("txManager");
    }

    public AsynchronousTaskType getTaskType()
    {
        return AsynchronousTaskType.PROJECT_DELETION;
    }


    public void execute()
    {   
     DefaultTransactionDefinition def = new DefaultTransactionDefinition();
     def.setName("SomeTxName");
     def.setPropagationBehavior(TransactionDefinition.PROPAGATION_NESTED);
     TransactionStatus status = txManager.getTransaction(def);
     System.out.println(status.isRollbackOnly());
     boolean success = false;
        try
        {            
         //recordMessage("execute", LogMessages.PROJECT_DELETE_UNDERGOING, new String[]{projectName},1);
            for (Model mo:this.modelCol){
    checkForCancellation();
    //System.out.println("hello");
    //recordMessage("execute", LogMessages.PROJECT_DELETE_UNDERGOING, new String[]{projectName},mo.getId());
    //System.out.println("hello");
    pMgr.deleteModel(projectId,mo.getId());
    System.out.println("66666666666666666666");
            }
            checkForCancellation();
            System.out.println("%%%%%%%%%%%%%%%%%%%%%%");
           // pMgr.deleteModel(, modelId)
            pMgr.deleteProject(projectId);
            System.out.println("$$$$$$$$$$$$$$$$");
           // recordMessage("execute", LogMessages.PROJECT_DELETE_COMPLETE_SUCCESS, 
            //        new String[]{projectName},1L);
            success = true;
            //throw new Exception();
        }
        catch (TaskCanceledException e)
        {
            //Informational message that project creation was canceled

            //recordMessage("execute", LogMessages.PROJECT_DELETE_CANCELED, new String[]{projectName},0L);   
        }
        catch (Throwable t)
        {
            LOG.log(Level.INFO, "caught throwable while deleting project " + projectId, t);
            if (t instanceof IncorrectResultSizeDataAccessException)
            {
                // runtime exception indicating that the project could not be located
                // during the status update or copy process
               // recordErrorMessage("execute", LogMessages.PROJECT_DELETE_FAILED_INTEGRITY, new String[]{projectName},0L);                
            }
            else
            {
                // some other unexpected error occurred - log error
               // recordErrorMessage("execute", LogMessages.PROJECT_DELETE_FAILED_UNKNOWN, new String[]{projectName},0L);                
            }
        }
        finally
        {
            //if not successful, attempt to mark the project as failed
            if (!success)
            {
               System.out.println("i am here4:"+success);
               txManager.rollback(status);
               System.out.println("am");
            }else{
               System.out.println("i am here3:"+success);
               txManager.commit(status);
            }
        }
    }


}

Но все работает не так, как ожидалось. Фактически результат каждый раз также не согласован, т. Е. Иногда код застревает в методах компонента. Я не могу понять, что происходит ..кто-то, пожалуйста, помогите

1 Ответ

3 голосов
/ 06 декабря 2010

Вы должны понимать, что происходит на уровне БД. Прежде всего, поскольку вы запускаете новую тему, вы должны запросить у Spring новую транзакцию (т.е. TransactionDefinition.PROPAGATION_REQUIRES_NEW вместо TransactionDefinition.PROPAGATION_NESTED).

Это означает, что вы должны снова извлечь все компоненты из базы данных (поскольку вы читаете их с первой транзакцией, которая больше не действительна). Это не так дорого, как кажется, так как ваш кеш все равно будет содержать их. Просто загрузите их снова, используя идентификатор компонента, который вы получили из внешней транзакции. Обычно рекомендуется собрать все идентификаторы, закрыть внешнюю транзакцию (чтобы избавиться от любых блокировок), а затем запустить внутренний поток.

Почему новая транзакция? Потому что другой поток (который запустил поток удаления) в конечном итоге завершит и закроет внешнюю транзакцию. Это вне контроля потока удаления, поэтому это может произойти в любое время.

Если удаляемый поток зависает, то другой поток блокирует один из компонентов, которые вы пытаетесь удалить. Включите ведение журнала SQL для вашей платформы ORM, чтобы увидеть, какие блоки SQL. Это должно дать вам представление, где искать.

...