Activiti 的数据持久化

数据持久化

​ activiti 使用了一种稍显特别的方式来持久化数据。

本文基于activiti 5.22.0

架构

​ activiti 采用了充血模型,大部分业务逻辑放在了对应的实体类 entity 中,但是也许是觉得实体类责任过重,所有实体类都有一个对应的管理类 manager 类持有和数据库交互的方法包括新增、删除和查询(没有修改,这个问题后面说)。因为相比查询和删除来说新增需要更多的业务逻辑,所以有些实体类自身仍持有新增方法。

manager 类

所有管理类
AbstractManager
Session

​ manager 类架构比较简单,所有的 manager 类继承了抽象类 AbstractManager,AbstractManager 实现了接口 Session,作为 Session 架构的一部分。

​ AbstractManager 定义了实例对象的新增删除和获取 DbSqlSession 等方法。

entity 类

所有实体类的顶级接口 PersistentObject 是数据持久化尤其是更新操作的关键。

public interface PersistentObject {
  //activiti中所有实体表均为单主键
  String getId();
  //当实体类保存时,若无主键,则会调用id生成器创建一个
  void setId(String id);
  //获取实体状态,后文详解
  Object getPersistentState();
}

除 PersistentObject 外,还有几个和持久化相关的接口。

HasRevision

public interface HasRevision {
  void setRevision(int revision);
  int getRevision();
  int getRevisionNext();
}

乐观锁,提供给需要修改的表。

BulkDeleteable

标记用接口,标识该实体是否可以批量删除。

SessionFactory 架构

JPA
用户,组,用户组
activiti实体
EntityManagerSessionFactory
SpringEntityManagerSessionFactory
UserEntityManagerFactory
GroupEntityManagerFactory
MembershipEntityManagerFactory
GenericManagerFactory
DefaultHistoryManagerSessionFactory
SessionFactory
DbSqlSessionFactory

SessionFactory 为顶级接口,其中定义了 openSession 和 getSessionType 方法。

public interface SessionFactory {
  //获取其管理的Session的class
  Class<?> getSessionType();
  //获取其管理的Session实例
  Session openSession();
}
  1. DefaultHistoryManagerSessionFactory:管理 DefaultHistoryManager 默认历史管理类,之所以单独管理是为了方便自定义历史管理类并交给 DefaultHistoryManagerSessionFactory 管理。
  2. SpringEntityManagerSessionFactory、EntityManagerSessionFactory 管理 JPA 实体
  3. UserEntityManagerFactory、GroupEntityManagerFactory、MembershipEntityManagerFactory 分别管理用户管理类、组管理类、用户-组管理类,之所以分别管理也是为了方便拓展和自定义实现。
  4. GenericManagerFactory 管理除用户相关的管理类之外的管理类
  5. DbSqlSessionFactory 管理 DbSqlSession,非常重要的管理类。负责根据数据库不同选择不同的执行 SQL。
初始化
protected void init() {
  ...
  initSessionFactories();
  ...
}

​ 初始化操作同样是在 ProcessEngineConfigurationImpl 的 init 方法中(EntityManagerSessionFactory 在 jpa 初始化方法中初始化的)

