EJB 3.1 单例 + JPA + JSF 设计建议需求

5

假设有一个简单的JSF网页应用程序(没有Seam),其中JSF bean调用了几个EJB,这些EJB又加载并保存JPA实体。我想使用@Singleton注释来替代EntityManagerFactory,并注入EntityManager

@Singleton
public class MyEJB {
  @PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
  protected EntityManager em; // not EntityManagerFactory
}

规范说明@Singleton是线程安全的,支持并发和事务属性,这使其可以安全地从JSF bean中调用。我还期望由于EntityManager不会为每个调用重新创建,并且具有内部缓存能力,因此也会带来性能上的好处。
在这里,我的主要关注点是在拥有多个单例对象的情况下对JPA实体进行创建/更新操作,从而导致存在相同数量的长期存在的EntityManagers。
  • 如果一个单例对象更新了JPA实例,那么这些更改如何传播到其他单例对象?
  • 由于我无法关闭entity manager,在每次实体更新时是否需要刷新它?
  • 如果这几个单例对象共享同一个entity manager,会更好吗?
  • 我只看到了很少的这种设计示例。为什么? 是否有任何严重的缺点?
非常感谢!
2个回答

5
我预计由于EntityManager不会为每次调用重新创建并具有其内部缓存能力,因此也会带来性能上的好处。
您可能会节省一些内存使用单例模式,但是在应用程序中随处使用它可能会使其变慢,因为只有一个EJB可以为应用程序的各个用户的所有并发请求提供服务,容器锁定对EJB的访问,当它忙于处理一个请求时,它不能处理另一个请求。但是,可以使用锁类型(即@Lock(WRITE)@Lock(READ))在某种程度上缓解这种情况。
单例模式对于想要使用EJB定时器定期执行代码或定期更新缓存等情况非常有用。
如果一个单例模式更新JPA实例,那么其他单例模式如何传播这些更改?
应该与非单例EJB的行为没有任何区别。
由于我无法关闭实体管理器,因此每次更新实体时需要刷新它吗?
如果使用CMT,则不需要,在每个事务结束时,所有内容都将自动刷新。
如果这几个单例模式共享同一个实体管理器,是否会更好?
看起来对我来说过早进行了优化。只需让容器为您注入EM即可。
我只看到了很少的这种设计示例。为什么?有任何严重的缺点吗?
已经解释过了。

这个答案很好,但如果您提到注入的EM实际上是每个事务EM的代理,则可能会消除混淆。请参阅http://stackoverflow.com/questions/7010470/ejb-3-1-transaction-entitymanager/7017915#7017915 - Brett Kail
Behrang,特别感谢@Lock。对于只读操作,性能显著提高,在我的情况下快了5倍。 - andbi

0

关于更改Singleton EJBs的LockType,有一件事我想提醒。虽然从一般情况来看这听起来是个不错的主意,但你应该记住,像EntityManger这样的资源是非线程安全的,因此需要提供适当的并发访问控制。你可以使用@Lock(WRITE)注解标记访问非线程安全资源的方法,但如果你的Singleton EJB的几乎所有接口方法都访问这些资源,那么你将面临与完全写锁定相同的情况。另一个选择是使用Bean并发管理和手动细粒度同步,但这也是一个值得考虑的决定。

正因为如此,总体上我更喜欢Stateless EJBs而不是Singleton,并只在特定情况下使用后者。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接