1.-我正在使用EcipseLink 2.0.0使用Glassfish 2.1,所以真的使用JPA 1.0规范,我有一个无状态EJB,可以找到实体等。据我所知,JPA 1.0定义了一个在缓存上下文级别(无状态EJB的事务级别)工作的L1缓存,但我无法弄清楚为什么下一个代码打印“Not same instance”(如果它在同一个事务中)。
@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class EntityServiceBean implements EntityServiceLocal {
@PersistenceContext(unitName = "Model")
private EntityManager entityManager;
@Override
public <T> T find(Class<T> type, Object id) {
T entity = entityManager.find(type, id);
if(entity != entityManager.find(type, id)) {
System.out.println("Not same instance");
}
return entity;
}
....
}
我甚至试过了这个属性:
<property name="eclipselink.cache.type.default" value="Full"/>
persistence.xml文件中的,但也是如此。
2.-如果可能,我真正希望实现的是对无状态EJB的多次调用返回相同的实例,换句话说,跨越事务和使用无状态EJB的持久性上下文的JPA缓存生命,例如:
... // POJO class
EntityServiceLocal entityService = ...
Product pA = entityService.find(Product.class, 1l);
...
Product pB = entityService.find(Product.class, 1l);
System.out.println("Same instance?" + pA == pB); // TRUE
我读到很多JPA实现都使用了一个L2缓存(现在在JPA 2.0中定义),即使使用JPA 1.0,它也可以跨越多个持久性上下文,但我不知道是否误解了L2缓存概念和/或我错过了任何配置。
这可能吗?或者我该怎么做才能避免每分钟从数据库中读取超过20k个实体来更新需要它的那些实体?
答案 0 :(得分:4)
我正在使用EcipseLink 2.0.0使用Glassfish 2.1,所以真正使用JPA 1.0规范,我有一个无状态EJB,可以找到实体等。据我所知,JPA 1.0定义了一个在缓存上下文级别(无状态EJB的事务级别)工作的L1缓存,但我无法弄清楚为什么下一个代码打印“Not same instance”(如果它在同一个事务中)。
THIS 非常奇怪,应该在Java EE上下文中的事务内部保持对象标识。这在JPA wiki book:
中有很好的记录Object Identity
Java中的对象标识意味着两个 变量(x,y)指的是相同的 逻辑对象,然后x == y返回 真正。意思是两者都引用了 同样的事情(两者都指向同一个 记忆位置)。
在JPA中维护对象标识 在交易中,(通常) 在同一个EntityManager中。该 例外是在JEE管理中 EntityManager,对象标识只是 维持在交易内部。
所以JPA中的情况如下:
无论如何,这都适用 访问对象:Employee employee1 = entityManager.find(Employee.class, 123); Employee employee2 = entityManager.find(Employee.class, 123); assert (employee1 == employee2);
Employee employee1 = entityManager.find(Employee.class, 123); Employee employee2 = employee1.getManagedEmployees().get(0).getManager(); assert (employee1 == employee2);
在JPA中,对象标识不是 在整个EntityManagers中维护。每 EntityManager维护自己的 持久化上下文,以及它自己 其对象的事务状态。
所以JPA中的情况如下:
EntityManager entityManager1 = factory.createEntityManager(); EntityManager entityManager2 = factory.createEntityManager(); Employee employee1 = entityManager1.find(Employee.class, 123); Employee employee2 = entityManager2.find(Employee.class, 123); assert (employee1 != employee2);
对象标识通常很好 事情,因为它避免了你的 应用程序管理多个副本 对象,并避免应用程序 改变一个副本,但不改变另一个副本。 不同的EntityManagers或者 交易(在JEE中)不维护 对象标识就是每一个 交易必须隔离其变化 来自系统的其他用户。这个 然而,通常也是一件好事 它确实需要应用程序 了解副本,分离的对象和 合并。
某些JPA产品可能有一个概念 只读对象,在哪个对象中 可以保持身份 EntityManagers通过共享对象 高速缓存中。
我无法在Java SE环境中重现EclipseLink 2.0的问题(在事务中和EntityManager
内) - 抱歉,我不会在GF 2.1下测试。
我甚至尝试使用
<property name="eclipselink.cache.type.default" value="Full"/>
文件中的属性persistence.xml
,但是做了相同的
没有什么可以“激活”L1缓存。
如果可能的话,我真正希望实现的是对无状态EJB的多次调用返回相同的实例,换句话说,跨越事务和使用无状态EJB的持久化上下文的JPA缓存生命(...): / p>
L2缓存确实是跨越多个事务的缓存,并且大多数JPA提供程序都支持EntityManagers和L2缓存。但是,虽然L2缓存会减少数据库命中,但并不能保证所有提供程序都能保证对象标识。
例如,对于Hibernate,默认情况下不启用L2缓存,并且您不会获得对象标识,因为Hibernate不会将实体本身放入缓存中。
使用EclipseLink,默认启用L2 cache,您将获得object identity depending on the cache type。默认值为SOFT-WEAK cache of size 100,它确实保留了对象标识。虽然您可以非常精细地配置(直到实体级别),但是对于distributed environment,可以默认配置。
答案 1 :(得分:1)
您设置了哪些其他设置,以及如何映射您的课程?
同一事务中的两个发现应该始终是同一个实例。
如果您标记对象或查询为只读,则可以让find()跨事务边界返回相同的实例 (@ReadOnly,“eclipselink.read-only”=“true”)。您需要确保以只读方式使用它。尝试允许更新由不同事务共享的同一实例是不可能的,也不是一个好主意。
答案 2 :(得分:-3)
我们发现Eclipselink L2实体缓存似乎根本没有做它应该做的事情。我们从数据库中保留L2缓存的对象相对较少,但是我们的内存使用量在24小时内失控,直到我们用完堆(4GB)。我已经分析了应用程序,并确认内存使用量来自Eclipselink的L2缓存。我们正在使用SOFT-WEAK,并且它根本不会撤消旧对象(文档建议它应该做的事情),并创建多个属性相同的对象实例(文档建议它是不应该这样做,这是一个善意的只读应用程序。我们在集群环境中使用任何形式的缓存协调都是完全不成功的。我们有三个高级Java开发人员,每个人都有超过十年的经验,其中一个实际上在IBM工作的Java实现仍然没有成功实现任何缓存协调功能,在最后一个过程中花时间定期解决问题六个月甚至可以追踪Eclipselink来源,找出问题所在。每24小时重新启动我们的应用程序非常烦人,而我现在就完成了L2缓存。我只希望没有它我们可以获得可接受的性能,至少Eclipselink JPA 1实现可以设法做并行读取,这似乎已经在JPA 2中消失了。数据库实现者多年前用MVCC解决了这个问题,为什么它是一套智能鉴于它已经完成,Java开发者无法管理同样的壮举?我们所做的只是将其向上游移动,并将数据表示为对象而不是元组和页面。