Как охотно загружать ленивые поля с JPA 2.0? - PullRequest
12 голосов
/ 31 августа 2011

У меня есть класс сущностей с таким ленивым полем, как это:

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

Поле истории обычно должно загружаться лениво, потому что оно обычно велико.Однако иногда мне нужно загружать его с нетерпением, но я не пишу что-то уродливое, например movie.getStory (), чтобы форсировать загрузку.Для ленивых отношений я знаю, что соединение извлечения может вызвать энергичную загрузку, но это не работает для ленивого поля.Как мне написать запрос, чтобы охотно загрузить поле истории?

Ответы [ 6 ]

3 голосов
/ 31 августа 2011

Я бы попробовал Hibernate.initialize(movie). Но вызов метода получения (и добавление комментария о том, что это вызывает инициализацию) не так уж и плох.

2 голосов
/ 29 октября 2013

Вы можете использовать ключевые слова fetch all properties в своем запросе:

SELECT movie 
FROM Movie movie FETCH ALL PROPERTIES
WHERE ...
1 голос
/ 12 сентября 2015

Одно из возможных решений:

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

Другое может заключаться в использовании @ Transactional on метода в ManagedBean или Stateless и попытке доступа к movie.getReferencedEntities (). Size () для его загрузки, но при этом будет генерироваться N + 1 проблема, то есть генерирование дополнительных N запросов для каждого отношения, которое не слишком эффективно во многих случаях.

1 голос
/ 29 мая 2012

Чтобы процитировать спецификацию JPA (2.0, 11.1.6):

Стратегия LAZY - это подсказка среде выполнения персистентного поставщика о том, что данные должны извлекаться лениво при первом обращении к ним.Реализация позволяет быстро получать данные, для которых была указана подсказка стратегии LAZY.

Hibernate поддерживает только то, что вы пытаетесь, если вы используете его функции улучшения байт-кода.Есть несколько способов сделать это.Во-первых, использовать инструмент улучшения времени сборки.Второе - использовать (класс-) улучшение времени загрузки.В средах Java EE вы можете включить это в Hibernate JPA, используя параметр hibernate.ejb.use_class_enhancer (установите его в значение true, по умолчанию используется значение false).В средах Java SE вам нужно улучшать классы по мере их загрузки, либо самостоятельно, либо вы можете использовать org.hibernate.bytecode.spi.InstrumentedClassLoader

0 голосов
/ 24 октября 2015

Если вы не возражаете против использования POJO в качестве результата запроса, вы можете использовать запрос конструктора .Это потребует, чтобы у вашего объекта был конструктор со всеми необходимыми параметрами и запросом, подобным этому:

select new Movie(m.id, m.story) from Movie m
0 голосов
/ 29 мая 2012

Я бы предложил обходить объекты с помощью отражения 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);
            }
        }
    }
}
...