protected void initSessionFactories() {
  //开关属性
  if (sessionFactories==null) {
    sessionFactories = new HashMap<Class<?>, SessionFactory>();

    //初始化DbSqlSessionFactory
    if (dbSqlSessionFactory == null) {
      dbSqlSessionFactory = new DbSqlSessionFactory();
    }
    
    dbSqlSessionFactory.setDatabaseType(databaseType);
    dbSqlSessionFactory.setIdGenerator(idGenerator);
    dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory);
    dbSqlSessionFactory.setDbIdentityUsed(isDbIdentityUsed);
    dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed);
    dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix);
    dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema);
    dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog);
    dbSqlSessionFactory.setDatabaseSchema(databaseSchema);
    dbSqlSessionFactory.setBulkInsertEnabled(isBulkInsertEnabled, databaseType);
    dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert);
    addSessionFactory(dbSqlSessionFactory);

    //初始化GenericManagerFactory,可以看到除了人员和历史之外的管理类都由GenericManagerFactory管理
    addSessionFactory(new GenericManagerFactory(AttachmentEntityManager.class));
    addSessionFactory(new GenericManagerFactory(CommentEntityManager.class));
    addSessionFactory(new GenericManagerFactory(DeploymentEntityManager.class));
    addSessionFactory(new GenericManagerFactory(ModelEntityManager.class));
    addSessionFactory(new GenericManagerFactory(ExecutionEntityManager.class));
    addSessionFactory(new GenericManagerFactory(HistoricActivityInstanceEntityManager.class));
    addSessionFactory(new GenericManagerFactory(HistoricDetailEntityManager.class));
    addSessionFactory(new GenericManagerFactory(HistoricProcessInstanceEntityManager.class));
    addSessionFactory(new GenericManagerFactory(HistoricVariableInstanceEntityManager.class));
    addSessionFactory(new GenericManagerFactory(HistoricTaskInstanceEntityManager.class));
    addSessionFactory(new GenericManagerFactory(HistoricIdentityLinkEntityManager.class));
    addSessionFactory(new GenericManagerFactory(IdentityInfoEntityManager.class));
    addSessionFactory(new GenericManagerFactory(IdentityLinkEntityManager.class));
    addSessionFactory(new GenericManagerFactory(JobEntityManager.class));
    addSessionFactory(new GenericManagerFactory(ProcessDefinitionEntityManager.class));
    addSessionFactory(new GenericManagerFactory(ProcessDefinitionInfoEntityManager.class));
    addSessionFactory(new GenericManagerFactory(PropertyEntityManager.class));
    addSessionFactory(new GenericManagerFactory(ResourceEntityManager.class));
    addSessionFactory(new GenericManagerFactory(ByteArrayEntityManager.class));
    addSessionFactory(new GenericManagerFactory(TableDataManager.class));
    addSessionFactory(new GenericManagerFactory(TaskEntityManager.class));
    addSessionFactory(new GenericManagerFactory(VariableInstanceEntityManager.class));
    addSessionFactory(new GenericManagerFactory(EventSubscriptionEntityManager.class));
    addSessionFactory(new GenericManagerFactory(EventLogEntryEntityManager.class));
    //初始化DefaultHistoryManagerSessionFactory
    addSessionFactory(new DefaultHistoryManagerSessionFactory());
    //初始化人员管理相关的SessionFactory
    addSessionFactory(new UserEntityManagerFactory());
    addSessionFactory(new GroupEntityManagerFactory());
    addSessionFactory(new MembershipEntityManagerFactory());
  }
  //若有,则初始化自定义的SessionFactory
  if (customSessionFactories!=null) {
    for (SessionFactory sessionFactory: customSessionFactories) {
      addSessionFactory(sessionFactory);
    }
  }
}

​ 默认初始化操作除了 DbSqlSessionFactory 外均只是创建了对应的类并添加到 sessionFactories 属性中,而 DbSqlSessionFactory 初始化时设置的属性有

//数据库类型
dbSqlSessionFactory.setDatabaseType(databaseType);
//id生成器
dbSqlSessionFactory.setIdGenerator(idGenerator);
//SqlSessionFactory
dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory);
//是否使用群组和用户表
dbSqlSessionFactory.setDbIdentityUsed(isDbIdentityUsed);
//是否使用历史表
dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed);
//表名前缀
dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix);
//表名前缀是否为模式名
dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema);
//数据库目录
dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog);
//数据库模式
dbSqlSessionFactory.setDatabaseSchema(databaseSchema);
//能否批量插入
dbSqlSessionFactory.setBulkInsertEnabled(isBulkInsertEnabled, databaseType);
//单次插入的最大数量
dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert);

