OpenJPA (6)

8 Object Locking
8.1 Configuring Default Locking
    如何使用lock对load时的性能有重要的影响。OpenJPA通过openjpa.ReadLockLevel和openjpa.WriteLockLevel来配置缺省的事务读写lock level。这些缺省配置只适用于非乐观事务;在乐观事务中,OpenJPA缺省不进行lock。在尝试获取lock时,可以通过openjpa.LockTimeout配置最长的等待时间(缺省值-1指定没有限制),超过这个时间后OpenJPA会抛出异常。配置方式如下:

<property name="openjpa.ReadLockLevel" value="none"/>
<property name="openjpa.WriteLockLevel" value="write"/>
<property name="openjpa.LockTimeout" value="30000"/>

 

8.2 Configuring Lock Levels at Runtime
    在每个事务开始时,OpenJPA初始化EntityManager的缺省lock levels和time out。在运行时可以通过EntityManager的FetchPlan 接口修改这些配置。也可以通过Query相关的fetch plan修改Query级别上的配置。这些运行时的修改可以在乐观事务中使用,但是不能在事务之外进行锁定。以下是个简单的例子:

// load stock we know we're going to update at write lock mode
em.getTransaction().begin();
Query q = em.createQuery("select s from Stock s where symbol = :s");
q.setParameter("s", symbol);

OpenJPAQuery oq = OpenJPAPersistence.cast(q);
FetchPlan fetch = oq.getFetchPlan ();
fetch.setReadLockMode(LockModeType.WRITE);
fetch.setLockTimeout(3000); // 3 seconds
Stock stock = (Stock) q.getSingleResult();

// load an object we don't need locked at none lock mode
fetch = OpenJPAPersistence.cast(em).getFetchPlan();
fetch.setReadLockMode(null);
Market market = em.find(Market.class, marketId);

stock.setPrice(market.calculatePrice(stock));
em.getTransaction().commit();

 

8.3 Lock Manager
    OpenJPA 内部使用org.apache.openjpa.kernel.LockManager 处理locking相关的实际工作。可以通过openjpa.LockManager 属性进行配置,它有以下选项。

 

8.3.1 pessimistic
    这个选项是org.apache.openjpa.jdbc.kernel.PessimisticLockManager 的一个别名。它使用SELECT FOR UPDATE (或其它等效的)语句锁定跟entity实例对应的数据库行,并且不区分read locks和write locks,也就是说所有的locks都是write locks。例如:

<property name="openjpa.LockManager" value="pessimistic(VersionCheckOnReadLock=true,VersionUpdateOnWriteLock=true)"/>

 

8.3.2 none
    这个选项是org.apache.openjpa.kernel.NoneLockManager的一个别名。它不进行任何锁定。例如:

<property name="openjpa.LockManager" value="none"/>

 

8.3.3 version
    这个选项是org.apache.openjpa.kernel.VersionLockManager的一个别名。它不进行排他锁定;相反,在事务结束时,它通过校验version来确保被read locks锁定的对象的一致性。无论被write locks锁定的对象是否被修改,其version都会被累加。为了避免脏读,事务的隔离级别应该至少为"read committed"以上。

 

8.4 Rules for Locking Behavior
    OpenJPA的隐含锁定行为有以下规则:

  1. 在事务中,当第一次读取某个对象的persistent state的时候,OpenJPA使用fetch plan中当前的read lock level锁定这个对象。未来对这个对象上的lazy persistent state的读取也采用相同的read lock level(无论fetch plan中的lock level是否改变)。
  2. 在事务中,当第一次修改某个对象的persistent state的时候,OpenJPA使用该对象第一次被读取时的write lock level(无论fetch plan中的lock level是否改变)。如果对象在之前没有被读取过,那么使用当前的write lock level。
  3. 当使用persistent relation field 访问某个对象的时候,这个对象在load过程中被当前fetch plan中的lock level锁定,而不是持有这个field的对象所"记住"的那个lock level。
  4. 在事务中,每次访问某个对象的时候,这个对象会被当前的read lock level重新锁定,并且这个read lock level会被该对象"记住"(规则1,2)。
  5. 如果显式地通过locking APIs锁定某个对象,那么这些操作都是再次锁定,并且这个lock level会被该对象"记住"(规则1,2)。
  6. 如果某个对象已经被锁定,那么尝试使用低级别的lock level再次锁定该对象会被忽略,也就是说在事务中,lock level不能被降低。 

8.5 Known Issues and Limitations
    出于性能的考虑和数据库的限制等,locking有以下限制:

  • 在乐观事务中,OpenJPA通常会直到flush或commit前才真正开始事务。当使用pessimistic lock manager时,OpenJPA必须在乐观事务中锁定某个对象时开始事务。这因为pessimistic lock manager需要使用数据库的lock。此时OpenJPA会以INFO级别在openjpa.Runtime category中输出一条日志。
  • 出于性能的考虑,OpenJPA只保证在从datastore中得到了某个对象的persistent state之后才进行锁定。这意味这其它事务可能在锁定前修改其persistent state。可以通过在锁定后refresh这个对象来确保这个对象被成功锁定。当使用pessimistic lock manager的时候,这个情况只在无法使用SELECT FOR UPDATE(或其它等效的)语句的时候,例如某些数据库不支持SELECT FOR UPDATE中使用join。此时OpenJPA会以INFO级别在openjpa.Runtime category中输出一条日志。

 

