org.activiti.impl.persistence.IbatisPersistenceSession.java Source code

Java tutorial

Introduction

Here is the source code for org.activiti.impl.persistence.IbatisPersistenceSession.java

Source

/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.activiti.impl.persistence;

import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.activiti.ActivitiException;
import org.activiti.ActivitiOptimisticLockingException;
import org.activiti.Job;
import org.activiti.Page;
import org.activiti.ProcessInstance;
import org.activiti.SortOrder;
import org.activiti.TableMetaData;
import org.activiti.TablePage;
import org.activiti.Task;
import org.activiti.impl.bytes.ByteArrayImpl;
import org.activiti.impl.db.DbidBlock;
import org.activiti.impl.db.IdGenerator;
import org.activiti.impl.db.PropertyImpl;
import org.activiti.impl.db.execution.DbExecutionImpl;
import org.activiti.impl.definition.ProcessDefinitionImpl;
import org.activiti.impl.execution.ExecutionImpl;
import org.activiti.impl.identity.GroupImpl;
import org.activiti.impl.identity.UserImpl;
import org.activiti.impl.job.JobImpl;
import org.activiti.impl.job.MessageImpl;
import org.activiti.impl.job.TimerImpl;
import org.activiti.impl.repository.DeploymentImpl;
import org.activiti.impl.task.TaskImpl;
import org.activiti.impl.task.TaskInvolvement;
import org.activiti.impl.time.Clock;
import org.activiti.impl.variable.DeserializedObject;
import org.activiti.impl.variable.VariableInstance;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;

/**
 * @author Joram Barrez
 * @author Tom Baeyens
 */
public class IbatisPersistenceSession implements PersistenceSession {

    private static Logger log = Logger.getLogger(IbatisPersistenceSession.class.getName());

    protected SqlSession sqlSession;
    protected long blockSize = 100;
    protected IdGenerator idGenerator;
    protected Map<String, String> databaseStatements;

    protected Inserted inserted = new Inserted(this);
    protected Loaded loaded = new Loaded(this);
    protected Deleted deleted = new Deleted(this);
    protected List<DeserializedObject> deserializedObjects = new ArrayList<DeserializedObject>();

    protected static String[] tableNames = new String[] { "ACT_PROPERTY", "ACT_BYTEARRAY", "ACT_DEPLOYMENT",
            "ACT_EXECUTION", "ACT_ID_GROUP", "ACT_ID_MEMBERSHIP", "ACT_ID_USER", "ACT_JOB", "ACT_PROCESSDEFINITION",
            "ACT_TASK", "ACT_TASKINVOLVEMENT", "ACT_VARIABLE" };

    public IbatisPersistenceSession(SqlSession sqlSession, IdGenerator idGenerator,
            Map<String, String> databaseStatements) {
        this.sqlSession = sqlSession;
        this.idGenerator = idGenerator;
        this.databaseStatements = databaseStatements;
    }

    public void insert(PersistentObject persistentObject) {
        String id = String.valueOf(idGenerator.getNextDbid());
        persistentObject.setId(id);
        inserted.add(persistentObject);
    }

    public void delete(PersistentObject persistentObject) {
        deleted.add(persistentObject);
        inserted.remove(persistentObject);
        loaded.remove(persistentObject);
    }

    public void flush() {
        List<PersistentObject> updatedObjects = loaded.getUpdatedObjects();
        log.fine("flushing...");
        for (PersistentObject insertedObject : inserted.getInsertedObjects()) {
            log.fine("flush insert " + PersistentObjectId.toString(insertedObject));
        }
        for (PersistentObject updatedObject : updatedObjects) {
            log.fine("flush update " + PersistentObjectId.toString(updatedObject));
        }
        for (PersistentObject deletedObject : deleted.getDeletedObjects()) {
            log.fine("flush delete " + PersistentObjectId.toString(deletedObject));
        }

        for (DeserializedObject deserializedObject : deserializedObjects) {
            deserializedObject.flush();
        }

        inserted.flush(sqlSession);
        loaded.flush(sqlSession, updatedObjects);
        deleted.flush(sqlSession);
    }

    public void close() {
        sqlSession.close();
    }

    public void commit() {
        sqlSession.commit();
    }

