JPA / Eclipselink缓存寿命

时间:2010-08-12 23:32:21

标签: java orm jpa caching eclipselink

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个实体来更新需要它的那些实体?

3 个答案:

答案 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开发者无法管理同样的壮举?我们所做的只是将其向上游移动,并将数据表示为对象而不是元组和页面。