簡體   English   中英

如何用JPA 2.0急切加載懶字段?

[英]How to eagerly load lazy fields with JPA 2.0?

我有一個實體類,它有一個像這樣的惰性字段:

@Entity
public Movie implements Serializable {
    ...
    @Basic(fetch = FetchType.LAZY)
    private String story;
    ...
}

故事場通常應該懶散地加載,因為它通常很大。 但有時候,我需要急切地加載它,但是我不會像movie.getStory()那樣寫一些丑陋的東西來強制加載。 對於懶惰的關系,我知道獲取連接可以強制加載,但它不適用於惰性字段。 如何編寫查詢以急切加載故事字段?

我試試Hibernate.initialize(movie) 但是調用getter(並添加一個強制初始化的注釋)並沒有錯。

您可以在查詢中使用fetch all properties關鍵字:

SELECT movie 
FROM Movie movie FETCH ALL PROPERTIES
WHERE ...

引用JPA規范(2.0,11.1.6):

LAZY策略是持久性提供程序運行時的提示,即數據在首次訪問時應該被懶惰地獲取。 允許實現急切地獲取已指定LAZY策略提示的數據。

如果您使用字節碼增強功能,Hibernate僅支持您嘗試的內容。 有幾種方法可以做到這一點。 首先是使用構建時增強工具。 第二種是使用(類)加載時間增強。 在Java EE環境中,您可以使用'hibernate.ejb.use_class_enhancer'設置在Hibernate JPA上啟用它(將其設置為true,默認為false)。 在Java SE環境中,您需要在加載類時自行增強類,或者您可以利用org.hibernate.bytecode.spi.InstrumentedClassLoader

一種可能的解決方案是:

SELECT movie 
FROM Movie movie LEFT JOIN FETCH movie.referencedEntities
WHERE...

其他可能是在ManagedBean或Stateless中使用@Transactional方法並嘗試訪問movie.getReferencedEntities()。size()來加載它但它會產生N + 1問題,即為每個關系生成額外的N個查詢在許多情況下有效。

如果您不介意將POJO作為查詢結果,則可以使用構造函數查詢 這將要求您的對象具有包含所有必需參數的構造函數以及如下所示的查詢:

select new Movie(m.id, m.story) from Movie m

我建議使用Java反射遍歷對象,調用以“get”開頭的所有方法,並對所有獲取的對象重復此操作,如果它具有@Entity注釋。

不是最美麗的方式,但它必須是一個強大的解決方法。 類似的東西(尚未測試):

public static <T> void deepDetach(EntityManager emanager, T entity) {
    IdentityHashMap<Object, Object> detached = new IdentityHashMap<Object, Object>();
    try {
        deepDetach(emanager, entity, detached);
    } catch (IllegalAccessException e) {
        throw new RuntimeException("Error deep detaching entity [" + entity + "].", e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException("Error deep detaching entity [" + entity + "].", e);
    }
}


private static <T> void deepDetach(EntityManager emanager, T entity, IdentityHashMap<Object, Object> detached) throws IllegalAccessException, InvocationTargetException {
    if (entity == null || detached.containsKey(entity)) {
        return;
    }

    Class<?> clazz = entity.getClass();

    Entity entityAnnotation  = clazz.getAnnotation(Entity.class);
    if (entityAnnotation == null) {
        return; // Not an entity. No need to detach.
    }

    emanager.detach(entity);
    detached.put(entity, null); // value doesn't matter. Using a map, because there is no IdentitySet.

    Method[] methods = clazz.getMethods();

    for (Method m : methods) {
        String name = m.getName();
        if (m.getParameterTypes().length == 0) {
            if (name.length() > 3 && name.startsWith("get") && Character.isUpperCase(name.charAt(3))) {
                Object res = m.invoke(entity, new Object[0]);
                deepDetach(emanager, res, detached);
            }
            // It is actually not needed for searching for lazy instances, but it will load
            // this instance, if it was represented by a proxy
            if (name.length() > 2 && name.startsWith("is") && Character.isUpperCase(name.charAt(2))) {
                Object res = m.invoke(entity, new Object[0]);
                deepDetach(emanager, res, detached);
            }
        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM