2010-04-07 2 views
48

Я хочу получить идентификатор отношения «один-к-одному» без загрузки всего объекта. Я думал, что я мог бы сделать это с помощью отложенной загрузки следующим образом:Hibernate один-на-один: getId() без выборки всего объекта

class Foo { 
    @OneToOne(fetch = FetchType.LAZY, optional = false) 
    private Bar bar; 
} 


Foo f = session.get(Foo.class, fooId); // Hibernate fetches Foo 

f.getBar(); // Hibernate fetches full Bar object 

f.getBar().getId(); // No further fetch, returns id 

Я хочу f.getBar(), чтобы не триггера другой выборки. Я хочу, чтобы hibernate дал мне прокси-объект, который позволяет мне вызвать .getId(), не выбирая объект Bar.

Что я делаю неправильно?

+0

же поведение с помощью @ManyToOne (выборка = FetchType.LAZY, факультативные = ложь) Однозначных ассоциаций просто не идут хорошо для меня .. – Rob

+0

Его ошибка Hibernate: https://hibernate.atlassian.net/browse/HHH-3718 См. Также сравнение поля или доступа к собственности: http://stackoverflow.com/questions/594597/hibernate-annotations-which-is- best-field-or-property-access – GKislin

ответ

29

Использование стратегии доступа свойство

Вместо

@OneToOne(fetch=FetchType.LAZY, optional=false) 
private Bar bar; 

Использование

private Bar bar; 

@OneToOne(fetch=FetchType.LAZY, optional=false) 
public Bar getBar() { 
    return this.bar; 
} 

Теперь он отлично работает!

Прокси инициализируется, если вы вызываете какой-либо метод , который не является идентификатором метода getter. Но он просто работает при использовании стратегии доступа к ресурсам. Имейте это в виду.

См: Hibernate 5.2 user guide

+0

Означает ли это, что я должен изменить свою сущность, чтобы иметь все аннотации на уровне свойств, чтобы это работало? Если я оставлю как есть и переместил одну к одной аннотации на уровень свойства и установил тип доступа к свойству, он не работает. –

+0

@shane lee См. Http://docs.jboss.org/ejb3/app-server/HibernateAnnotations/reference/en/html_single/# d0e1955 –

+1

Спасибо за ответ. Я не доверял запросу, выполняемому в моем проекте, поэтому я добавил в свой собственный тест интеграции db для проверки. Сейчас я работаю. Единственное изменение заключается в добавлении типа доступа в идентификатор целевого объекта. Это было единственное изменение. @Id @GeneratedValue ( стратегия = GenerationType.SEQUENCE, генератор = "FILECONTENT_ID_SEQ") @SequenceGenerator ( название = "FILECONTENT_ID_SEQ", SequenceName = "FILECONTENT_ID_SEQ") @Column ( имя = "ID", nullable = false) @Access (AccessType.PROPERTY) private Long id; –

0

Вы можете использовать запрос HQL. Метод getBar() действительно вернет прокси-сервер, который не будет выбран до тех пор, пока вы не вызовете некоторый метод привязки данных. Я не уверен, что именно ваша проблема. Можете ли вы дать нам еще один опыт?

+1

Спасибо за ответ. То, что вы описываете, не то, что происходит. getBar() вызывает выборки. Я ожидал бы, что вы описали, что объект-прокси возвращается и не выполняется выборка. Есть ли какая-либо другая конфигурация, которую я мог бы пропустить? – Rob

+0

На самом деле getId(), следующий за getBar(), вызывает выборку объекта. Вы не пропускаете конфигурацию IMO. Возможно, какой-то запрос вроде «выберите f.bar.id из Foo f где f.id =?» сделают трюк для вас. –

+0

Прокси-объект не должен извлекать полный бар на bar.getId(). Он уже знает идентификатор, так как это часть Foo. В любом случае, он выполняет выборку без вызова.getId() – Rob

22

Просто, чтобы добавить к Arthur Ronald FD Garcia'post: вы можете заставить доступ к свойству по @Access(AccessType.PROPERTY) (или осуждается @AccessType("property")), см http://256stuff.com/gray/docs/misc/hibernate_lazy_field_access_annotations.shtml

Другим решением может быть:

public static Integer getIdDirect(Entity entity) { 
    if (entity instanceof HibernateProxy) { 
     LazyInitializer lazyInitializer = ((HibernateProxy) entity).getHibernateLazyInitializer(); 
     if (lazyInitializer.isUninitialized()) { 
      return (Integer) lazyInitializer.getIdentifier(); 
     } 
    } 
    return entity.getId(); 
} 

Работы для отдельных объектов тоже.

+1

Я использовал вашу идею, а также тот факт, что прокси не могут переопределять конечные методы, чтобы изменить метод 'getId()', чтобы избежать инициализации. Пожалуйста, если можно, см. Мой ответ на этой странице и расскажите мне, что вы думаете. Я также не понимаю, почему вы проверяете, является ли 'lazyInitializer.isUninitialized()'. Не можете ли вы всегда возвращать 'lazyInitializer.getIdentifier()' когда объект является HibernateProxy? – MarcG

