Jpa 无状态会话Bean-停止多个调用
因此,我从BalusC那里读到了关于如何阻止无状态会话bean在被JSF访问时不断地破坏数据存储(例如DB),JSF可能/将进行多次调用,因此我以我认为是基于我所看到的最佳实践的精神来实现我的代码问题 我的无状态会话bean如下所示:Jpa 无状态会话Bean-停止多个调用,jpa,ejb-3.1,session-bean,Jpa,Ejb 3.1,Session Bean,因此,我从BalusC那里读到了关于如何阻止无状态会话bean在被JSF访问时不断地破坏数据存储(例如DB),JSF可能/将进行多次调用,因此我以我认为是基于我所看到的最佳实践的精神来实现我的代码问题 我的无状态会话bean如下所示: @Stateless @Named("productsService") public class ProductService { private static boolean changed = true; private List<
@Stateless
@Named("productsService")
public class ProductService {
private static boolean changed = true;
private List<Product> products;
private long count;
@PersistenceContext(name = "myPU")
private EntityManager em;
@Inject
private Product product;
public ProductService() {
}
private void productRecordsFromDB() {
products = em.createNamedQuery("Product.getAll", Product.class).getResultList();
Object o = em.createNamedQuery("Product.getCount", Product.class).getSingleResult();
count = ((Long) o).longValue();
}
public void addProduct() {
synchronized (ProductService.class) {
changed = true;
em.persist(product);
}
}
public long countProducts() {
return count;
}
public void removeProduct(Product p) {
synchronized (ProductService.class) {
changed = true;
em.remove(em.merge(p));
}
}
public int removeAllProducts() {
synchronized (ProductService.class) {
changed = true;
return em.createNamedQuery("Product.deleteAll").executeUpdate();
}
}
public List<Product> getProducts() {
synchronized (ProductService.class) {
if (changed) {
productRecordsFromDB();
changed = false;
}
return products;
}
}
public Product getProduct() {
return product;
}
public void setProduct(Product p) {
product = p;
}
}
编辑:我已经将synchronized块添加到相关部分,以确保串行访问,尽管这现在感觉更像是一个单例。我仍然很想知道其他人是如何处理与多个数据存储调用相关的问题的
具体地说,我已经创建了一个“dirty”标志,该标志被选中,如果数据库已经更新,则通过更新数据库将dirty标志设置为true。在检测到脏标志后,它被设置回false,因此只对DB进行一次调用。因此,不会发生DB抖动
我的问题是:我所做的,这是一个解决方案的合适的最佳实践,还是有一个我不知道的更聪明的方法?我在考虑旧的“J2EE”蓝图设计模式,以及Java EE 6中可能缺少的注释。您有两个主要问题: 状态在线程之间共享,无需使用锁进行保护 无状态bean的状态对客户端可见。 您可以通过静态变量在ProductService的所有实例之间共享状态。但是,对此变量的读取和写入不同步。因此,不能保证实例对已更改的对象共享相同的值。您必须通过与同一个锁同步的锁来控制访问,或者至少使其不稳定,以确保您将看到最后一个写入的值 下一个问题是无状态bean的状态不应该对客户端可见。这就是为什么他们被称为无国籍。对于客户端来说,哪个实例处理调用应该没有区别,因为客户端无法控制调用哪个实例。例如,考虑以下场景实例s1和s2,假设两者都处于新状态: s1.getProducts=>changed=false s2.getProducts=>因为已更改 如果为false,它将向实例变量返回null NON one set值 s2产品 对编辑的问题和评论的答复: 在无状态与状态之间仍然存在相同的问题。无状态用于没有状态的解决方案不适合的情况。正如您从下面看到的,哪个实例将处理业务方法调用仍然很重要: s1.getProducts=>changed=false; s2.getProducts=>因为changed为false,所以返回null 请遵循Arjan Tijms提供的方法,或者至少遵循相同的精神 这是解决方案的合适最佳实践,还是有一种我不知道的更聪明的方法 不幸的是,构建会话bean的方式不是最佳实践。事实上,正如Mikko所解释的,它完全不符合会话bean的正常工作方式。所以,尽管你做出了明显的努力,我恐怕你创造的是一个坏习惯的主要例子。它几乎完成了在无状态会话bean中不应该做的所有事情 为了解决BalusC概述的问题,您可以使用一个单独的支持bean来绑定视图,而不是让它直接绑定到服务。然后,这种支持是在请求期间或视图范围期间缓存结果的bean 例如 服务:
@Stateless
public class ProductService {
@PersistenceContext(name = "myPU")
private EntityManager em;
public List<Product> getProducts() {
em.createNamedQuery("Product.getAll", Product.class).getResultList();
}
// ...
}
然后,如果支持bean使用CDI,则通过例如CODI添加@ViewScope注释:
我把服务调用放在这里是@PostConstruct,但是根据您的具体要求,这当然也可能是在操作方法中,或者通过getter中的延迟加载模式。这是我很久以来看到的最严重的无状态会话bean滥用 您已经创建了一种单例,但是在一个绝对不适合这种情况的bean中,全局和本地操作的关注点混合在一起,并且通过在类文本上同步,存在死锁的风险
如果您需要一个单例,您应该使用专用的@Singleton bean,但是如果您想防止在请求过程中重复来自JSF的调用,请使用支持bean,如另一个答案中所示。有一个具体的应用程序将EJB作为单例。EJB3.0标准允许对单个会话bean进行分时同步访问。也就是说,您可以使用以下选项来防止/控制会话缓存 1A单例EJB可以维护数据库结果的本地缓存副本,该副本使用EJB3.0计时器bean按计划刷新,这是一个非常简单的例子。然后,您可以将这个单例注入到您喜欢的任何上下文中,如JSF托管bean、CDIBean等 2或者,您的特定用例可以缓存它自己的所需数据的本地副本,该副本在bean中读取一次 尽管此范例比@RequestScoped bean更适用于寿命更长的bean,但它是@PostConstruct
3与2相关,有一个@ApplicationScoped bean,它的功能类似于上面的1,但现在维护着大量的各种列表将'changed'变量更改为instance将是不好的,我认为,因为如果有合并的bean,一个可以更新,但其他bean不会看到更新,除非他们也进行了更新。因此,同步的静态变量应该解决这个问题,不是吗?不管哪个实例处理它,因为对DB调用的访问是在类级别同步的。我真的很困惑,每个回复的人似乎都在关注设计模式的滥用,而不是解决数据库混乱的问题。我可以使用一个请求范围的bean,它调用一个无状态会话bean,但仍然不能解决数据库抖动的问题。如果其他实例返回null(如示例中您没有控制权的实例)是s2.products填充,而另一个实例返回填充列表,在这种情况下,选择哪一个实例来处理您无法控制的调用并不是问题,那么我对您的问题理解不够,无法提供进一步的答案。我希望您能找到解决方案。不幸的是,您所发布的内容并没有解决问题,即当JSF组件调用数据库时,停止对数据库的破坏。您只是创建了另一个类并将问题转移到其中。您为什么这么认为?这不仅仅是另一门课。对于每个请求或每个视图范围,数据库只被查询一次,每次JSF调用getter时,缓存的产品都被返回。这是一个非常便宜的呼叫,不涉及数据库访问。我有效地将这种缓存从一个全局位置移动到一个本地位置,但是如果一个JSF组件多次调用这种缓存,这是由于JSF的生命周期,那么抖动问题就没有得到解决。在其中一个类的某个地方,需要一个布尔变量来跟踪数据库是否已更新。理论上,这应该由JPA处理。问题在于,每个提供程序都有自己的方法来处理基于persistence.xml文件中的属性或使用@QueryHint的缓存L1和L2。问题是,这些都是提示,并没有更多。@CodesNChaos这根本不是巴卢斯的意思。数据库是否更新与此无关。问题是,在单个请求期间,JSF通常会多次查询属性。如果没有我概述的模式,每次这样的咨询都会导致DB调用。这不是你想要的。您也不希望数据在生命周期处理过程中的任意点发生更改。因此,在整个生命周期中,以及在处理回发时,数据实际上应该保持不变。我在这里使用它是因为默认情况下CDI没有视图范围。我认为DB抖动(trashing)意味着许多冗余调用通过我提供的解决方案得到解决。当然,如何处理它是你自己的决定。祝你好运!但你却没有提供任何解决方案。我没有那么多的街头信誉,所以我不能否决你的答案。我能说的就是我觉得你的回答完全没有用。你问我你所做的是否是最佳实践伙伴,我回答不是。很抱歉,如果你不喜欢这个答案。不幸的是,你没有提供一个解决方案,你伴随着弱trolling。否则我会忽略这一点。
@ViewScoped
@ManagedBean
public class SomeBacking {
private List<Product> products;
@EJB
ProductService productService;
@PostConstruct
public void init() {
products = productService.getProducts();
}
public List<Product> getProducts() {
return products;
}
}