Гибернация - двунаправленный @OneToOne

У меня есть 2 класса: User и UserPicture, которые имеют отношение 1: 1.

public class User {
     @Id
     @GeneratedValue(strategy=GenerationType.AUTO)
     @Column(name="id", nullable = false, unique = true)
 private int id;

     private String firstname;

     private String lastname;

     @OneToOne
     @JoinColumn(name = "picture") //field named "picture" in the database
     private UserPicture userPicture;

     ..
}


public class UserPicture {

     @Id
     @GeneratedValue(strategy=GenerationType.AUTO)
     @Column(name="id", nullable = false, unique = true)
     private int id;

     private Blob image;

     @OneToOne
     @JoinColumn(name = "user")
     User user;

«user» в UserPicture будет загружен, а «userPicture» в User - нет - что я сделал не так?

ИЗМЕНИТЬ. Нужно добавить, что я просто создаю UserPicture и вставляю их (с существующим userId) - может быть, мне нужно каскадировать «user» в UserPicture?


person user1731299    schedule 24.10.2012    source источник


Ответы (3)


Вы должны сопоставить свои классы.

public class User {
    ...
    @OneToOne (mappedBy="user")
    private UserPicture userPicture;
    ...
}

public class UserPicture {
    ...
    @OneToOne
    @JoinColumn (name="user")
    private User user;
    ...
}
person Pigueiras    schedule 24.10.2012
comment
Оно работает! Спасибо! Но если я вставлю UserPicture, пользователь не будет обновлен? - person user1731299; 24.10.2012
comment
@ user1731299 Все, что вам нужно сделать, чтобы связать User с UserPicture, - это создать UserPicture, вызвать setUser для объекта и сохранить его в БД. - person Pigueiras; 24.10.2012
comment
Далее: Пользовательский интерфейс просит меня добавить изображение пользователя пользователю A. В моем сервисе я перезагружаю пользователя A, устанавливаю этого пользователя в свой новый объект UserPicture и создаю / вставляю UserPicture - ›поле 'picture' в таблице user остается нулевым. Если я загружаю пользователя, изображение поля загружается… действительно странно. - person user1731299; 24.10.2012
comment
@ user1731299 Поле picture не должно существовать в этой конфигурации. У вас должна быть только ссылка user в UserPicture таблице. Если вы восстановите пользователя из БД, спящий режим загрузит для вас атрибут UserPicture, потому что это отношение OneToOne. Надеюсь, теперь понятно :-) - person Pigueiras; 24.10.2012
comment
Все чисто! Еще один вопрос: можно ли сделать userPicture в User ленивым? - person user1731299; 24.10.2012
comment
Ты уверен? Я нашел много блогов и обсуждений, в которых говорилось, что @OneToOne не поддерживает ленивых? Я изменил User: @OneToMany (mappedBy = user, fetch = FetchType.LAZY) частный список ‹UserPicture› pictureList; - person user1731299; 24.10.2012

Что касается вашего вопроса: (потому что у меня недостаточно репутации, чтобы ответить в комментарии)

«Все ясно! Еще один вопрос: можно ли сделать userPicture в User lazy? - user1731299 24 окт.

Да, это возможно сделать ленивую выборку. Однако просто сказать «fetchType = FetchType.Lazy» не получится. Причина в том, что Hibernate необходимо проверить объединенную таблицу, чтобы узнать, является ли она нулевым значением или есть ли там запись. Поскольку это сопоставление OneToOne, Hibernate полагает, что он может сохранить вызов базы данных, просто извлекая любые данные, которые там есть, поскольку ему все равно нужно было проверять, было ли оно нулевым. Это не относится к сопоставлениям x-to-many-mappings, поскольку Hibernate знает, что «многие» означает, что в другой таблице есть список ... будь то пустой или заполненный список, это все равно список. Для одного значения он должен различать фактические данные и нулевое значение.

Способ обойти это - сообщить Hibernate, что там ВСЕГДА будет значение и НИКОГДА не будет нулевого значения. Зная это, Hibernate может создать заполнитель, пока не придет время получить эти данные. В аннотациях это делается путем добавления «optional = false» к аннотации @OneToOne.

Однако будьте осторожны! С этим есть некоторые проблемы; включая тот, который я пытаюсь выяснить сейчас (и как я наткнулся на ваш вопрос здесь). Этот необязательный параметр = false заставляет Hibernate делать небольшую дополнительную проверку и, кажется, сбивает Hibernate с толку в том, как он должен выполнять вставки. Итак, вы можете держаться подальше от этой техники ленивой загрузки.

person AForsberg    schedule 06.08.2013

Ленивая загрузка в режиме One to One работает, даже когда мы указываем поле как не допускающее значения NULL в аннотации JoinColumn. Однако в двунаправленном режиме One to One ленивая загрузка не работает для объекта, где мы используем mappedBy = ''. Например, если у нас есть два объекта Contract и House, где таблица Contract содержит внешний ключ для House. Когда мы используем здесь двунаправленный OneToOne и пытаемся загрузить контракт, тогда работает ленивая загрузка (т. Е. House загружается не сразу), но когда мы пытаемся загрузить House (используя репозиторий House), контракт всегда загружается с нетерпением. Кто-нибудь знает, почему это происходит?

Public class Contract {
  .....
  @onetoone(lazy)
  @JoinColumn(name='houseid', nullable=false)
  Private House house
  .....
}

Public class House {
  .....
  @onetoone(lazy, mappedBy='house')
  Private Contract contract
  .....
}

Я знаю, что это только частичный ответ, без вопросов. Но это очень сильно связано с обсуждаемым здесь вопросом.

person Vaibhav Bansal    schedule 25.07.2016
comment
Вы должны повторно сформулировать свой ответ в виде вопроса. Краткий ответ: при заполнении сущности House Hibernate необходимо знать, является ли контракт NULL или, если Hibernate должен использовать объект Proxy для House. Чтобы это знать, Hibernate необходимо проверить таблицу контрактов и найти текущий дом. И ... если второй запрос уже проверяет таблицу, почему бы не загрузить ее EAGERly? Это в лучшем случае избавляет от гибернации дополнительный запрос (тот же самый запрос, если вы вызываете house.getContract ()). - person Manuel; 22.05.2020