JPA: OneToOne relation owner

I want to make a relation between 2 models by User.id and Address.user_id columns.

I have created two tables with One-To-One relations:

@Entity()
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id", referencedColumnName = "user_id")
    private Address address;

    public int getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Address getAddress() {
        return address;
    }

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

}



@Entity
@Table(name = "address")
public class Address extends com.mezoline.domain.common.Entity {

    @Id
    @GeneratedValue
    private int id;

    @OneToOne(mappedBy = "address")
    private User user;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

}

Here I can already see the problem: hibernate did not generate database column Address.user_id how I expected.

I create Address instance and add that to User:

    User user = entityManager.find(User.class, 69);
    Address address = new Address();
    address.setCity("Тест");
    userTransaction.begin();
    user.setAddress(address);
    entityManager.merge(user);
    userTransaction.commit();

After I call merge(user). Data success saved… without any relation info.

UPD:

With config below, JPA will create relation column Address.user_id (just relation owner was swapped)

public class User { 
    ...
    @OneToOne(cascade = CascadeType.ALL, mappedBy = "user")
    private Address address;
    ...
}

public class Address {
    ....
    @OneToOne()
    @JoinColumn(name = "user_id")
    private User user;
    ....
}

But after save Address.user_id is null… (other columns are being filled)

UPD2:

Thanks. Second config work fine, when was set the inverse side relation field (as suggested in the comments):

Address address = createAddress();
address.setUser(user);
user.setAddress(address);

But I don`t understand, why first config (where the User is the owning side) don`t work.

Answer

That’s because there is a logical error in your expectations about the initial config.

You declared User to be the owning side of the relationship. Owning means the foreign key representing the relationship will be kept in the USER table, not in the Address table.

At the same time, you annotated User.address with @JoinColumn(name = "id", referencedColumnName = "user_id"). Basically, I think you’re trying to force the ADRESS table to hold the foreign key. But in that case, Adress should be the owning side.

If you want the initial config to work, all you should do is you should replace the current JoinColumn annotation with @JoinColumn(name = "address_id"). The foreign key will end up in the USER table. When persisting the entities, User.address must be set; setting Address.user is optional.

If you absolutely need to have the key in the ADDRESS table, make the Address table the owning side (you will then only have to ensure Address.user is set at persist time; however, you will still want the User.address to be set in order for the cascade to work, unless you persist the Address entity explicitly).

Leave a Reply

Your email address will not be published. Required fields are marked *