Спящий режим один к одному: 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.
Что я делаю неправильно?
Ответы
Ответ 1
Использовать стратегию доступа
Вместо
@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 руководство пользователя
Ответ 2
Просто добавьте к 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();
}
Работает и для отдельных объектов.
Ответ 3
добавить @AccessType ( "свойство" )
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@AccessType("property")
protected Long id;
Ответ 4
Сохранение Java с Hibernate Book упоминает об этом в "13.1.3" Понимание прокси ":
Пока вы получаете доступ только к свойству идентификатора базы данных, нет необходима инициализация прокси. (Обратите внимание, что это неверно если вы сопоставляете свойство идентификатора с прямым доступом к полю; зимовать то даже не знает, что метод 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 из прокси. Если экземпляр является реальным объектом, он возвращает идентификатор.
Ответ 5
К сожалению, принятый ответ неверен. Также другие ответы не дают простейшего или четкого решения.
Используйте уровень доступа к свойствам для ID
класса BAR
.
@Entity
public class Bar {
@Id
@Access(AccessType.PROPERTY)
private Long Id;
...
}
Так же просто:)
Ответ 6
В org.hibernate.Session у вас есть функция, которая выполняет работу без ленивой загрузки объекта:
public Serializable getIdentifier (объект объекта) выдает исключение 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();
}
}
Ответ 7
В настоящее время существует библиотека данных dibatate hibernate Jackson:
https://github.com/FasterXML/jackson-datatype-hibernate
И вы можете настроить функции:
Hibernate4Module hibernate4Module = new Hibernate4Module();
hibernate4Module.configure(Hibernate4Module.Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true);
Это будет включать идентификатор ленивого загруженного отношения -
Ответ 8
Вы можете использовать запрос HQL. Метод getBar() действительно вернет прокси-сервер, который не будет выбран до тех пор, пока вы не вызовете некоторый метод привязки данных. Я не уверен, что именно ваша проблема. Можете ли вы дать нам еще больше фона?
Ответ 9
измените свой метод getter следующим образом:
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;
}