Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

One-to-One bi-directional mapping with composite primary key

Could someone please help me with doing the bi-directional one-to-one JPA mapping for the following relationship using the composite primary key using Hibernate/JPA? enter image description here

like image 243
skip Avatar asked Jan 25 '19 13:01

skip


1 Answers

You can use an @Embeddable to map the composite primary key of both entities and a @MapsId annotation on the address attribute of your User entity to share the primary key.

But before I show you the mapping, please be aware that User is a reserved word in most databases. I recommend you choose a different name for the entity. In my example, I changed it to Person.

Let's start with the @Embeddable that maps the primary keys. I named the class AddressKey. It's a simple Java class that implements the Serializable interface and has the attributes xId and yId. You also need to implement the equals and hashCode methods.

@Embeddable
public class AddressKey implements Serializable {

    private Long xId;
    private Long yId;

    public AddressKey() {}

    public AddressKey(Long xId, Long yId) {
        this.xId = xId;
        this.yId = yId;
    }

    // omit getter and setter for readability

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((xId == null) ? 0 : xId.hashCode());
        result = prime * result + ((yId == null) ? 0 : yId.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        AddressKey other = (AddressKey) obj;
        if (xId == null) {
            if (other.xId != null)
                return false;
        } else if (!xId.equals(other.xId))
            return false;
        if (yId == null) {
            if (other.yId != null)
                return false;
        } else if (!yId.equals(other.yId))
            return false;
        return true;
    }
}

If you annotate the attribute with @EmbeddedId, you can use the AddressKey embeddable to map the primary key of the Address class.

@Entity
public class Address {

    @EmbeddedId
    private AddressKey id;

    private String city;

    private String street;

    private String country;

    @OneToOne(mappedBy = "address")
    private Person person;

    // omit getter and setter methods
}

The mapping of the Person entity looks similar. In addition to the primary key mapping, you need to annotate the address attribute, which maps the association to the Person entity, with @MapsId. That tells Hibernate that it shall use the primary key of the associated Address entity as the primary of the Person entity.

You also need to annotate the address attribute with 2 @JoinColumn annotations to map the foreign keys to the xId and yId columns of your Person table. Without these annotations, Hibernate would map them to the columns address_xId and address_yId.

@Entity
public class Person {

    @EmbeddedId
    private AddressKey id;

    private String name;

    private String society;

    @OneToOne
    @JoinColumn(name="xId", referencedColumnName="xId")
    @JoinColumn(name="yId", referencedColumnName="yId")
    @MapsId
    private Address address;

    // omit getter and setter methods for readability
}
like image 172
Thorben Janssen Avatar answered Nov 03 '22 00:11

Thorben Janssen