​ 这个地方的 表名前缀、表名前缀是否为模式名、数据库目录、数据库模式 均只用于更新创建删除数据库表结构,真正用于执行 sql 时的配置是在 sqlSessionFactory 初始化时配置的

具体实现

​ GenericManagerFactory 初始化时属性中存储了对应 Session 的 Class,openSession 时使用反射的方式获取对应的管理类实例。

​ 其他除了 DbsqlSessionFactory 和 EntityManagerSessionFactory 外均直接使用 new 关键字创建对应 session 实例。EntityManagerSessionFactory 涉及 jpa 此处不做介绍,DbsqlSessionFactory 在后面详细介绍。

Session 架构

​ Session 均由对应的 SessionFactory 创建出来,且均实现了 Session 接口。

​ Session 接口中定义了 flush 和 close 两个方法

public interface Session {
  
  void flush();

  void close();
}

​ 除了 DbsqlSession 和 EntityManagerSessionImpl 外其他 Session 的 flush 和 close 是空实现的

命令模式及责任链

​ 此处不会详细讲,只会介绍一下涉及数据持久化的信息。

​ acticiti 中所有获取 Session 均会通过 CommandContext(具体命令类与命令实现者解耦),因此 CommandContext 中存储了该次操作中所有涉及到的 Session,而数据库交互只会通过 Session 来交互。

@SuppressWarnings({"unchecked"})
//所有获取session均会使用命令上下文的getSession方法
public <T> T getSession(Class<T> sessionClass) {
  //若该Session曾经获得过,则直接使用,否则获取Session
  Session session = sessions.get(sessionClass);
  if (session == null) {
    SessionFactory sessionFactory = sessionFactories.get(sessionClass);
    if (sessionFactory==null) {
      throw new ActivitiException("no session factory configured for "+sessionClass.getName());
    }
    //获取Session
    session = sessionFactory.openSession();
    //将其存入属性中
    sessions.put(sessionClass, session);
  }
  return (T) session;
}

​ 在命令上下文拦截器中在调用结束时,若命令上下文为非重用的,则会关闭命令上下文。

public <T> T execute(CommandConfig config, Command<T> command) {
  CommandContext context = Context.getCommandContext();

  //是否重用命令上下文
  boolean contextReused = false;
  // We need to check the exception, because the transaction can be in a rollback state,
  // and some other command is being fired to compensate (eg. decrementing job retries)
  //若无命令上下文或...则创建新命令上下文
  if (!config.isContextReusePossible() || context == null || context.getException() != null) { 
   context = commandContextFactory.createCommandContext(command);
  }  
  else {
      //将该拦截器标为重用命令上下文
   log.debug("Valid context found. Reusing it for the current command '{}'", command.getClass().getCanonicalName());
   contextReused = true;
  }

  try {
    // Push on stack
    Context.setCommandContext(context);
    Context.setProcessEngineConfiguration(processEngineConfiguration);
    
    return next.execute(config, command);
    
  } catch (Exception e) {
   
    context.exception(e);
    
  } finally {
    try {
        //若命令上下文非被重用,关闭命令上下文
     if (!contextReused) {
        context.close();
     }
    } finally {
     // Pop from stack
     Context.removeCommandContext();
     Context.removeProcessEngineConfiguration();
     Context.removeBpmnOverrideContext();
    }
  }
  
  return null;
}

​ 关闭命令上下文的实现为:

public void close() {
  ...
  try {
    ...
	//若无异常
    if (exception == null) {
      flushSessions();
    }
	...
  } catch (Throwable exception) {
    ...
  } finally {
    closeSessions();
  }
  ...
}

​ 在一次请求正常结束后,CommandContext 会调用所有涉及到的 Session 的 flush 方法。
​ 无论该次请求是否正常结束,CommandContext 会调用所有涉及到的 Session 的 close 方法。

DbSqlSessionFactory 与 DbSqlSession

