6
votes

I am experiencing an unexpected behavior while performing a jpql query on an entity having a one-to-one relationship with another. The key point is that the relationship is from the primary key of the root entity to a field of the destination entity which is not the ID.

Here is the example:

// root entity
@Entity
@Table(name = "PERSON")

public class Person {

    @Id
    @Column(name = "PERSON_ID")
    private long id;

    private String name;

    @OneToOne(optional = false)
    @JoinColumn(name = "PERSON_ID", referencedColumnName = "PERSON_ID", insertable = false, updatable = false, unique = true)
    private Address mainAddress;
    ...
}
// referenced entity
@Entity
@Table(name = "ADDRESS")
public class Address {

    @Id
    @Column(name = "ADDRESS_ID")
    private long id;

    @Column(name = "PERSON_ID")
    private long personId;
    ...
}

The following jpql query:

select p from Person p left join fetch p.mainAddress

Produces the following ORACLE SQL query:

SELECT ... FROM PERSON p LEFT OUTER JOIN ADDRESS a ON p.PERSON_ID = a.ADDRESS_ID

While I expected :

SELECT ... FROM PERSON p LEFT OUTER JOIN ADDRESS a ON p.PERSON_ID = a.PERSON_ID

Basically the attribute of the referencedColumnName = "PERSON_ID" attribute is ignored and the join is performed on the primary key.

Somebody can please explain me why?

2
I solved by mapping the reverse relationship and using the mappedBy attribute. This way the ORM understands which is the join column. Thanks @Alan Hay - leocasucci
did you find the solution? - Yingsheng

2 Answers

5
votes

With the @OneToOne mapped as it is, the foreign key is going to be in the Person table, viz. the Person table (if you hadn't specified person_id in the @JoinColumn annotation) should have a FK column 'address_id'.

Form what you expect the generated SQL to look like, it would appear that you want the FK to be in the address table i.e. address has a FK column person_id. You can do this by as follows:

@Entity
@Table(name = "PERSON")

public class Person {

    @Id
    @Column(name = "PERSON_ID")
    private long id;

    private String name;

    @OneToOne(mappedBy = "person")
    private Address mainAddress;

}


@Entity
@Table(name = "ADDRESS")
public class Address {

    @Id
    @Column(name = "ADDRESS_ID")
    private long id;

    @OneToOne(optional = false)
    @JoinColumn(name = "PERSON_ID", insertable = false, updatable = false, unique = true)
    private Person person;

}
0
votes

I think, that @JoinColumn for @OneToOne mapping means a join column in a table of the persistent in which @JoinColumn is specified: Person, (unlike @OneToMany).

So use a more simply and clear mapping

@Entity
@Table(name = "persons")
public class Person {

    @Id
    @Column(name = "f_id")
    private Long id;

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

    @OneToOne(mappedBy = "person", optional = false)
    private Address mainAddress;

}

@Entity
@Table(name = "addreses")
public class Address {

    @Id
    @Column(name = "f_id")
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "fk_person")
    private Person person; 

}