扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
对于有状态会话 Bean,是否存在非常有价值的情况呢?
亲爱的 EJB 倡导者:
似乎除了有状态会话 Bean 之外,您已经为所有的 EJB 组件类型找到了合适的用途。有状态会话 Bean 是“最差”的实践或其他的什么情况吗?
署名:
Is Something Rotten in the State?
|
其主要用途是在多个事务之间管理状态信息
亲爱的 Something Rotten:
从您的来信中我可以推断出,您曾阅读过我以前的专栏,并且知道我的格言“没有不好的模式,只有不好的模式应用”。这句话说明,对于有些组件,比如有状态会话,仅适用于很少的场合,但是在这些情况下,它们可能是最简单并且最有效的解决方案,并且在这些情况下,使用其他更常见的组件,比如无状态会话,可能会更加复杂,并且是更糟糕的解决方案。
例如,假设您有一个应用程序,它必须遍历数据库中大量行。说得具体一点,假设您需要重新计算手头每件库存产品的价格,并计算其总和。您可能希望一次处理给定数目的行(或者在某个特定的时间段内),这样一来,就不会出现事务超时或因为在单个事务中占用过多的资源而出现内存溢出的情况。
使用有状态会话 Bean,客户端的逻辑业务可能非常简单,如下所示:
RevaluationServiceHome home = (RevaluationServiceHome)initCtx.lookup( "java:comp/env/RevaluationService" ); ReValuationService service = home.create(); while (service.revalueNextBlock(50)) {} double totalValue = service.getTotalValue(); service.remove(); |
执行了这段代码之后,对所有的行进行了更新,并设置了 totalValue。因为每个方法在其自己的事务中执行,所以您不需要关心如何管理客户端事务。仅有一件棘手的事情需要记住,在完成之后,需要调用会话的 remove() 方法,特别是对于远程的会话、或者是在循环中调用这段代码的时候。其原因是,每次在调用 create 方法的同时,创建了一个新的实例。
有状态会话 EJB 组件的实现同样可以非常简单:
public class RevaluationService implements SessionBean { double totalValue = 0.0; transient Iterator inventory = null; public void ejbCreate() { InventoryHome home = (InventoryHome)initCtx.lookup( "java:comp/env/Inventory" ); inventory = home.findAll().iterator(); totalValue = 0.0; } public boolean revalueNextBlock(int count) { for (; count > 0; count--) { if (inventory.hasNext()) { Inventory item = (Inventory)inventory.next(); totalValue += item.revalue(); } else { return false; } return true; } public double getTotalValue() { return totalValue; } public void ejbActivate() {} public void ejbPassivate() {} public void ejbRemove() {} } |
假设有一个名为 Product 的实体 EJB 组件,其中具有一个用来计算并返回价格的方法。但它是分离关注点的关键,我们在这些专栏中讨论过这个问题。
希望这对您有所帮助。
好的,就先到此为止,
您的 EJB 倡导者
|
利用激活和钝化瞬态变量来启用池机制
亲爱的 EJB 倡导者:
这确实有所帮助。我思考了很久,如何能够在不使用有状态会话 EJB 组件的情况下完成相同的任务。
但是当我看到您为名为“inventory”的 Iterator 使用 transient 关键字的时候,我便意识到这段代码无法工作,因为您没有实现 ejbActivate() 和 ejbPassivate() 方法。我的理解是,当会话 EJB 池中分配的实例数目达到最大值,并且创建了一个新实例的时候,某个不幸的活动实例将会被钝化。然后,当某个不活动的实例的下一个事务到来时,再将其激活。
如果在您的实现中不对实例进行钝化,那么“inventory”Iterator 将为空,或者更糟,具有来自另一个对话的未定义的值。
我认为您应该在 ejbPassivate() 中将当前位置保存到一个变量中,并重新启动 ejbActivate() 中的查询,如下所示:
public class RevaluationService implements SessionBean { double totalValue = 0.0; InventoryKey lastKey = null; transient Iterator inventory = null; transient Inventory item = null; public void ejbCreate() { InventoryHome home = (InventoryHome)initCtx.lookup( "java:comp/env/Inventory" ); inventory = home.findAll().iterator(); totalValue = 0.0; last = null; } public boolean revalueNextBlock(int count) { for (; count > 0; count--) { if (inventory.hasNext()) { item = (Inventory)inventory.next(); totalValue += item.revalue(); } else { return false; } return true; } public double getTotalValue() { return totalValue; } public void ejbActivate() { InventoryHome home = (InventoryHome)initCtx.lookup( "java:comp/env/Inventory" ); inventory = home.findAfter(lastKey).iterator(); } public void ejbPassivate() { if (item == null) { lastKey = null; } else { lastKey = item.getPrimaryKey(); } } public void ejbRemove() {} } |
署名:
Not Feeling So Rotten About State Anymore
|
始终让实现适应设计
亲爱的 Not So Rotten:
我不得不承认我的实现在具有多客户端的远程环境中,并不像您的代码那样合适。然而,您是否考虑到了我的场景中的具体情况,即在单个“会话”中处理整个数据库(从创建到删除)。在设计过程中必须考虑到,可能每台服务器仅有一个客户端(为了实现最好的性能,会话 Bean 可能与数据库共存)。
我还要承认的是,对于这些设计中的假设,如果能够在 ejbActivate() 和 ejbPassivate() 方法中引发某种系统异常,以便在失败时提供更好的错误消息,而不是您在碰到空指针时所获得的消息,那么这样做可以使得系统更加健壮。
但是为什么我正感觉到是您在引导着我呢?也许您就是将来撰写本专栏的人选!
顺便说明一下,明年我将要从事新的工作,可能很少有时间撰写关于 EJB 的专栏了,所以在短期之内,这将是我这个专栏的最后一期。感谢两年以来曾阅读和评论过这些专栏的所有读者。我非常高兴能够作为:
您的 EJB 倡导者
好的,就先到此为止,
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者