​ DbSqlSessionFactory 除了负责创建 DbSqlSession 外,还负责屏蔽不同数据库之间的语法差异。其持有 SqlSessionFactory 以及所有数据库相关的配置项。

DbSqlSessionFactory 初始化

​ SqlSessionFactory 在 Factory 初始化时被 set 到了 DbSqlSessionFactory 中,而 SqlSessionFactory 的初始化在方法 initSqlSessionFactory 中。

protected void initSqlSessionFactory() {
  //开关属性
  if (sqlSessionFactory==null) {
    InputStream inputStream = null;
    try {
      inputStream = getMyBatisXmlConfigurationSteam();

      // update the jdbc parameters to the configured ones...
      Environment environment = new Environment("default", transactionFactory, dataSource);
      Reader reader = new InputStreamReader(inputStream);
      Properties properties = new Properties();
      //指定数据库表名前缀
      properties.put("prefix", databaseTablePrefix);
      String wildcardEscapeClause = "";
      //指定转义符
      if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) {
        wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'";
      }
      properties.put("wildcardEscapeClause", wildcardEscapeClause);
      //根据数据库类型不同,设置不同sql方言
      if(databaseType != null) {
        properties.put("limitBefore" , DbSqlSessionFactory.databaseSpecificLimitBeforeStatements.get(databaseType));
        properties.put("limitAfter" , DbSqlSessionFactory.databaseSpecificLimitAfterStatements.get(databaseType));
        properties.put("limitBetween" , DbSqlSessionFactory.databaseSpecificLimitBetweenStatements.get(databaseType));
        properties.put("limitOuterJoinBetween" , DbSqlSessionFactory.databaseOuterJoinLimitBetweenStatements.get(databaseType));
        properties.put("orderBy" , DbSqlSessionFactory.databaseSpecificOrderByStatements.get(databaseType));
        properties.put("limitBeforeNativeQuery" , ObjectUtils.toString(DbSqlSessionFactory.databaseSpecificLimitBeforeNativeQueryStatements.get(databaseType)));
      }
      
      Configuration configuration = initMybatisConfiguration(environment, reader, properties);
      sqlSessionFactory = new DefaultSqlSessionFactory(configuration);

    } catch (Exception e) {
      throw new ActivitiException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e);
    } finally {
      IoUtil.closeSilently(inputStream);
    }
  }
}

​ 这里有个非常关键的操作就是 根据数据库类型不同,为某些操作拼接不同sql,这里是屏蔽数据库差异的关键之一。此外我们也可以看到数据库表名前缀是怎么起作用的。

DbSqlSession 创建

​ 不同于其他 Session,DbSqlSession 需要获取 DbSqlSessionFactory 中的众多配置项,此外还需要创建出 SqlSession。

public DbSqlSession(DbSqlSessionFactory dbSqlSessionFactory) {
  this.dbSqlSessionFactory = dbSqlSessionFactory;
  this.sqlSession = dbSqlSessionFactory
    .getSqlSessionFactory()
    .openSession();
}

​ 在 DbSqlSession 创建时,它持有了 DbSqlSessionFactory 方便获取其中的配置项,同时使用 DbSqlSessionFactory 中的 SqlSessionFactory 创建出 SqlSession。

​ 这里说明一下 DbSqlSessionFactory、DbSqlSession、SqlSessionFactory、SqlSession 之间的关系

​ DbSqlSessionFactory、DbSqlSession 是 activiti 的类。SqlSessionFactory、SqlSession是属于 mybatis 的类,activiti 对其作了一层包装方便实现包括数据库差异屏蔽及实体类操作。SqlSession 由 SqlSessionFactory 创建。DbSqlSession 由 DbSqlSessionFactory 创建且持有 DbSqlSessionFactory 的引用,同时持有 SqlSession 用于数据库交互。DbSqlSessionFactory 持有 SqlSessionFactory 用于创建 SqlSession 。

屏蔽数据库差异

