【问题标题】:How to get a single instance for an associated read-only entity with JPA and Hibernate?如何使用 JPA 和 Hibernate 为关联的只读实体获取单个实例?
【发布时间】:2019-07-18 08:10:49
【问题描述】:

我有这两个实体:

@Entity
@Immutable
@Cacheable
@Cache(region = "dosefrequency", usage = CacheConcurrencyStrategy.READ_ONLY)
public class DoseFrequency{
   .....
} 

@Entity
public class PrescriptionDrug {

     .....
    @ManyToOne(optional=false)
    @JoinColumn(name="doseFrequency")   
    public DoseFrequency getDoseFrequency() {
        return doseFrequency;
     }

}

DoseFrequency 是一个只读实体,PrescriptionDrug 关联了一个 DoseFreqyency。我希望每次加载一个或多个 PrescriptionDrug 时都不会重复 DoseFrequency 的实例。

我知道 DoseFrequency 实例将被缓存在 hibernate 的一级缓存中,但加载是在几个会话中完成的(它是一个 web 应用程序)。我用的是二级缓存,但是那个缓存不存储实例只序列化实体。

我在 DoseFrequency 中使用 Tuplizer 得到了这种行为,但我不知道是否有任何其他方法可以实现这一点。

@Entity
@Immutable
@Cacheable
@Cache(region = "dosefrequency", usage = CacheConcurrencyStrategy.READ_ONLY)
@Tuplizer(impl=DoseFrequencyTuplizer.class)
public class DoseFrequency {
   ....
}


public class DoseFrequencyTuplizer extends PojoEntityTuplizer {

    public DoseFrequencyTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
        super(entityMetamodel, mappedEntity);
    }

    @Override
    protected Instantiator buildInstantiator(EntityMetamodel entityMetamodel, PersistentClass persistentClass) {
        return new DoseFrequencyInstantiator(DoseFrequency.class.getName(),entityMetamodel,persistentClass,null);
    }

    }


public class DoseFrequencyInstantiator implements Instantiator {

    private final Class targetClass;
    protected PojoEntityInstantiator pojoEntityInstantiator;

    public DoseFrequencyInstantiator(String targetClassName, EntityMetamodel entityMetamodel,
            PersistentClass persistentClass, ReflectionOptimizer.InstantiationOptimizer optimizer) {
        try {
            this.targetClass = Class.forName(targetClassName);
        } catch (ClassNotFoundException e) {
            throw new HibernateException(e);
        }

        pojoEntityInstantiator = new PojoEntityInstantiator(entityMetamodel, persistentClass, optimizer);

    }

    @Override
    public Object instantiate(Serializable id) {
        DoseFrequency df = MedereEMRCache.instance.findDoseFrequencyByID(Long.valueOf(id.toString()));
        if (df == null) {
            return pojoEntityInstantiator.instantiate(id);
        }
        return df;
    }

    @Override
    public Object instantiate() {
        return instantiate(null);
    }

    @Override
    public boolean isInstance(Object object) {
        try {
            return targetClass.isInstance(object);
        } catch (Throwable t) {
            throw new HibernateException("could not get handle to entity as interface : " + t);
        }
    }

}

我知道实例将在应用程序的所有线程之间共享,但它们被视为只读,因此不应修改。

这种方法对吗?

谢谢

【问题讨论】:

    标签: java spring hibernate spring-boot orm


    【解决方案1】:

    您也可以使用LoadEventListener 来始终为同一个实例提供服务。尽管如此,由于实体是不可变的,因此不需要此功能,因此,即使您有多个副本,它仍然是不可变的。

    此外,即使你实现了单例模式,它也只会在每个 JVM 中强制执行,所以我不明白你为什么要实现这个请求。

    实体仅被视为每个EntityManager 的单例。

    【讨论】:

    • 感谢弗拉德的回答。在 VisualVM 的采样器中,不可变对象的实例总是在我每次加载它们时上升(当然 GC 会清理它们)。我想避免像单例一样为这些实体实现。我将尝试使用 LoadEventListener,再次感谢您
    猜你喜欢
    • 2015-09-27
    • 2020-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 2010-10-31
    相关资源
    最近更新 更多