Activiti的持久化和缓存最终都是有DbSqlSession处理的,在此主要也是讲这个对象。类图如下。
public class DbSqlSession implements PersistenOperation, Session {
protected SqlSession sqlSession; protected List<PersistentObject> insertedObjects = new ArrayList<PersistentObject>(); protected Map<Class<?>, Map<String, CachedObject>> cachedObjects = new HashMap<Class<?>, Map<String, CachedObject>>(); protected List<DeleteOperation> deletedObjects = new ArrayList<DeleteOperation>(); }
首先是insert,这里它使用了一个List对插入对象进行缓存,在通过flush()方法,flush之后最终会使用sqlSession把数据鎚入数据源,sqlSesion是所有对象最终持久化的唯一入口。
delete的作法基本上和inert一样,deltet做多了一层封装DeleteOperation,使用这个对象,适配id进行删除。
重点是update,这里update要结合select一起使用。在DbSqlSession对象中,没有update方法,update要求先进行select查询对象,更新查询对象,最终flush()的适合,chched容器会检查最终状态是否和查询时的状态一致,如果不一致,才会进行持久化更新。也就是说,用户只需要查询出对象,使用对象的set方法,更新属性,这样子就会默认进行更新了。在实现的适合注意getPersistentState()的实现,也就是说,会使用到属性更新的属性,需要够着一个判断状态更新的对象让cache容器对比。
flush()的时候,cache容器会先清理掉insert对象和delete对象。
我们看看CachedObject对象的设计。
public static class CachedObject { protected PersistentObject persistentObject; protected Object persistentObjectState; public CachedObject(PersistentObject persistentObject, boolean storeState) { this.persistentObject = persistentObject; if (storeState) { this.persistentObjectState = persistentObject.getPersistentState(); } } public PersistentObject getPersistentObject() { return persistentObject; } public Object getPersistentObjectState() { return persistentObjectState; } }
这里可以看明白我们属性设置对比和最终持久。当查询的适合,用storeState = true 构造CachedObject, PersistentState会被保存在对象中。外部API通过select对象的引用更新属性,flush()的适合,会拿最终对象的PersistentState与select出来的对象进行对比,如果不一致,就update数据源记录。代码如下。
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 (!deletedObjects.contains(persistentObject)) { Object originalState = cachedObject.getPersistentObjectState(); if (!originalState.equals(persistentObject.getPersistentState())) { updatedObjects.add(persistentObject); } else { log.finest("loaded object '" + persistentObject + "' was not updated"); } } } } return updatedObjects; }
到这里,我遇到了一个问题,如果我不使用对象属性更新,也想update数据源记录,显然activiti这一套实现机制是不支持的,那怎么办呢?我的解决办法如下。
/** * 更新数据,对源码的扩展。 * 一般的更新是直接通过Entity对象属性设置。此处暴露出一种直接更新对象的方法。 * * @param persistentObject */ public void update(PersistentObject persistentObject) { PersistentObject oldPersistentObject = selectById(persistentObject.getClass(), persistentObject.getId()); //先put对象进缓存容器,再讲持久状态回滚,这样flush的时候,会检查到对象状态变化,进而更新对象。 CachedObject cachedObject = cachePut(persistentObject, false); cachedObject.persistentObjectState = oldPersistentObject.getPersistentState(); }
先用select,保证数据源中的记录已经缓存在cache容器中,跟着强制更新PersistentState。保证flush()的时候可以检查状态的一致性。
activiti依赖Mybatis,关于缓存机制,Mybatis还有自身的机制,有自己的一套缓存回收机制。activiti默认是不适用Mybatis的缓存的,activiti自身的缓存机制够简单,如果在自身command机制里面适用,性能很高,如果是比较复杂的业务场景,会在结合外部扩展适用的话,很可能就不够用了。所有个人认为可以考虑结合Mybatis的缓存机制,混合适用。