​ activiti 通过两种方式来屏蔽数据库之间的差异,第一种为插入部分方言 sql 语法,第二种为使用专用方言sql。

插入部分方言 sql 语法
//分页查询前
public static final Map<String, String> databaseSpecificLimitBeforeStatements = new HashMap<String, String>();
//分页查询后
public static final Map<String, String> databaseSpecificLimitAfterStatements = new HashMap<String, String>();
//分页查询间
public static final Map<String, String> databaseSpecificLimitBetweenStatements = new HashMap<String, String>();
//排序
public static final Map<String, String> databaseSpecificOrderByStatements = new HashMap<String, String>();
//分页外连接字段间
public static final Map<String, String> databaseOuterJoinLimitBetweenStatements = new HashMap<String, String>();
//原生查询分页查询前
public static final Map<String, String> databaseSpecificLimitBeforeNativeQueryStatements = new HashMap<String, String>();

​ 在 DbSqlSessionFactory 静态代码块中初始化了这六个特殊语法的 Map,其中每个 Map 的 key 为数据库类型,value 为 sql 方言片段。

​ 在 SqlSession 初始化时,对应数据库的方言片段会被设置到 SqlSession 中作为变量。

以 mysql 分页查询为例

在静态代码块中 mysql 的方言片段被初始化到 databaseSpecificLimitAfterStatements中

...
databaseSpecificLimitAfterStatements.put("mysql", "LIMIT #{maxResults} OFFSET #{firstResult}");
...

在 SqlSession 初始化时,mysql 对应的的分页查询 sql 片段会被设置到 SqlSession 中作为变量。

...
properties.put("limitAfter",DbSqlSessionFactory.databaseSpecificLimitAfterStatements.get(databaseType));
Configuration configuration = initMybatisConfiguration(environment, reader, properties);
sqlSessionFactory = new DefaultSqlSessionFactory(configuration);
...

执行 sql 时,对应SQL中的${}字段会被替换为对应值

<select id="selectTaskByQueryCriteria" parameterType="org.activiti.engine.impl.TaskQueryImpl" resultMap="taskResultMap">
  ${limitBefore}
  select distinct RES.* ${limitBetween}
  <include refid="selectTaskByQueryCriteriaSql"/> 
  ${orderBy}
  ${limitAfter}
</select>
使用专用方言 sql

​ 当使用第一种方案仍无法满足要求时,会使用该数据库的专用 sql。

protected static final Map<String, Map<String, String>> databaseSpecificStatements = new HashMap<String, Map<String,String>>();

​ 在 DbSqlSessionFactory 静态代码块中初始化了专用 sql 的 map,其为两层 map 结构,外层 key 为数据库类型,内层 key 为通用 sql 的 id,value 为专用 sql 的 id。

public void setDatabaseType(String databaseType) {
  this.databaseType = databaseType;
  this.statementMappings = databaseSpecificStatements.get(databaseType);
}

​ DbSqlSessionFactory 设置数据库类型时会根据数据库类型取出该数据库的专用 sql 的映射关系存入 statementMappings 属性中。

public String mapStatement(String statement) {
  if (statementMappings==null) {
    return statement;
  }
  String mappedStatement = statementMappings.get(statement);
  return (mappedStatement!=null ? mappedStatement : statement);
}

​ 每次指定 sql 前都会调用 mapStatement 方法使用通用 sql 的 id 获取专用 sql 的 id,若有,则调用专用 sql。

数据的增删改

​ 如果在 activiti 内打断点就会发现 manage 类的新增和删除操作执行后数据库内字段值并未发生变化,且会发现并没有修改方法,这是因为 activiti 实现了一套比较特殊的持久化方式。在 DbSqlSession 中缓存了在该次提交中新增、删除及可能更新的实体,并在执行 flush 方法时进行数据库交互。(在 close 方法为关闭 SqlSession)

