2014-10-02 80 views
9

我遇到了一個沒有被初始化的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

回答

1

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

7

調試窗口中的實體看起來像已正確初始化

當你有一些實體了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