Когда использовать EntityManager.find () vs EntityManager.getReference () с JPA



я столкнулся с ситуацией (которая мне кажется странной, но, возможно, вполне нормальной), где я использую EntityManager.getReference(LObj.getClass(), LObj.getId ()), чтобы получить объект базы данных, а затем передать возвращенный объект для сохранения в другой таблице.



Так что в основном поток был такой:




class TFacade{

createT(FObj, AObj) {
T TObj = new T();
TObj.setF(FObj);
TObj.setA(AObj);
...
EntityManager.persist(TObj);
...
L LObj = A.getL();
FObj.setL(LObj);
FFacade.editF(FObj);
}
}

@TransactionAttributeType.REQUIRES_NEW
class FFacade{

editF(FObj){
L LObj = FObj.getL();
LObj = EntityManager.getReference(LObj.getClass(), LObj.getId());
...
EntityManager.merge(FObj);
...
FLHFacade.create(FObj, LObj);
}
}

@TransactionAttributeType.REQUIRED
class FLHFacade{

createFLH(FObj, LObj){
FLH FLHObj = new FLH();
FLHObj.setF(FObj);
FLHObj.setL(LObj);
....
EntityManager.persist(FLHObj);
...
}
}



Я получал следующее исключение " java.ленг.IllegalArgumentException: неизвестная сущность: com. my. persistence.L$$EnhancerByCGLIB$$3e7987d0"



изучив его некоторое время, я, наконец, понял, что это было потому, что я использовал EntityManager.метод getReference (), который я получал выше исключение, поскольку метод возвращал прокси.



Это заставляет меня задаться вопросом, когда рекомендуется использовать EntityManager.метод getReference () вместо EntityManager.метод find ()?



EntityManager.getReference() бросает EntityNotFoundException если он не может найти искомую сущность, что очень удобно само по себе. EntityManager.метод find () просто возвращает null, если он не может найти сущность.



что касается границ транзакций, мне кажется, что вам нужно будет использовать метод find() перед передачей вновь найденного объекта в новую транзакцию. Если вы используете метод getReference (), то вы, вероятно, окажетесь в ситуации, аналогичной моей с вышеуказанным исключением.

995   4  

4 ответов:

Я обычно использую getReference метод, когда мне не нужно обращаться к состоянию базы данных (я имею в виду метод getter). Просто изменить состояние (я имею в виду метод setter). Как вы должны знать, getReference возвращает прокси-объект, который использует мощную функцию автоматического грязные проверки. Предположим следующее

public class Person {

    private String name;
    private Integer age;

}


public class PersonServiceImpl implements PersonService {

    public void changeAge(Integer personId, Integer newAge) {
        Person person = em.getReference(Person.class, personId);

        // person is a proxy
        person.setAge(newAge);
    }

}

если я называю найти метод, поставщик JPA, за кулисами, вызовет

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

если я называю getReference способ, JPA провайдер, за кулисами, будет звонить

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

А знаешь почему ???

когда вы вызываете getReference, вы получите прокси-объект. Что-то вроде этого (поставщик JPA заботится о реализации этого прокси)

public class PersonProxy {

    // JPA provider sets up this field when you call getReference
    private Integer personId;

    private String query = "UPDATE PERSON SET ";

    private boolean stateChanged = false;

    public void setAge(Integer newAge) {
        stateChanged = true;

        query += query + "AGE = " + newAge;
    }

}

таким образом, перед фиксацией транзакции поставщик JPA увидит флаг stateChanged для обновления или не физического лица. Если после инструкции update строки не обновляются, поставщик JPA создаст исключение EntityNotFoundException в соответствии с JPA спецификация.

С уважением,

поскольку ссылка является "управляемой", но не гидратированной, она также может позволить вам удалить объект по идентификатору, не загружая его в память в первую очередь.

поскольку вы не можете удалить неуправляемый объект, просто глупо загружать все поля с помощью find(...) или createQuery(...), только чтобы немедленно удалить его.

MyLargeObject myObject = em.getReference(MyLargeObject.class, objectId);
em.remove(myObject);

как я объяснил в в этой статье, если у вас есть родитель Post сущность и ребенка PostComment как показано на следующей схеме:

enter image description here

если вы называете find при попытке установить @ManyToOne

это заставляет меня задуматься, когда целесообразно использовать EntityManager.getReference() метод вместо этого EntityManager.найти() метод?

EntityManager.getReference() это действительно метод, подверженный ошибкам, и на самом деле очень мало случаев, когда клиентский код должен его использовать.
Лично мне никогда не нужно было его использовать.