public void flush() {
  List<DeleteOperation> removedOperations = removeUnnecessaryOperations();
  
  flushDeserializedObjects();
  List<PersistentObject> updatedObjects = getUpdatedObjects();
  
  ...日志

  flushInserts();
  flushUpdates(updatedObjects);
  flushDeletes(removedOperations);
}

​ DbSqlSession 中有这三个属性是持久化的关键。

//所有insert方法放入的对象
protected Map<Class<? extends PersistentObject>, List<PersistentObject>> insertedObjects = new HashMap<Class<? extends PersistentObject>, List<PersistentObject>>();
//缓存的实体
protected Map<Class<?>, Map<String, CachedObject>> cachedObjects = new HashMap<Class<?>, Map<String,CachedObject>>();
//将要执行的删除操作
protected List<DeleteOperation> deleteOperations = new ArrayList<DeleteOperation>();
insertedObjects 的新增
public void insert(PersistentObject persistentObject) {
  //若未设置id,使用配置的id生成器生成一个id
  if (persistentObject.getId()==null) {
    String id = dbSqlSessionFactory.getIdGenerator().getNextId();  
    persistentObject.setId(id);
  }
  //将该实体放到insertedObjects中
  Class<? extends PersistentObject> clazz = persistentObject.getClass();
  if (!insertedObjects.containsKey(clazz)) {
   insertedObjects.put(clazz, new ArrayList<PersistentObject>());
  }
  
  insertedObjects.get(clazz).add(persistentObject);
  //置入缓存
  cachePut(persistentObject, false);
}

所有的实体类及管理类的新增方法并不会真正的操作数据库,而是维护了DbSqlSession 中的 insertedObjects 属性,其中 insertedObjects 的 key 为实体的 Class,value 为实体本身。

deleteOperations 的新增

​ deleteOperations 内存放的是一个内部类,其实现的接口为 DeleteOperation

public interface DeleteOperation {
   
   /**
    * @return The persistent object class that is being deleted.
    *         Null in case there are multiple objects of different types!
    */
   // 返回被删除的实体的类型,若为多个不同类型的实体,则返回 null
   Class<? extends PersistentObject> getPersistentObjectClass();
  
  //要删除删除的实体和 other 是否为同一实体,只有 CheckedDeleteOperation 中有具体实现
  boolean sameIdentity(PersistentObject other);
  //清除缓存,只有 CheckedDeleteOperation 中有具体实现
  void clearCache();
  
  void execute();
  
}

​ 具体实现有三个,但只有 BulkDeleteOperation 和 CheckedDeleteOperation 有用。

​ 其中按照 id 删除会使用 CheckedDeleteOperation,按条件删除会调用 BulkDeleteOperation。

public void delete(String statement, Object parameter) {
  deleteOperations.add(new BulkDeleteOperation(statement, parameter));
}

public void delete(PersistentObject persistentObject) {
  //若重复删除
  for (DeleteOperation deleteOperation: deleteOperations) {
      if (deleteOperation.sameIdentity(persistentObject)) {
        log.debug("skipping redundant delete: {}", persistentObject);
        return; // Skip this delete. It was already added.
      }
  }
  
  deleteOperations.add(new CheckedDeleteOperation(persistentObject));
}
cachedObjects 的维护
protected CachedObject cachePut(PersistentObject persistentObject, boolean storeState) {
  Map<String, CachedObject> classCache = cachedObjects.get(persistentObject.getClass());
  if (classCache==null) {
    classCache = new HashMap<String, CachedObject>();
    cachedObjects.put(persistentObject.getClass(), classCache);
  }
  CachedObject cachedObject = new CachedObject(persistentObject, storeState);
  classCache.put(persistentObject.getId(), cachedObject);
  return cachedObject;
}

通过 cachePut 方法新增,所有的新增、修改、查询的实体均会存入缓存。

其中新增和修改的 storeState 为 false,查询为 true;

