休眠实体代理初始化

问题描述:

我遇到了一个没有被初始化的Hibernate实体的问题。
它似乎仍然返回一个未初始化的代理...休眠实体代理初始化

如果我看看我的调试信息,我会期待我的实体被初始化。
但它看起来像如下:

entity = {[email protected]}"[email protected][id=1,version=0]" 
    handler = {[email protected]} 
     interfaces = {java.lang.Class[2]@9197} 
     constructed = true 
     persistentClass = {[email protected]}"class SomeEntityImpl" 
     getIdentifierMethod = null 
     setIdentifierMethod = null 
     overridesEquals = true 
     componentIdType = null 
     replacement = null 
     entityName = {[email protected]}"SomeEntityImpl" 
     id = {[email protected]}"1" 
     target = {[email protected]}"[email protected][guid=<null>,id=1,version=0]" 
     initialized = true 
     readOnly = true 
     unwrap = false 
     session = {[email protected]}"SessionImpl(PersistenceContext[entityKeys=[EntityKey[EntityReferenceImpl#2], EntityKey[SomeEntityImpl#1], EntityKey[... 
     readOnlyBeforeAttachedToSession = null 
     sessionFactoryUuid = null 
     allowLoadOutsideTransaction = false 

请注意,我的Hibernate POJO仍然仅包含一个handler甚至做一个明确的初始化后...
在我的调试视图,可以看我的“真正的”财产值(不显示在上面)当我展开target节点时。

我在做什么:

EntityReferenceImpl entityReference = findEntityReference(session); 
SomeEntity entity = null; 
if (entityReference != null) { 
    // initialize association using a left outer join 
    HibernateUtil.initialize(entityReference.getSomeEntity()); 
    entity = entityReference.getSomeEntity(); 
} 
return entity; 

通知的HibernateUtil.initialize来电!

SomeEntity映射:

public class SomeEntityImpl extends AbstractEntity implements SomeEntity { 
    @OneToMany(mappedBy = "someEntity", fetch = FetchType.EAGER, targetEntity = EntityReferenceImpl.class, orphanRemoval = true) 
    @Cascade(CascadeType.ALL) 
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE) 
    private Set<EntityReference> entityReferences = new HashSet<>(); 

    @Target(EntityName.class) 
    @Embedded 
    private Name name; 

    @Target(EntityAddress.class) 
    @Embedded 
    private Address address; 

    ... 

} 

EntityReferenceImpl映射:

public class EntityReferenceImpl extends AbstractEntity implements EntityReference { 

@ManyToOne(optional = true, fetch = FetchType.LAZY, targetEntity = SomeEntityImpl.class) 
@JoinColumn(name = "entity_id") 
private SomeEntity someEntity; 

... 

} 

那么什么是副作用:当POJO后带有更新的属性我仍然有相同的结构(如提及上面),我可以看到target节点下的更新属性。
但是,当我试图使用session.merge()session.update()session.saveOrUpdate()更新实体时,Hibernate不检测'脏'属性并且不会调用对数据库的更新查询。


有没有人有关于这种奇怪行为的线索?我尽我所能,但没有任何结果。
所有的帮助是非常欢迎的!

+0

'HibernateUtil'类是什么包?你不使用'Hibernate'类吗? – walkeros 2014-10-10 10:59:28

+0

DId你解决了这个问题? Iam面临类似问题 – 2014-11-13 09:46:34

Hibernate使用代理来拦截对LAZY实体的调用。您在调试中看到的结构是代理的外观。

您无需致电HibernateUtil.initialize,只需使用“提取连接”即可加载您对单个查询感兴趣的所有实体。

如果实体附加到当前会话,dirty checking mechanism将自动将所有entity state transitions转换为数据库DML语句。

Session.update旨在重新附加分离的实体(在已关闭的会话中加载的实体)。

Session.merge用于将实体状态复制到已加载的实体上(如果以前未加载,则实时加载实体)。

检查您是否启用了交易,否则您只能选择实体。对于持久/合并和脏检查更新,您必须使用事务(使用Java EE或支持Spring @Transactional)。

+1

我同意你的意见。我已经在HQL查询中使用'fetch join',但是我的效果相同。这就是为什么我试图确保Hibernate.initialize()。我用readOnly = false使用Spring Transactional注释。它似乎是Hibernate不检测脏属性。问题是代理不能正确初始化(就像你可以在调试视图中看到的那样)。 Hibernate不检查未初始化的关联(将会是一个巨大的开销)。问题仍然是为什么它没有正确初始化。它是否与实现接口有关? – user2054927 2014-10-05 16:07:27

+0

根据我的经验,最好避免实体上的接口关联。你可以拥有接口,但是可以在实际的实体上建立所有的关联,因为无论如何这都会模拟真实的关系 – 2014-10-05 16:26:45

调试窗口中的实体看起来像已正确初始化

当你有一些实体了Hibernate被代理,这个实体甚至被正确初始化后存储代理对象。初始化代理对象后本身不会消失......

public class EntityReferenceImpl extends AbstractEntity implements EntityReference { 

@ManyToOne(fetch = FetchType.LAZY, ...) 
private SomeEntity someEntity; 
... 

在你的榜样,你必须拥有@ManyToOne(LAZY)SomeEntity实体EntityReferenceImpl实体。

当休眠加载EntityReferenceImpl它将从resultSet值中填充所有字段,但someEntity字段被设置为代理对象。

此代理对象是这样的:

class SomeEntity_$$_javassist_3 extends SomeEntity implements HibernateProxy { 
    + firstname = NULL; 
    + lastname = NULL; 
    + age = 0; 
    + handler; //of type: JavassistLazyInitializer 

    getFirstname() { 
    handler.invoke(..., Method thisMethod, Method proceed, args); 
    } 
    getLastName() {...} 
} 

SomeEntity类有(例如)方法getFirstName()等,但了Javassist生成的类简单地扩展您SomeEntity并具有一些新的字节码生成的方法,如c7getFirstName()

最重要 - 代理类具有新字段:handler类型JavassistLazyInitializer

让我们看看如何JavassistLazyInitializer样子:

JavassistLazyInitializer { 
    + target; //holds SomeEntity object 
    invoke(..., Method thisMethod, Method proceed, args) { 
    if (target == null) { 
     target = initialize(); // calls sessionImpl.immediateLoad 
    } 
    return thisMethod.invoke(target, args); 
    } 
} 

所以,当你进入你的代理对象 - 它你的领域,如firstnamelastname等 当你初始化这个代理,SomeEntity加载进入目标字段。代理对象上的firstname,lastname字段与之前一样为空 - 代理不使用它们,但实际数据在SomeEntity对象中,由target字段保存。

这是如何在休眠中实现代理。

你可能会问 - 为什么这样的解决方案?这样的设计来自多态性问题。如果SomeEntity将是抽象父类与2个子类EntityAEntityB休眠没有问题 - someEntity字段持有代理(生成)类扩展SomeEntity但具体EntityAEntityB内部target字段。

然而,这种解决方案和多态性存在一些缺陷。您的someEntity字段将为instance of SomeEntity,但从不instance of EntityA也不instance of EntityB