下面基于Cactus的测试验证了该行为。假定一个Person CMP bean是利用乐观并发部署的,并且cache-between-transaction被设置为true:
... public class OptimisticLockingTest extends ServletTestCase { private UserTransaction tx; protected void setUp() throws Exception { super.setUp(); Context context = new InitialContext(); this.tx = (UserTransaction)context .lookup("javax/transaction/UserTransaction"); }
public void testCacheBetweenTransactions() throws Exception { PersonLocalHome localHome = (PersonLocalHome)Locator .getLocalHome(PersonLocalHome.JNDI_NAME); // create record via CMP in first transaction this.tx.begin(); PersonLocal local = localHome.create(); local.setName("John"); Long pk = local.getPrimaryKey(); this.tx.commit(); // update some field(s) via direct JDBC call in another // transaction. Assume that updatePersonNameViaJdbc() // method will update version column as well String newName = "Paul"; this.tx.begin(); updatePersonNameViaJdbc(pk, newName); this.tx.commit(); // find CMP again and try to update name in yet // another transaction this.tx.begin(); local = localHome.findByPrimaryKey(pk); // code doesn't see changes made directly // because bean instance was cached between transactions assertFalse(newName.equals(local.getName()); try { // this should throw optimistic concurrency // exception (on commit) local.setName("George"); this.tx.commit(); fail("expected OptimisticConcurrencyException not thrown"); } catch (RollbackException expected) { // unfortunately there seems to be no better way to // assert that underlying exception was // an OptimisticConcurrencyException assertTrue("Unexpected exception type: "+expected, expected.getMessage() .indexOf("Optimistic concurrency violation") > 0); } } } ... |
我希望您同意对长期缓存的失效进行控制是有益的。随着它的出现,甚至出现了WebLogic 7.0和8.1的解决方案。与可用于为只读bean清空缓存的CachingHome/CachingLocalHome接口类似,一个 EntityEJBHome和一个EntityEJBLocalHome,再加上同系列的invalidate()方法,使应用程序能让特定实体bean 的所有缓存或者一部分缓存无效。WebLogic Server中的任何CMP本地接口都可转换为EntityEJBLocalHome。利用前面的例子,我们可以在 updatePersonNameViaJdbc()方法调用后插入下面的代码:
... // flush cache assertTrue("PersonLocalHome not instance of EntityEJBLocalHome: "+ localHome, localHome instanceof EntityEJBLocalHome); EntityEJBLocalHome entityEJBLocalHome = (EntityEJBLocalHome)localHome; entityEJBLocalHome.invalidate(pk); ... |
现在当下一次调用findByPrimaryKey()时,bean实例将被从数据库中重新加载,并且所有一切变得更好。除了invalidate()方法,还有invalidateAll()和invalidate(Collection)方法。
WebLogic Server 9.0中的改进
在WebLogic Server 9.0中,对使用乐观并发的缓存bean进行的显式缓存禁用被归档,并且与只读bean一致。(比如,bean home或远程主接口可被缓存到上面调用的CachingHome或CachingLocalHome和invalidate()方法中)。此外,read-timeout-seconds参数适用于用乐观并发部署的bean。开发人员还对集群中的bean实例无效化有更多的控制。默认情况下, 当在一个集群中部署具有乐观并发策略的bean,并且该集群的一个成员更新该bean时,WebLogic Server会试图使该集群的所有节点中的bean的所有副本无效。该无效化使您避免了乐观并发故障,但是会影响性能,因为它是一项资源密集型操作。可通过在weblogic-cmp-jar.xml中将cluster-invalidation-disabled设置为true来防止EJB容器使集群中的bean副本无效。