9 Enhancement
    OpenJPA使用enhancer来支持运行时性能优化、延迟加载、脏检查等功能。被enhancer修改过的字节码是Java debugger兼容的,而且很好地保留了stack trace中的行号。唯一的需要注意的是,如果采用property access,那么修改后的getter和setter方法名,在stack track中或者step-through时,会加上"pc"前缀。

 

9.1 Enhancing at Build Time
    Enhancer可以在build的时候被调用。如果对已经加强过的class再次进行加强,那么不会对class文件做更多的修改。可以在命令行上通过PCEnhancer类进行加强,例如:

java org.apache.openjpa.enhance.PCEnhancer Magazine.java

    以下是几个可选的命令行参数: 

  • -directory/-d <output directory>: 输出的class文件的路径。
  • -enforcePropertyRestrictions/-epr <true/t | false/f>: 如果entity使用property access,但是却没有遵守相关限制的时候,是否抛出异常。缺省是false。
  • -addDefaultConstructor/-adc <true/t | false/f>: JPA规范要求所有的persistence class必须提供一个无参构造函数。这个标志指示enhancer在persistence class没有提供无参构造函数的时候,是否创建一个protected型的无参构造函数。缺省是true。
  • -tmpClassLoader/-tcl <true/t | false/f>: 是否使用临时classloader加载persistence class。 缺省是true。

9.2 Enhancing on Deployment
    在entities被部署到container的时候,Java EE 5 规范包含hooks来自动加强这些entities。 因此,如果使用Java EE 5兼容的应用服务器,OpenJPA会自动在运行时加强entities。如果某些entites使用了build时的enhancement,那么OpenJPA运行时enhancer会识别并略过这些已经加强过的entities。

9.3 Enhancing at Runtime
    在类被载入到JVM的时候,可以使用OpenJPA agent来加强被载入的persistence class。OpenJPA agent是在应用的main方法执行之前被调用的classes。OpenJPA agent使用JVM hooks来拦截并加强被载入的包含persistence metadata的类。在每一个被载入的类中查找persistence metadata可能会减慢应用的初始化速度。可以设置persistent class list来指示agent只查找包含在persistent class list中的类。以下是使用OpenJPA agent的几个例子:

java -javaagent:/home/dev/openjpa/lib/openjpa.jar com.xyz.Main
java -javaagent:/home/openjpa/lib/openjpa.jar=addDefaultConstructor=false com.xyz.Main

    可以使用OpenJPA's plugin syntax来为agent传递配置。此外也支持以下的选项: 

  • addDefaultConstructor: 输出的class文件的路径。
  • enforcePropertyRestrictions: 如果entity使用property access,但是却没有遵守相关限制的时候,是否抛出异常。缺省是false。
  • scanDevPath: 是否检查classpath来查找persistence class。如果没有指定persistent class list,同时设置这个标志为true,那么OpenJPA会检查每一个被载入到JVM的类。
  • classLoadEnhancement: 指定是否使用load-time class enhancement。缺省是true。
  • runtimeRedefinition: 指定是否使用class redefinition。缺省是true。

9.4 Omitting the OpenJPA enhancer
    OpenJPA并不要求必须运行enhancer。如果没有运行enhancer,OpenJPA使用以下几种可能的替代方法。

9.4.1 Java 6 class retransformation
    如果使用Java 6,那么OpenJPA会自动检测并尝试动态注册ClassTransformer来重定义persistence class。同时OpenJPA也会为persistence class创建子类。当执行query或者遍历关系的时候,OpenJPA会返回子类的对象,因此instanceof操作符仍然会正确工作,但是o.getClass() 会返回子类类型。

9.4.2 Java 5 class redefinition
    如果使用Java 5,并指定了OpenJPA agent,那么OpenJPA会使用Java 5 class redefinition 来重定义没有被agent加强的persistence class。由于agent缺省会进行加强,因此这只有在为agent设置classLoadEnhancement 为false的时候有效(或者其它特殊情况)。

9.4.3 State comparison and subclassing
    在以上情况外,OpenJPA会创建persistence class的子类。然而在有些情况下,OpenJPA无法在访问psersistence state的时候得到通知。
    如果使用property access,那么OpenJPA会为从数据库中查询得到的对象自动创建子类,并返回这个子类作为代理。这个子类中的getter和setter方法中增添了额外的代码以便通知OpenJPA所有对persistence state的访问。对于你自己创建的对象,由于无法使用子类代理,因此会在脏检查的时候使用state比较的方式。在这种方式下要额外保存该对象的一个snap shot以便用于比较。
    如果使用field access,那么OpenJPA无法跟踪对psrsistence state的访问。因此OpenJPA会在脏检查的时候使用state比较的方式,代价是性能上的降低和内存使用的增加。此外,single-valued fields (one-to-one, many-to-one, and any other non-collection or non-map field that has a lazy loading configuration)上的延迟加载的相关配置也被忽略,因为OpenJPA无法跟踪最这些field的访问。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值