public CachedObject(PersistentObject persistentObject, boolean storeState) {
  //实体本身
  this.persistentObject = persistentObject;
  if (storeState) {
    //实体查询出来的时候的状态
    this.persistentObjectState = persistentObject.getPersistentState();
  }
}

也就是说若为查询出的实体,会将实体查询出时的状态存入缓存中。

具体查询过程在查询处介绍。

移除多余操作

removeUnnecessaryOperations 方法

因为此处缓存的实体用于判断该实体是否被更新,因此新增和删除的实体要在缓存中删除。

/**
 * Clears all deleted and inserted objects from the cache, 
 * and removes inserts and deletes that cancel each other.
 */
//这个方法的返回值中只有在本次提交中新增且删除的删除动作,用来触发删除事件,虽然最后好像也没有触发什么事件
protected List<DeleteOperation> removeUnnecessaryOperations() {
  List<DeleteOperation> removedDeleteOperations = new ArrayList<DeleteOperation>();

  //遍历被删除的对象
  for (Iterator<DeleteOperation> deleteIterator = deleteOperations.iterator(); deleteIterator.hasNext();) {
   
    DeleteOperation deleteOperation = deleteIterator.next();
    Class<? extends PersistentObject> deletedPersistentObjectClass = deleteOperation.getPersistentObjectClass();
    
    List<PersistentObject> insertedObjectsOfSameClass = insertedObjects.get(deletedPersistentObjectClass);
    if (insertedObjectsOfSameClass != null && insertedObjectsOfSameClass.size() > 0) {
       
     for (Iterator<PersistentObject> insertIterator = insertedObjectsOfSameClass.iterator(); insertIterator.hasNext();) {
       PersistentObject insertedObject = insertIterator.next();
       
       // 如果被删除的实体是本次提交中新增的
       if (deleteOperation.sameIdentity(insertedObject)) {
         // remove the insert and the delete, they cancel each other
         // 将其在新增和删除中都移除,因为这两个操作互相抵消了
         insertIterator.remove();
         deleteIterator.remove();
         // add removed operations to be able to fire events
         // 添加删除动作用于触发事件
         removedDeleteOperations.add( deleteOperation);
       }
     }
     // 如果该实体的新增全部被移除,将其在新增中移除
     if (insertedObjects.get(deletedPersistentObjectClass).size() == 0) {
       insertedObjects.remove(deletedPersistentObjectClass);
     }
     
    }
    
    // in any case, remove the deleted object from the cache
    // 将被删除的实体在缓存中删除
    deleteOperation.clearCache();
  }
  
  for (Class<? extends PersistentObject> persistentObjectClass : insertedObjects.keySet()) {
   for (PersistentObject insertedObject : insertedObjects.get(persistentObjectClass)) {
      // 将新增的实体在缓存中删除
      cacheRemove(insertedObject.getClass(), insertedObject.getId());
   }
  }

  return removedDeleteOperations;
}
获取在本次提交中发生更新的实体

getUpdatedObjects 方法

public List<PersistentObject> getUpdatedObjects() {
  List<PersistentObject> updatedObjects = new ArrayList<PersistentObject>();
  //遍历缓存
  for (Class<?> clazz: cachedObjects.keySet()) {
    
    Map<String, CachedObject> classCache = cachedObjects.get(clazz);
    for (CachedObject cachedObject: classCache.values()) {
      
      PersistentObject persistentObject = cachedObject.getPersistentObject();
      //若该实体没有被删除
      if (!isPersistentObjectDeleted(persistentObject)) {
        //获取实体初始状态,若为新增和修改,此处为空,否则为查询出时的状态
        Object originalState = cachedObject.getPersistentObjectState();
        //若实体与查询出来的时候的状态不同,则说明该实体被修改
        if (persistentObject.getPersistentState() != null && 
              !persistentObject.getPersistentState().equals(originalState)) {
          updatedObjects.add(persistentObject);
        } else {
          log.trace("loaded object '{}' was not updated", persistentObject);
        }
      }
      
    }
    
  }
  return updatedObjects;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值