Marek Sebera
Marek Sebera

Reputation: 40651

@OneToOne bidirectional mapping with @JoinColumn

Let's say I have Person

class Person{
    @Id Integer id;

    @OneToOne
    @JoinColumn(name = "person_id")
    Job myJob;
}

and Job

class Job{
    @Id Integer id;
    Integer person_id;

    @OneToOne
    @PrimaryKeyJoinColumn(name = "person_id")
    Person currentWorker;
}

I'm not able to map the Person and Job to other Entity, when fetching.
What mistake am I doing ?

Upvotes: 4

Views: 29795

Answers (2)

Robert Peake
Robert Peake

Reputation: 118

This discussion assumes that a property such as "update" is assigned to hibernate.hbm2ddl.auto in persistence.xml to enable Hibernate to establish a One-To-One relationship and create a foreign key in the owning entity for the non-owning entity, together with a foreign key identity column in the corresponding table. However, this process will succeed only if the relationship is established at the time the tables are created. Adding @JoinColumn and @OneToOne(mappedBy) to existing entities will cause Hibernate to complain that the FK column doesn't exist in the owning table. So when implementing the relationship between tables containing live data, it is necessary to add the FK column manually. Hibernate will then be able to establish the FK constraint with an odd name like FKg6wt3d1u6o13gdc1hj36ad1ot.

The nuances involved are illustrated by a slightly more detailed example. Consider a database in which the entity Contact will be a common component of various tables (employee, customer, vendor, etc.) joined OneToOne. As a preliminary consideration, while a OneToOne relationship may be bi-directional, its implementation in Hibernate dictates that the common entity be assigned the non-owning designation so that a FK will be created in each owning entity's table. Implementing OneToOne the other way around will cause Hibernate to look for the foreign key in the NON-owning class and throw an error when it isn't found. (Been there, done that.)

@Entity // common, non-owning entity
public class Contact implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)

private Integer id;

    @Column(name="fn")
    private String firstName;

    @Column(name="ln")
    private String lastName;

// "person" is the Contact entity as declared in Director
  @OneToOne(optional=false, mappedBy = "person")    
  private Director director;

 // GETTERS & SETTERS

AND

@Entity // owning entity
public class Director implements Serializable {

private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id", updatable = false, nullable = false)

private Integer id;

    @Column(name="title")
    private String title;

@OneToOne
    @JoinColumn
    private Contact person;

public Integer getId() {
        return id;
}   
public String getTitle() {
        return title;
}
public void setTitle(String title) {
        this.title = title;
}
public Contact getPerson() {
        return person;
}
public void setPerson(Contact person) {
        this.person = person;
}

Assuming the FK column exists in Director, the code produces:

alter table Director 
   add constraint FKg6wt3d1u6o13gdc1hj36ad1ot 
   foreign key (person_id) 
   references Contact (id)

The derivation of the FK name Hibernate assigns to the Contact entity in Director is a bit obscure. It is a concatenation of the variable name assigned to the Contact instance variable in the owning entity (here, person) + "_" + the entity's primary key "id," producing person_id. Note also that the @OneToOne(mappedBy = "person") annotation references this same Contact instance variable. Finally, Hibernate accesses the FK column directly without the need for corresponding getters and setters in the Director class.

Upvotes: 1

Guaido79
Guaido79

Reputation: 1261

Your code should be:

@Entity
public class Person implements Serializable {

    @Id Integer id;

    @OneToOne
    @JoinColumn(name = "id")
    Job myJob;
}

@Entity
public class Job implements Serializable {

    @Id Integer id;

    @OneToOne(mappedBy = "myJob")
    Person currentWorker;
}  

(pay attemption to remove duplicated colum 'person_id' from Job)

or other approach sharing primary key:

@Entity
public class Person {
    @Id Integer id;

    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    Job myJob;
}            

@Entity
public class Job {
    @Id Integer id;
} 

Upvotes: 10

Related Questions