EntityManager.getReference () и EntityManager.find (): нет разницы с точки зрения накладных расходов

я не согласен с принятый ответ и в частности:

если я называю найти метод, поставщик JPA, за кулисами, вызовет

SELECT NAME, AGE FROM PERSON WHERE PERSON_ID = ?

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

если я называю getReference метод, поставщик JPA, за кулисами, будет звоните

UPDATE PERSON SET AGE = ? WHERE PERSON_ID = ?

это не то поведение, которое я получаю с Hibernate 5 и javadoc из getReference() не говорит такого:

получить экземпляр, состояние которого может быть лениво притащили. Если запрошенный экземпляра не существует в базе данных, EntityNotFoundException выбрасывается при первом обращении к состоянию экземпляра. (Стойкость среда выполнения поставщика может создать исключение EntityNotFoundException когда вызывается getReference.) Приложение не должно ожидать, что состояние экземпляра будет доступно при отсоединении, если оно не было доступ к приложению, когда диспетчер сущностей был открыт.

EntityManager.getReference() запасные части a запрос для получения сущности в двух случаях:

1) если сущность хранится в контексте сохранения, то есть кэш первого уровня.
И это поведение не является специфичным для EntityManager.getReference(), EntityManager.find() также сохранит запрос для извлечения сущности, если сущность хранится в контексте сохраняемости.

вы можете проверить первый пункт с любым примером.
Вы также можете полагаться на фактическую реализацию Hibernate.
Действительно,EntityManager.getReference() опирается на createProxyIfNecessary() метод org.hibernate.event.internal.DefaultLoadEventListener класс для загрузки объекта.
Вот его реализация:

private Object createProxyIfNecessary(
        final LoadEvent event,
        final EntityPersister persister,
        final EntityKey keyToLoad,
        final LoadEventListener.LoadType options,
        final PersistenceContext persistenceContext) {
    Object existing = persistenceContext.getEntity( keyToLoad );
    if ( existing != null ) {
        // return existing object or initialized proxy (unless deleted)
        if ( traceEnabled ) {
            LOG.trace( "Entity found in session cache" );
        }
        if ( options.isCheckDeleted() ) {
            EntityEntry entry = persistenceContext.getEntry( existing );
            Status status = entry.getStatus();
            if ( status == Status.DELETED || status == Status.GONE ) {
                return null;
            }
        }
        return existing;
    }
    if ( traceEnabled ) {
        LOG.trace( "Creating new proxy for entity" );
    }
    // return new uninitialized proxy
    Object proxy = persister.createProxy( event.getEntityId(), event.getSession() );
    persistenceContext.getBatchFetchQueue().addBatchLoadableEntityKey( keyToLoad );
    persistenceContext.addProxy( keyToLoad, proxy );
    return proxy;
}

интересная часть:

Object existing = persistenceContext.getEntity( keyToLoad );

2) Если мы не эффективно манипулировать сущностью, Эхо к лениво извлечена из javadoc.
Действительно, для обеспечения эффективной загрузки объекта требуется вызов метода на нем.
Таким образом, выигрыш будет связан со сценарием, в котором мы хотим загрузить объект, не имея необходимость его использовать ? В рамках приложений эта потребность действительно необычна и, кроме того,getReference() поведение также очень вводит в заблуждение, если Вы читаете следующую часть.

почему предпочтение EntityManager.найти () над EntityManager.getReference()

С точки зрения накладных расходов, getReference() не лучше, чем find() как обсуждалось в предыдущем пункте.
Так зачем же использовать то или другое ?

вызов getReference() может вернуть лениво принесенный сущность.
Здесь ленивая выборка относится не к отношениям сущности, а к самой сущности.
Это означает, что если мы вызываем getReference() и тогда контекст сохранения закрыт, сущность может никогда не загружаться, и поэтому результат действительно непредсказуем. Например, если прокси-объект сериализован, вы можете получить null ссылка как сериализованный результат или если метод вызывается на прокси-объект, исключение, например LazyInitializationException бросается.

это означает вот это бросок EntityNotFoundException это основная причина для использования getReference() для обработки экземпляра, который не существует в базе данных, поскольку ситуация ошибки может никогда не выполняться, пока сущность не существует.

EntityManager.find() не имеет амбиций бросать EntityNotFoundException если объект не найден. Его поведение является одновременно простым и ясным. Вы никогда не будете удивлены, как он возвращает всегда загруженный объект или null (если сущность не найдена) , но никогда не является сущностью под формой прокси-сервер, который не может быть эффективно загружен.
Так что EntityManager.find() следует отдавать предпочтение в большинстве случаев.

Comments

    Ничего не найдено.