2015-07-20 8 views
2

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

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

Мы сделали это правильно, следующим образом:

@Entity 
@Table(name = "user") 
class User { 
    @Id 
    @Column(name = "user_id") 
    private Long id; 

    //... 

    @OneToOne(cascade = CascadeType.MERGE, mappedBy = "user") 
    private Address address; // this attribute is crucial 
} 

@Entity 
@Table(name = "address") 
class Address { 
    @Id 
    @Column(name = "address_id") 
    private Long id; 

    @OneToOne 
    @JoinColumn(name = "user_id") 
    private User user; // this attribute is not needed for the business at all, but mappedBy requires it 

    //... 

} 

База данных:

-- SQL: 
CREATE TABLE user 
(
    user_id INT NOT NULL, 
    -- ... 
    CONSTRAINT user_pk PRIMARY KEY (user_id) 
); 

CREATE TABLE address 
(
    address_id INT NOT NULL, 
    user_id INT NOT NULL, 
    -- ... 
    CONSTRAINT address_pk PRIMARY KEY (address_id), 
    CONSTRAINT address_user_id_fk FOREIGN KEY (user_id) REFERENCES user (user_id), 
    CONSTRAINT address_user_id_uk UNIQUE (user_id) -- it says it's a one to one relation for now, if in the future it won't be anymore, just remove this constraint 
); 

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

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

ответ

3

Стандартное решение состоит в том, чтобы правильно обновлять обе стороны bidirectional association (хотя для сохранения ассоциации в базу данных необходимо обновить только принадлежащую сторону). Добавить в Address сеттер в User классе:

public void setAddress(Address address) { 
    this.address = address; 
    address.setUser(this); 
} 

Кроме того, вы можете расширить возможности каскадирования для address собственности включить PERSIST, а также, так что она всегда сохраняется вместе с пользователем:

@OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "user") 
private Address address; 

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

user.setAddress(address); 
session.persist(user); 
+0

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

0

Если «частный пользователь пользователя» не требуется, удалите его и удалите также в MULTIBY в сущности «Пользователь». Используйте однонаправленное отношение.

В случае вашего примера mappedBy означает, что владельцем ассоциации является объект «Адрес», поэтому сохраните экземпляр адреса, а не пользователя. Как:

adress.setUser(user); 
session.save(adress); 
+0

Как я мог это сделать, если внешний ключ по адресу? –

+0

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

+0

К сожалению, это вызывает ошибку: «Отсутствует столбец: address_address_id в public.user», что я имею в виду, когда я сказал, что mappedBy необходимо указать, что внешний ключ находится на другом объекте. –

0

Вам нужно добавить CasecadeType.PERSIST сделать создание адреса casecade с созданием пользователя.

В коде Java, вам нужно сделать:

user.setAddress(address); 
address.setUser(user); 
session.persist(user); 

Тогда ваш пользователь будет создан с адресом.

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

// your code to persist a new User and Address 
session.flush(); 
session.refresh(user); 

Если это не то, что вы хотите, то вам нужно использовать свой собственный код Java и дать подробное описание того, что вы ожидаете.

+0

Могу ли я опустить строку 'address.setUser (user);'? Если бы я мог, это был ответ. –

+0

попробуйте! Для меня это должно работать – Qianlong

+0

К сожалению, это вызывает исключение: «ОШИБКА: значение null в столбце« user_id »нарушает не-нулевое ограничение –