    public void rollback() {
        sqlSession.rollback();
    }

    public String statement(String statement) {
        return databaseStatements.get(statement);
    }

    // executions ///////////////////////////////////////////////////////////////

    public DbExecutionImpl findExecution(String executionId) {
        // TODO check if this execution was already loaded
        DbExecutionImpl execution = (DbExecutionImpl) sqlSession.selectOne(statement("selectExecution"),
                executionId);

        if (execution != null) {
            execution = (DbExecutionImpl) loaded.add(execution);
        }
        return execution;
    }

    @SuppressWarnings("unchecked")
    public List<DbExecutionImpl> findRootExecutionsByProcessDefintion(String processDefinitionId) {
        List executions = sqlSession.selectList(statement("selectRootExecutionsForProcessDefinition"),
                processDefinitionId);
        return loaded.add(executions);
    }

    @SuppressWarnings("unchecked")
    public List<ExecutionImpl> findChildExecutions(String parentExecutionid) {
        List executions = sqlSession.selectList(statement("selectChildExecutions"), parentExecutionid);
        return loaded.add(executions);
    }

    public void deleteExecution(String executionId) {
        ExecutionImpl execution = findExecution(executionId);
        execution.end(); // TODO replace with real delete instead of end(), since this will create history traces
    }

    public long findProcessInstanceCountByDynamicCriteria(Map<String, Object> params) {
        return (Long) sqlSession.selectOne(statement("selectProcessInstanceCountByDynamicCriteria"), params);
    }

    @SuppressWarnings("unchecked")
    public List<ProcessInstance> findProcessInstancesByDynamicCriteria(Map<String, Object> params) {
        return sqlSession.selectList(statement("selectProcessInstanceByDynamicCriteria"), params);
    }

    // variables ////////////////////////////////////////////////////////////////

    public List<VariableInstance> findVariablesByExecutionId(String executionId) {
        List variablesInstances = sqlSession.selectList(statement("selectVariablesByExecutionId"), executionId);
        loaded.add(variablesInstances);
        return variablesInstances;
    }

    public List<VariableInstance> findVariablesByTaskId(String taskId) {
        List variableInstances = sqlSession.selectList(statement("selectVariablesByTaskId"), taskId);
        loaded.add(variableInstances);
        return variableInstances;
    }

    // tasks ////////////////////////////////////////////////////////////////////

    public TaskImpl findTask(String id) {
        TaskImpl task = (TaskImpl) sqlSession.selectOne(statement("selectTask"), id);
        if (task != null) {
            task = (TaskImpl) loaded.add(task);
        }
        return task;
    }

    @SuppressWarnings("unchecked")
    public List<TaskInvolvement> findTaskInvolvementsByTask(String taskId) {
        List taskInvolvements = sqlSession.selectList(statement("selectTaskInvolvementsByTask"), taskId);
        return loaded.add(taskInvolvements);
    }

    @SuppressWarnings("unchecked")
    public List<TaskImpl> findTasksByExecution(String executionId) {
        List tasks = sqlSession.selectList(statement("selectTaskByExecution"), executionId);
        return loaded.add(tasks);
    }

    // finders for static deployment and process definition information /////////

    @SuppressWarnings("unchecked")
    public List<DeploymentImpl> findDeployments() {
        return (List<DeploymentImpl>) sqlSession.selectList(statement("selectDeployments"));
    };

    public DeploymentImpl findDeployment(String deploymentId) {
        return (DeploymentImpl) sqlSession.selectOne(statement("selectDeployment"), deploymentId);
    };

    public DeploymentImpl findDeploymentByProcessDefinitionId(String processDefinitionId) {
        return (DeploymentImpl) sqlSession.selectOne(statement("selectDeploymentByProcessDefinitionId"),
                processDefinitionId);
    }

    @SuppressWarnings("unchecked")
    public List<ByteArrayImpl> findDeploymentResources(String deploymentId) {
        return sqlSession.selectList(statement("selectByteArraysForDeployment"), deploymentId);
    }

    @SuppressWarnings("unchecked")
    public List<String> findDeploymentResourceNames(String deploymentId) {
        return sqlSession.selectList(statement("selectResourceNamesForDeployment"), deploymentId);
    }

