Я столкнулся с ситуацией (которая я считаю странной, но, возможно, вполне нормальной), когда я использую EntityManager.getReference (LObj.getClass (), LObj.getId ()), чтобы получить объект базы данных, а затем передать возвращенный объект в сохраняться в другой таблице.
Итак, в основном поток был таким:
class TFacade { createT (FObj, AObj) { T TObj = новый 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 = новый FLH (); FLHObj.setF (FObj); FLHObj.setL (LObj); .... EntityManager.persist (FLHObj); ... } }
Я получал следующее исключение «java.lang.IllegalArgumentException: Unknown entity: com.my.persistence.L $$ EnhancerByCGLIB $$ 3e7987d0»
Посмотрев на это некоторое время, я наконец понял, что это произошло потому, что я использовал метод EntityManager.getReference (), я получал указанное выше исключение, поскольку метод возвращал прокси.
Это заставляет меня задаться вопросом, когда целесообразно использовать метод EntityManager.getReference () вместо метода EntityManager.find () ?
EntityManager.getReference () генерирует исключение EntityNotFoundException, если не может найти искомую сущность, что само по себе очень удобно. Метод EntityManager.find () просто возвращает null, если не может найти объект.
Что касается границ транзакций, мне кажется, что вам нужно будет использовать метод find () перед передачей вновь найденной сущности в новую транзакцию. Если вы используете метод getReference (), вы, вероятно, окажетесь в ситуации, аналогичной моей, с указанным выше исключением.
источник
Ответы:
Я обычно использую метод getReference, когда мне не нужно получать доступ к состоянию базы данных (я имею в виду метод получения). Просто чтобы изменить состояние (я имею в виду метод установки). Как вы должны знать, getReference возвращает прокси-объект, который использует мощную функцию, называемую автоматической грязной проверкой. Предположим следующее
Если я вызову метод find , провайдер JPA за кадром вызовет
Если я вызову метод getReference , провайдер JPA за кадром вызовет
А знаете почему ???
Когда вы вызываете getReference, вы получаете прокси-объект. Что-то вроде этого (провайдер JPA позаботится о реализации этого прокси)
Итак, перед фиксацией транзакции провайдер JPA увидит флаг stateChanged, чтобы обновить ИЛИ НЕ лицо. Если строки не обновляются после оператора обновления, поставщик JPA сгенерирует исключение EntityNotFoundException в соответствии со спецификацией JPA.
С уважением,
источник
SELECT
раньшеUPDATE
, независимо от того, какой изfind()
/getReference()
я использую. Что еще хуже,SELECT
проходит NON-LAZY отношения (выдача newSELECTS
), хотя я просто хочу обновить одно поле в одной сущности.Как я объяснил в этой статье , при условии, что у вас есть родительский
Post
объект и дочерний элемент,PostComment
как показано на следующей диаграмме:Если вы звоните
find
при попытке установить@ManyToOne
post
связь:Hibernate выполнит следующие инструкции:
Запрос SELECT на этот раз бесполезен, потому что нам не нужно извлекать объект Post. Мы только хотим установить базовый столбец внешнего ключа post_id.
Теперь, если вы используете
getReference
вместо этого:На этот раз Hibernate выдаст только инструкцию INSERT:
В отличие от
find
,getReference
only возвращает прокси-объект сущности, у которого только установлен идентификатор. Если вы обращаетесь к прокси-серверу, связанный оператор SQL будет запускаться, пока EntityManager все еще открыт.Однако в этом случае нам не нужен доступ к прокси-объекту. Мы хотим распространить внешний ключ только на базовую запись таблицы, поэтому для этого варианта использования достаточно загрузки прокси.
При загрузке прокси вам необходимо знать, что при попытке доступа к ссылке на прокси после закрытия EntityManager может возникнуть исключение LazyInitializationException. Для получения дополнительных сведений об обработке
LazyInitializationException
файлов см. Эту статью .источник
hibernate.jpa.compliance.proxy
свойство конфигурации , поэтому вы можете выбрать соответствие JPA или более высокую производительность доступа к данным.getReference
вообще нужен, если достаточно просто установить новый экземпляр модели с установленным PK. Что мне не хватает?Поскольку ссылка является «управляемой», но не гидратированной, она также может позволить вам удалить объект по идентификатору без необходимости сначала загружать его в память.
Поскольку вы не можете удалить неуправляемый объект, просто глупо загружать все поля с помощью find (...) или createQuery (...) только для того, чтобы немедленно удалить его.
источник
EntityManager.getReference()
действительно является методом, подверженным ошибкам, и действительно очень мало случаев, когда клиентский код должен его использовать.Лично мне это никогда не понадобилось.
EntityManager.getReference () и EntityManager.find (): нет разницы в накладных расходах
Я не согласен с принятым ответом, в частности:
Это не то поведение, которое я получаю с Hibernate 5, и javadoc
getReference()
не говорит о таком:EntityManager.getReference()
избавляет запрос для извлечения сущности в двух случаях:1) если объект хранится в контексте постоянства, это кеш первого уровня.
И это поведение не является специфическим для
EntityManager.getReference()
,EntityManager.find()
также избавит от запроса для получения объекта, если объект хранится в контексте Persistence.Вы можете проверить первый пункт на любом примере.
Вы также можете положиться на реальную реализацию Hibernate.
Действительно, для загрузки объекта
EntityManager.getReference()
полагается наcreateProxyIfNecessary()
методorg.hibernate.event.internal.DefaultLoadEventListener
класса.Вот его реализация:
Интересная часть:
2) Если мы не можем эффективно манипулировать сущностью, повторяется лениво загруженная документация javadoc.
Действительно, чтобы обеспечить эффективную загрузку объекта, требуется вызов метода для него.
Значит, выигрыш будет связан со сценарием, когда мы хотим загрузить сущность без необходимости ее использовать? В рамках приложений такая потребность действительно необычна, и, кроме того,
getReference()
поведение также вводит в заблуждение, если вы прочитаете следующую часть.Почему лучше EntityManager.find () чем EntityManager.getReference ()
С точки зрения накладных расходов
getReference()
не лучше, чемfind()
обсуждалось в предыдущем пункте.Так зачем использовать то или другое?
Вызов
getReference()
может вернуть ленивую полученную сущность.Здесь ленивая выборка относится не к отношениям сущности, а к самой сущности.
Это означает, что если мы вызываем,
getReference()
а затем контекст Persistence закрывается, объект может никогда не загрузиться, и поэтому результат действительно непредсказуем. Например, если прокси-объект сериализован, вы можете получитьnull
ссылку как сериализованный результат или, если метод вызывается для прокси-объекта, возникает исключение, такое какLazyInitializationException
.Это означает, что возникновение
EntityNotFoundException
этого является основной причиной использованияgetReference()
для обработки экземпляра, которого нет в базе данных, поскольку ситуация с ошибкой может никогда не возникнуть, пока сущность не существует.EntityManager.find()
не имеет амбиций бросить,EntityNotFoundException
если сущность не будет найдена. Его поведение одновременно простое и понятное. Вы никогда не будете удивлены, поскольку он всегда возвращает загруженный объект илиnull
(если объект не найден), но никогда объект в форме прокси, который не может быть эффективно загружен.Так
EntityManager.find()
следует отдавать предпочтение в большинстве случаев.источник
Я не согласен с выбранным ответом, и, как правильно указал davidxxx, getReference не обеспечивает такое поведение динамических обновлений без выбора. Я задал вопрос относительно действительности этого ответа, см. Здесь - невозможно обновить, не выполнив select при использовании установщика после getReference () hibernate JPA .
Честно говоря, я не видел никого, кто действительно использовал бы эту функцию. ВЕЗДЕ. И я не понимаю, почему за него так проголосовали.
Теперь, во-первых, независимо от того, что вы вызываете для прокси-объекта гибернации, установщика или получателя, запускается SQL и объект загружается.
Но потом я подумал: а что, если прокси JPA getReference () не предоставляет такой возможности? Я могу просто написать свой прокси.
Теперь мы все можем утверждать, что выборка по первичным ключам выполняется так же быстро, как и запрос, и на самом деле это не то, чего следует избегать. Но для тех из нас, кто не может справиться с этим по той или иной причине, ниже представлена реализация такого прокси. Но прежде чем я познакомлюсь с реализацией, ознакомьтесь с ее использованием и простотой использования.
ИСПОЛЬЗОВАНИЕ
И это вызовет следующий запрос -
и даже если вы хотите вставить, вы все равно можете выполнить PersistenceService.save (new Order ("a", 2)); и он запустил бы вставку как следует.
РЕАЛИЗАЦИЯ
Добавьте это в свой pom.xml -
Сделайте этот класс для создания динамического прокси -
Сделайте интерфейс со всеми методами -
Теперь создайте перехватчик, который позволит вам реализовать эти методы на вашем прокси -
И класс исключения -
Сервис для сохранения с помощью этого прокси -
источник