+0

Я не помню, почему я использовал 'if (lazyInitializer.isUninitialized())'. Возможно, использовать грязный трюк только тогда, когда это действительно необходимо. Я думаю, что это может быть опущено. – xmedeko

+1

Аннотации Hibernate «AccessType» устарели. Используйте примечание JPA2 вместо '@Access (AccessType.PROPERTY)' – minni

3

В org.hibernate.Session у вас есть функция, которые делают работу без отложенной загрузки объекта:

общественных Сериализуемого getIdentifier (объект Object) бросает HibernateException;

Найдено в спящем 3.3.2.GA:

public Serializable getIdentifier(Object object) throws HibernateException { 
     errorIfClosed(); 
     checkTransactionSynchStatus(); 
     if (object instanceof HibernateProxy) { 
      LazyInitializer li = ((HibernateProxy) object).getHibernateLazyInitializer(); 
      if (li.getSession() != this) { 
       throw new TransientObjectException("The proxy was not associated with this session"); 
      } 
      return li.getIdentifier(); 
     } 
     else { 
      EntityEntry entry = persistenceContext.getEntry(object); 
      if (entry == null) { 
       throw new TransientObjectException("The instance was not associated with this session"); 
      } 
      return entry.getId(); 
     } 
    } 
+0

+1. Он просто не работает без сеанса, например. для отдельных объектов. – xmedeko

7

Java-Persistence с Hibernate книге упоминает об этом в разделе "13.1.3 Понимание Proxies":

Пока доступ только свойство идентификатора базы данных, нет необходимо инициализировать прокси-сервер. (Обратите внимание, что это не так: , если вы сопоставляете свойство идентификатора с прямым доступом к полю, Hibernate тогда даже не знает, что метод getId() существует. Если вы его вызываете, прокси должен быть инициализирован.)

Однако, основываясь на @xmedeko ответ на этой странице я разработал хак, чтобы избежать инициализации прокси даже при использовании стратегии прямого доступа поля. Просто измените метод getId(), как показано ниже.

Вместо:

public long getId() { return id; } 

Использование:

public final long getId() { 
     if (this instanceof HibernateProxy) { 
      return (long)((HibernateProxy)this).getHibernateLazyInitializer().getIdentifier(); 
     } 
     else { return id; } 
    } 

Идея заключается в том, чтобы отметить метод getId(), как final, так что прокси не может заменить его. Затем вызов метода не может запускать какой-либо прокси-код и, следовательно, не может инициализировать прокси-сервер. Сам метод проверяет, является ли его экземпляр прокси, и в этом случае возвращает id из прокси. Если экземпляр является реальным объектом, он возвращает id.

+0

Ха-ха, это действительно ужасный хак :) «Не делай этого дома» –

+0

@ OndraŽižka Вы ошибаетесь. Этот код работает отлично. Кроме того, он не нарушает никаких правил, побочных эффектов нет, и ясно, что он делает и почему. Итак, если вы можете думать о какой-либо причине не использовать этот код или почему это «ужасно», пожалуйста, поделитесь. – MarcG

+0

Он переходит на внутренние классы Hibernate, которые могут быть изменены без предварительного уведомления. Хотя я не сомневаюсь, что он работает отлично, это не то, что я бы поставил в приложение, которое должно длиться годами. –

0

изменить метод получения так:

public Bar getBar() { 
    if (bar instanceof HibernateProxy) { 
     HibernateProxy hibernateProxy = (HibernateProxy) this.bar; 
     LazyInitializer lazyInitializer = hibernateProxy.getHibernateLazyInitializer(); 
     if (lazyInitializer.getSession() == null) 
      bar = new Bar((long) lazyInitializer.getIdentifier()); 
    } 

    return bar; 
} 
10

добавить @AccessType ("собственность")

@Id 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
@AccessType("property") 
protected Long id; 
+10

Аннотации Hibernate «AccessType» устарели. Вместо этого используйте примечание JPA2: '@Access (AccessType.PROPERTY)' – minni

1

Существует в настоящее время джексон зимуют библиотека типов данных здесь:

https://github.com/FasterXML/jackson-datatype-hibernate

И вы c настроить функции:

Hibernate4Module hibernate4Module = new Hibernate4Module(); 
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true); 

Это будет включать в себя идентификатор ленивым загруженного

Связь-
5

К сожалению, принятый ответ неверен. Также другие ответы не дают простейшего или четкого решения.

Используйте уровень доступа к объекту для ID класса BAR.

@Entity 
public class Bar { 

    @Id 
    @Access(AccessType.PROPERTY) 
    private Long id; 

    ... 
} 

Просто так просто :)

Смежные вопросы