    public ByteArrayImpl findDeploymentResource(String deploymentId, String resourceName) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("deploymentId", deploymentId);
        params.put("resourceName", resourceName);
        return (ByteArrayImpl) sqlSession.selectOne(statement("selectDeploymentResource"), params);
    }

    @SuppressWarnings("unchecked")
    public byte[] getByteArrayBytes(String byteArrayId) {
        Map<String, Object> temp = (Map) sqlSession.selectOne(statement("selectBytesOfByteArray"), byteArrayId);
        return (byte[]) temp.get("BYTES_");
    }

    public ByteArrayImpl findByteArrayById(String byteArrayId) {
        ByteArrayImpl byteArray = (ByteArrayImpl) sqlSession.selectOne(statement("selectByteArrayById"),
                byteArrayId);
        loaded.add(byteArray);
        return byteArray;
    }

    public ProcessDefinitionImpl findProcessDefinitionById(String processDefinitionId) {
        return (ProcessDefinitionImpl) sqlSession.selectOne(statement("selectProcessDefinitionById"),
                processDefinitionId);
    }

    public ProcessDefinitionImpl findLatestProcessDefinitionByKey(String processDefinitionKey) {
        return (ProcessDefinitionImpl) sqlSession.selectOne(statement("selectLatestProcessDefinitionByKey"),
                processDefinitionKey);
    }

    @SuppressWarnings("unchecked")
    public List<ProcessDefinitionImpl> findProcessDefinitions() {
        return sqlSession.selectList(statement("selectProcessDefinitions"));
    }

    @SuppressWarnings("unchecked")
    public List<ProcessDefinitionImpl> findProcessDefinitionsByDeployment(String deploymentId) {
        List<ProcessDefinitionImpl> processDefinitions = new ArrayList<ProcessDefinitionImpl>();
        List<String> ids = sqlSession.selectList(statement("selectProcessDefinitionIdsByDeployment"), deploymentId);
        for (String id : ids) {
            processDefinitions.add(findProcessDefinitionById(id));
        }
        return processDefinitions;
    }

    public ProcessDefinitionImpl findProcessDefinitionByDeploymentAndKey(String deploymentId,
            String processDefinitionKey) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("deploymentId", deploymentId);
        parameters.put("processDefinitionKey", processDefinitionKey);
        return (ProcessDefinitionImpl) sqlSession.selectOne(statement("selectProcessDefinitionByDeploymentAndKey"),
                parameters);
    }

    // job /////////////////////////////////////////////////////////////////////

    public JobImpl findJobById(String jobId) {
        JobImpl job = (JobImpl) sqlSession.selectOne(statement("selectJob"), jobId);
        if (job != null) {
            loaded.add(job);
        }
        return job;
    }

    public List<JobImpl> findJobs() {
        return sqlSession.selectList(statement("selectJobs"));
    }

    public List<JobImpl> findNextJobsToExecute(int maxJobsPerAcquisition) {
        Date now = Clock.getCurrentTime();
        RowBounds rowBounds = new RowBounds(0, maxJobsPerAcquisition);
        List<JobImpl> jobs = sqlSession.selectList(statement("selectNextJobsToExecute"), now, rowBounds);
        if (jobs != null) {
            loaded.add(jobs);
        }
        return jobs;
    }

    public TimerImpl findFirstTimer() {
        return (TimerImpl) sqlSession.selectOne(statement("selectFirstTimer"));
    }

    public List<TimerImpl> findTimersByExecutionId(String executionId) {
        return sqlSession.selectList(statement("selectTimersByExecutionId"), executionId);
    }

    @SuppressWarnings("unchecked")
    public List<Job> dynamicFindJobs(Map<String, Object> params, Page page) {
        final String query = "org.activiti.persistence.selectJobByDynamicCriteria";
        if (page == null) {
            return sqlSession.selectList(query, params);
        } else {
            return sqlSession.selectList(query, params, new RowBounds(page.getOffset(), page.getMaxResults()));
        }
    }

    public long dynamicJobCount(Map<String, Object> params) {
        return (Long) sqlSession.selectOne("org.activiti.persistence.selectJobCountByDynamicCriteria", params);
    }

    // user /////////////////////////////////////////////////////////////////////

    public void saveUser(UserImpl user) {
        if (user.isNew()) {
            sqlSession.insert(statement("insertUser"), user);
        } else {
            sqlSession.update(statement("updateUser"), user);
        }
    }

    public UserImpl findUser(String userId) {
        return (UserImpl) sqlSession.selectOne(statement("selectUser"), userId);
    }

    public List<UserImpl> findUsersByGroup(String groupId) {
        return sqlSession.selectList(statement("selectUsersByGroup"), groupId);
    }

    public List<UserImpl> findUsers() {
        return sqlSession.selectList(statement("selectUsers"));
    }

    public boolean isValidUser(String userId) {
        return findUser(userId) != null;
    }

    public void deleteUser(String userId) {
        sqlSession.delete(statement("deleteMembershipsForUser"), userId);
        sqlSession.delete(statement("deleteUser"), userId);
    }

    public void saveGroup(GroupImpl group) {
        if (group.isNew()) {
            sqlSession.insert(statement("insertGroup"), group);
        } else {
            sqlSession.update(statement("updateGroup"), group);
        }
    }

    public GroupImpl findGroup(String groupId) {
        return (GroupImpl) sqlSession.selectOne(statement("selectGroup"), groupId);
    }

    public List<GroupImpl> findGroupsByUser(String userId) {
        return sqlSession.selectList(statement("selectGroupsByUser"), userId);
    }

    public List<GroupImpl> findGroupsByUserAndType(String userId, String groupType) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("userId", userId);
        parameters.put("groupType", groupType);
        return sqlSession.selectList(statement("selectGroupsByUserAndType"), parameters);
    }

    public List<GroupImpl> findGroups() {
        return sqlSession.selectList(statement("selectGroups"));
    }

    public void deleteGroup(String groupId) {
        sqlSession.delete(statement("deleteMembershipsForGroup"), groupId);
        sqlSession.delete(statement("deleteGroup"), groupId);
    }

    public void createMembership(String userId, String groupId) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("userId", userId);
        parameters.put("groupId", groupId);
        sqlSession.insert(statement("insertMembership"), parameters);
    }

    public void deleteMembership(String userId, String groupId) {
        Map<String, Object> parameters = new HashMap<String, Object>();
        parameters.put("userId", userId);
        parameters.put("groupId", groupId);
        sqlSession.delete(statement("deleteMembership"), parameters);
    }

    @SuppressWarnings("unchecked")
    public List<Task> findCandidateTasks(String userId, List<String> groupIds) {
        Map<String, Object> params = new HashMap<String, Object>();
        params.put("userId", userId);
        params.put("groupIds", groupIds);
        List tasks = (List) sqlSession.selectList(statement("selectCandidateTasks"), params);
        return loaded.add(tasks);
    }

    @SuppressWarnings("unchecked")
    public List<Task> findTasksByAssignee(String assignee) {
        return sqlSession.selectList(statement("selectTasksByAssignee"), assignee);
    }

    @SuppressWarnings("unchecked")
    public List<Task> dynamicFindTasks(Map<String, Object> params, Page page) {
        final String query = statement("selectTaskByDynamicCriteria");
        if (page == null) {
            return sqlSession.selectList(query, params);
        } else {
            return sqlSession.selectList(query, params, new RowBounds(page.getOffset(), page.getMaxResults()));
        }
    }

    public long dynamicFindTaskCount(Map<String, Object> params) {
        return (Long) sqlSession.selectOne(statement("selectTaskCountByDynamicCriteria"), params);
    }

    /*
     * INSERT operations
     */
    public void insertDeployment(DeploymentImpl deployment) {
        deployment.setId(String.valueOf(idGenerator.getNextDbid()));
        sqlSession.insert(statement("insertDeployment"), deployment);
        for (ByteArrayImpl resource : deployment.getResources().values()) {
            resource.setId(String.valueOf(idGenerator.getNextDbid()));
            resource.setDeploymentId(deployment.getId());
            sqlSession.insert(statement("insertByteArray"), resource);
        }
    }

    public void insertProcessDefinition(ProcessDefinitionImpl processDefinition) {
        sqlSession.insert(statement("insertProcessDefinition"), processDefinition);
    }

    public void deleteDeployment(String deploymentId) {
        sqlSession.delete(statement("deleteProcessDefinitionsForDeployment"), deploymentId);
        sqlSession.delete(statement("deleteByteArraysForDeployment"), deploymentId);
        sqlSession.delete(statement("deleteDeployment"), deploymentId);
    }

    public Map<String, Long> getTableCount() {
        Map<String, Long> tableCount = new HashMap<String, Long>();
        try {
            for (String tableName : tableNames) {
                tableCount.put(tableName, getTableCount(tableName));
            }
        } catch (Exception e) {
            throw new ActivitiException("couldn't get table counts", e);
        }
        return tableCount;
    }

    protected long getTableCount(String tableName) {
        log.fine("selecting table count for " + tableName);
        Long count = (Long) sqlSession.selectOne(statement("selectTableCount"),
                Collections.singletonMap("tableName", tableName));
        return count;
    }

    @SuppressWarnings("unchecked")
    public TablePage getTablePage(String tableName, int offset, int maxResults, String sortColumn,
            SortOrder sortOrder) {

        TablePage tablePage = new TablePage();

        Map<String, String> params = new HashMap<String, String>();
        params.put("tableName", tableName);
        if (sortColumn != null) {
            params.put("sortColumn", sortColumn);
            if (sortOrder.equals(SortOrder.ASCENDING)) {
                params.put("sortOrder", "asc");
            } else {
                params.put("sortOrder", "desc");
            }

            tablePage.setSort(sortColumn);
            tablePage.setOrder(sortOrder);
        }

        List<Map<String, Object>> tableData = (List<Map<String, Object>>) sqlSession
                .selectList(statement("selectTableData"), params, new RowBounds(offset, maxResults));

        tablePage.setTableName(tableName);
        tablePage.setStart(offset);
        tablePage.setTotal(getTableCount(tableName));
        tablePage.setRows(tableData);
        return tablePage;
    }

    public TableMetaData getTableMetaData(String tableName) {
        TableMetaData result = new TableMetaData();
        try {
            result.setTableName(tableName);
            DatabaseMetaData metaData = sqlSession.getConnection().getMetaData();
            ResultSet resultSet = metaData.getColumns(null, null, tableName, null);
            while (resultSet.next()) {
                String name = resultSet.getString("COLUMN_NAME");
                String type = resultSet.getString("TYPE_NAME");
                result.addColumnMetaData(name, type);
            }
        } catch (SQLException e) {
            throw new ActivitiException("Could not retrieve database metadata: " + e.getMessage());
        }

        return result;
    }

    public DbidBlock getNextDbidBlock() {
        PropertyImpl property = (PropertyImpl) sqlSession.selectOne(statement("selectProperty"), "next.dbid");
        long oldValue = Long.parseLong(property.getValue());
        long newValue = oldValue + blockSize;
        Map<String, Object> updateValues = new HashMap<String, Object>();
        updateValues.put("name", property.getName());
        updateValues.put("revision", property.getDbversion());
        updateValues.put("newRevision", property.getDbversion() + 1);
        updateValues.put("value", Long.toString(newValue));
        int rowsUpdated = sqlSession.update(statement("updateProperty"), updateValues);
        if (rowsUpdated != 1) {
            throw new ActivitiOptimisticLockingException("couldn't get next block of dbids");
        }
        return new DbidBlock(oldValue, newValue - 1);
    }

    public void addDeserializedObject(Object deserializedObject, byte[] serializedBytes,
            VariableInstance variableInstance) {
        deserializedObjects.add(new DeserializedObject(deserializedObject, serializedBytes, variableInstance));
    }

    public SqlSession getSqlSession() {
        return sqlSession;
    }

    public IdGenerator getIdGenerator() {
        return idGenerator;
    }

    public static String[] getTableNames() {
        return tableNames;
    }

    private static class Loaded {

        private static Logger log = Logger.getLogger(Loaded.class.getName());

        protected static Map<Class<?>, String> updateStatementIds = new HashMap<Class<?>, String>();
        static {
            updateStatementIds.put(DbExecutionImpl.class, "updateExecution");
            updateStatementIds.put(TaskImpl.class, "updateTask");
            updateStatementIds.put(TaskInvolvement.class, "updateTaskInvolvement");
            updateStatementIds.put(VariableInstance.class, "updateVariableInstance");
            updateStatementIds.put(ByteArrayImpl.class, "updateByteArray");
            updateStatementIds.put(MessageImpl.class, "updateMessage");
            updateStatementIds.put(TimerImpl.class, "updateTimer");
        }

        protected Map<Object, LoadedObject> loadedObjects = new HashMap<Object, LoadedObject>();
        protected IbatisPersistenceSession ibatisPersistenceSession;

        public Loaded(IbatisPersistenceSession ibatisPersistenceSession) {
            this.ibatisPersistenceSession = ibatisPersistenceSession;
        }

        public PersistentObject add(PersistentObject persistentObject) {
            Object key = getKey(persistentObject);
            LoadedObject loadedObject = loadedObjects.get(key);
            if (loadedObject != null) {
                return loadedObject.getPersistentObject();
            }
            loadedObject = new LoadedObject(persistentObject);
            loadedObjects.put(key, loadedObject);
            return persistentObject;
        }

        public List add(List persistentObjects) {
            List<PersistentObject> loadedPersistentObjects = new ArrayList<PersistentObject>();
            for (Object persistentObject : persistentObjects) {
                PersistentObject loadedPersistentObject = add((PersistentObject) persistentObject);
                loadedPersistentObjects.add(loadedPersistentObject);
            }
            return loadedPersistentObjects;

        }

        public void remove(PersistentObject persistentObject) {
            Object key = getKey(persistentObject);
            loadedObjects.remove(key);
        }

        protected Object getKey(PersistentObject persistentObject) {
            List<Object> key = new ArrayList<Object>();
            key.add(persistentObject.getClass());
            key.add(persistentObject.getId());
            return key;
        }

        public void flush(SqlSession sqlSession, List<PersistentObject> updatedObjects) {
            for (PersistentObject updatedObject : updatedObjects) {
                Class<?> updatedObjectClass = updatedObject.getClass();
                String updateStatementId = findUpdateStatementId(updatedObjectClass);
                if (updateStatementId == null) {
                    throw new ActivitiException("no update statement id for " + updatedObject.getClass());
                }
                log.fine("updating: " + updatedObjectClass + "[" + updatedObject.getId() + "]");
                sqlSession.update(ibatisPersistenceSession.statement(updateStatementId), updatedObject);
            }
            updatedObjects.clear();
        }

        protected String findUpdateStatementId(Class<?> updatedObjectClass) {
            if (updatedObjectClass == null) {
                return null;
            }
            String updateStatementId = updateStatementIds.get(updatedObjectClass);
            if (updateStatementId == null) {
                return findUpdateStatementId(updatedObjectClass.getSuperclass());
            }
            return updateStatementId;
        }

        public List<PersistentObject> getUpdatedObjects() {
            List<PersistentObject> updatedObjects = new ArrayList<PersistentObject>();
            for (LoadedObject loadedObject : loadedObjects.values()) {
                PersistentObject persistentObject = (PersistentObject) loadedObject.getPersistentObject();
                Object originalState = loadedObject.getPersistentObjectState();
                if (!originalState.equals(persistentObject.getPersistentState())) {
                    updatedObjects.add(persistentObject);
                } else {
                    log.finest("persistent object '" + persistentObject + "' was not updated");
                }
            }
            return updatedObjects;
        }
    }

    private static class Inserted {

        private static Logger log = Logger.getLogger(Inserted.class.getName());

        protected static Map<Class<?>, String> insertStatementIds = new HashMap<Class<?>, String>();
        static {
            insertStatementIds.put(DbExecutionImpl.class, "insertExecution");
            insertStatementIds.put(JobImpl.class, "insertJob");
            insertStatementIds.put(TaskImpl.class, "insertTask");
            insertStatementIds.put(TaskInvolvement.class, "insertTaskInvolvement");
            insertStatementIds.put(VariableInstance.class, "insertVariableInstance");
            insertStatementIds.put(ByteArrayImpl.class, "insertByteArray");
            insertStatementIds.put(MessageImpl.class, "insertMessage");
            insertStatementIds.put(TimerImpl.class, "insertTimer");
        }

        protected List<PersistentObject> insertedObjects = new ArrayList<PersistentObject>();
        protected IbatisPersistenceSession ibatisPersistenceSession;

        public Inserted(IbatisPersistenceSession ibatisPersistenceSession) {
            this.ibatisPersistenceSession = ibatisPersistenceSession;
        }

        public void add(PersistentObject persistentObject) {
            insertedObjects.add(persistentObject);
        }

        public void remove(PersistentObject persistentObject) {
            insertedObjects.remove(persistentObject);
        }

        public List<PersistentObject> getInsertedObjects() {
            return insertedObjects;
        }

        public void flush(SqlSession sqlSession) {
            for (PersistentObject insertedObject : insertedObjects) {
                Class<?> insertedObjectClass = insertedObject.getClass();
                String insertStatementId = findInsertStatementId(insertedObjectClass);
                if (insertStatementId == null) {
                    throw new ActivitiException("no insert statement id for " + insertedObject.getClass());
                }
                log.fine("inserting: " + insertedObjectClass + "[" + insertedObject.getId() + "]");
                sqlSession.insert(ibatisPersistenceSession.statement(insertStatementId), insertedObject);
            }
            insertedObjects.clear();
        }

        protected String findInsertStatementId(Class<?> insertedObjectClass) {
            if (insertedObjectClass == null) {
                return null;
            }
            String insertStatementId = insertStatementIds.get(insertedObjectClass);
            if (insertStatementId == null) {
                return findInsertStatementId(insertedObjectClass.getSuperclass());
            }
            return insertStatementId;
        }

        public int size() {
            return insertedObjects.size();
        }
    }

    private static class Deleted {

        private static Logger log = Logger.getLogger(Deleted.class.getName());

        protected static Map<Class<?>, String> deleteStatementIds = new HashMap<Class<?>, String>();
        static {
            deleteStatementIds.put(DbExecutionImpl.class, "deleteExecution");
            deleteStatementIds.put(TaskImpl.class, "deleteTask");
            deleteStatementIds.put(TaskInvolvement.class, "deleteTaskInvolvement");
            deleteStatementIds.put(VariableInstance.class, "deleteVariableInstance");
            deleteStatementIds.put(ByteArrayImpl.class, "deleteByteArray");
            deleteStatementIds.put(MessageImpl.class, "deleteJob");
            deleteStatementIds.put(TimerImpl.class, "deleteJob");
        }

        protected List<PersistentObject> deletedObjects = new ArrayList<PersistentObject>();
        protected Set<PersistentObjectId> deletedObjectIds = new HashSet<PersistentObjectId>();
        protected IbatisPersistenceSession ibatisPersistenceSession;

        public Deleted(IbatisPersistenceSession ibatisPersistenceSession) {
            this.ibatisPersistenceSession = ibatisPersistenceSession;
        }

        public void add(PersistentObject persistentObject) {
            PersistentObjectId deletedObjectId = new PersistentObjectId(persistentObject);
            if (!deletedObjectIds.contains(deletedObjectId)) {
                deletedObjects.add(persistentObject);
                deletedObjectIds.add(deletedObjectId);
            }
        }

        public int size() {
            return deletedObjects.size();
        }

        public List<PersistentObject> getDeletedObjects() {
            return deletedObjects;
        }

        public void flush(SqlSession sqlSession) {
            for (PersistentObject deletedObject : deletedObjects) {
                Class<?> deletedObjectClass = deletedObject.getClass();
                String deleteStatementId = findDeleteStatementId(deletedObjectClass);
                if (deleteStatementId == null) {
                    throw new ActivitiException("no delete statement id for " + deletedObject.getClass());
                }
                log.fine("deleting: " + deletedObjectClass + "[" + deletedObject.getId() + "]");
                sqlSession.delete(ibatisPersistenceSession.statement(deleteStatementId), deletedObject);
            }
            deletedObjects.clear();
            deletedObjectIds.clear();
        }

        protected String findDeleteStatementId(Class<?> deletedObjectClass) {
            if (deletedObjectClass == null) {
                return null;
            }
            String deleteStatementId = deleteStatementIds.get(deletedObjectClass);
            if (deleteStatementId == null) {
                return findDeleteStatementId(deletedObjectClass.getSuperclass());
            }
            return deleteStatementId;
        }
    }
}