另一种更先进的长期缓存的方法是通过将weblogic-ejb-jar.xml中的cache-between-transactions元素设置为 true来配置bean。这种情况下,只有客户端首先引用该bean或者事务被回滚时WebLogic Server才会调用ejbLoad()来从数据库中加载数据。
尽管从理论上说,您可以对除数据库并发之外的所有并发策略使用该方法,但是在实践中,只有对乐观并发使用该方法才有意义。当应用于只读并发时,该设置被忽 略,因为bean数据已经被缓存;当应用于排他性并发时,只有EJB具有对底层数据库的排他性访问时才起作用,而这是极少出现的情况。此外,当具有排他性 并发的bean被部署在集群中时,WebLogic Server自动禁用事务间的缓存,因为集群中的任何服务器都可能更新bean数据,并且没有用来在节点间同步或禁用缓存值的机制。这使得我们在进行长期 缓存时只有一种可行的并发策略:乐观并发。
如前所述,对于利用乐观并发策略部署的bean,WebLogic Server有一种内在机制,用来通过verify-columns检测底层数据变更。虽然乐观并发本身只能为数据库并发带来少量性能改善,但可利用事务 功能间的缓存提供更大的改善。如果在EJB缓存中bean实例已经可用的话,将cache-between-transactions设置为true将使 WebLogic Server忽略对数据库的调用。对于某些类型的应用程序(其中同一对象在短期内被不同事务多次访问),这可能导致显著的性能改善(在某些环境下,最多百 分之30到40)。自然地,既然我们使用的是乐观并发,您的应用程序必须做好在检测到并发冲突时处理 OptimisticConcurrencyException的准备。当 OptimisticConcurrencyException(RuntimeException的子类型)被抛出时,WebLogic Server 从缓存中丢弃一个EJB实例。注意,如果您将delay-updates-until-end-of-tx设置为true(默认),除非事务承诺否则就得 不到乐观异常,并且如果使用的是容器受控事务这将在应用程序代码之外。
与read-mostly模式(不提供通知集群中其他节点其中一个节点的数据发生变更的机制)相比,当具有乐观并发的bean被更新时,一个通知将被广播 给其他集群成员,并且缓存bean实例将被丢弃,以避免乐观冲突。由于WebLogic Server不广播数据变更本身,而是只广播某些种类的bean标识符,这种跨集群的缓存无效措施在提高性能和网络带宽利用率方面很有效。 WebLogic Server自动完成这种缓存无效工作,bean开发人员不需要再做其他配置。当对同一个bean发出下一个请求时,新鲜的数据将被从数据库中加载。
If the data in the database is updated by processes external to the server (or if you're using direct JDBC aclearcase/" target="_blank" >ccess from the server to modify the same tables as mapped to CMPs with long-term caching), then these processes should honor the contract on version columns. 换言之,如果实体bean被配置为使用数值版本列,那么外部进程应该在行数据更新时增加该值;如果使用了一个时间戳列,那么这个值应该被 更新为当前时间戳。如果不这样做会导致WebLogic Server覆盖数据,因为它的乐观检测机制不会触发异常,如果版本数据没有被更改的话。如果不可能通过修改外部进程来更新版本列,可用数据库触发器来实 现同样效果。如果不允许修改数据库模式,可对WebLogic Server进行配置,使其检查事务期间被读取的所有列或者只读取更新过的列(通过分别将verify-columns元素设置为Read或 Modified来实现)。注意,这种情况下,可能存在性能问题,因为生成的更新SQL语句更复杂。我建议进行测试,以确定这会对您具体环境中的更新造成 多大影响。
在事务间进行缓存提供了比上面讨论过的read-mostly模式更好的缓存数据模型。首先,并没有增加复杂度,比如部署同一bean的两个版本。另外, 对启动时间,以及当集群间bean发生变更时的自动缓存禁用等也没有造成影响。直到最近,WebLogic Server中与事务bean间缓存相关的一项特性还被忽视。没有公开的机制来有计划地使bean缓存无效。如果您对服务器上数据库进行排他性访问,这没 有什么大问题,但是在很多项目中,很少有这种情况,并且为了清空缓存,必须重启实例;同样,这也并不总是可能的。
让我们看一下如果一个bean用乐观并发部署并且当数据库中的数据被外部进程更新时被在事务间缓存,这时会发生什么。如果一个外部进程更新当前被容器缓存 的记录,然后应用程序通过CMP更新同样的列,那么会有两种可能的结果:如果外部进程在更新verify-columns时不遵守协定,那么就会出现更新 丢失的情况(来自CMP的更新覆盖外部进程对记录进行的修改)。另一方面,如果外部进程更新了版本列,或者bean被配置,以便用 Read/Modified列进行乐观控制,您就可能有一个OptimisticConcurrencyException。
在WebLogic Server中,OptimisticConcurrencyException是RuntimeException的一个子类,并且如果应用程序没有捕 获它,实体实例(以及调用同一事务中该实体的所有会话bean)就被丢弃,事务就被回滚;下一次,WebLogic Server会从数据库中重新加载数据,并且事务会成功完成。尽管缺乏“美感”,但这种方法对于使用队列(MOM)的应用程序来说还是很有效的;在事务回 滚时,该消息将保留在队列中,接着下一个重新交付尝试(如果有的话)会成功。值得再次一提的是,除非您的应用程序使用bean受控事务,否则您将捕获不到 OptimisticConcurrencyException,除非将delay-updates-until-end-of-tx设为false(非 默认值)。利用默认设置,WebLogic Server不能在数据库中执行实际的DML操作,并且操作会以RollbackException(它内部就是提到过的真正的 OptimisticConcurrencyException)异常而失败。