几个GAE/J中的Transaction的概念
Transaction是在计算机很多领域里都需要的,最基础的知识相信大家都懂,那就是一个或一组操作,要么都成功,一旦有任何一个失败则所有的都失败,要回滚到原始位置。这个概念在GAE/J中一样。在datastore中,所有的写操作都被认为是atomic的,任何尝试着创建,更新或者删除实体的操作要么成功,要么就失败。一个操作有可能因为很多原因而失败,比如当许多用户同时尝试去修改这个值,或者该操作超过了其允许的配额值,当然也有可能是datastore的内部错误造成。在任何情况下的出错,都会使得所有操作就像没有发生一样而回滚。
实体组在GAE/J中是一个十分重要的概念。最开始的时候,我也是什么都想着往关系数据库上套,结果发现一来出现很多外键上的问题,二来出现很多包含关系的问题。个人认为,对于datastore而言,实体组是必须十分熟悉的内容。实体组之间的关系是以父子来称呼的,比如一个实体是国家,另一个实体是省份,那么显然一个国家会有很多省份而一个省份属于一个国家,于是我们可以将国家视作一个父实体,则通过每一个子实体都可以得到父实体也就是这个国家。当实体之间有了父子关系的时候,我们可以将他们视为在一个实体组中。对于一个没有父实体的实体而言,它就是一个根实体。一个实体的父实体是在该实体创建时就设定好了而不可以更改的。 处在同一个根实体下的所有实体都可以看作属于同一个实体组,被存在同一个datastore节点上。
那么实体组和Transaction的关系在哪呢?一个单一的Transaction只能对一个实体组进行操作,或者将另一个实体加入到该实体组中去。无论是查询,更新还是删除实体,如果一旦涉及到多个实体组,就会报错。同样的道理,在一个单一的Transaction中,不能涉及到多个实体有多个根节点的,因为一个根节点就等于代表了一个实体组。
如果在任何的tx.begin()和tx.commit()之间,有多个进程同时调用同样的实体组,那么JDO就会抛出JDODataStoreException或者JDOException异常,这是由java.util.ConcurrentModificationException造成的。解决的办法可以通过建立一个循环,多次进行重试来保证每个进程都能够成功。如果在一个进程之间,更新多个实体组,则会报JDOFatalUserException的异常。
Datastore的隔离级别在Transaction之外是最接近于READ_COMMITTED,在Transaction之内是SERIALIZABLE,特别指出是一种Snapshot Isolation。
我们在更新一个实体的部分值时需要使用Transaction,原因是我们在修改的同时可能也会有其他的进程在修改。另外一个需要使用的地方实在更新或者创建Key的时候,道理与上者类似,以避免多进程的同时操作。最后一个需要使用的地方是多个读操作发生的时候,为了保持数据的一致性而使用Transaction。
一个Transaction应该很快的进行,来减少发生冲突的机会从而减少重试的次数。尽可能的在Transaction之外把数据准备好,然后在Transaction内部进行执行需要保持数据一致性的操作。应该在Transaction之内来准备Key,和使用Key来获得实体。将datanucleus.appengine.autoCreateDatastoreTxns设置为false,则所有的Transaction都被禁用了。该文件的位置位于war/WEB-INF/classes/META-INF。
如果你的代码是以前根据关系数据库编写的,里面的Transaction极有可能是基于全局的,于是在GAE/J下必然会报错。一个好的解决办法是,首先禁用掉GAE/J中的所有的Transaction,然后一步步的操作来解决这个Transaction的问题。
This entry was posted on Friday, January 15th, 2010 at 4:01 pm and is filed under GAE . You can follow any responses to this entry through the RSS 2.0 feed. Both comments and pings are currently closed.



