I have two entities which I would like to join through multiple columns. These columns are shared by an @Embeddable
object that is shared by both entities. In the example below, Foo
can have only one Bar
but Bar
can have multiple Foo
s (where AnEmbeddableObject
is a unique key for Bar
). Here is an example:
@Entity
@Table(name = "foo")
public class Foo {
@Id
@Column(name = "id")
@GeneratedValue(generator = "seqGen")
@SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1)
private Long id;
@Embedded
private AnEmbeddableObject anEmbeddableObject;
@ManyToOne(targetEntity = Bar.class, fetch = FetchType.LAZY)
@JoinColumns( {
@JoinColumn(name = "column_1", referencedColumnName = "column_1"),
@JoinColumn(name = "column_2", referencedColumnName = "column_2"),
@JoinColumn(name = "column_3", referencedColumnName = "column_3"),
@JoinColumn(name = "column_4", referencedColumnName = "column_4")
})
private Bar bar;
// ... rest of class
}
And the Bar class:
@Entity
@Table(name = "bar")
public class Bar {
@Id
@Column(name = "id")
@GeneratedValue(generator = "seqGen")
@SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1)
private Long id;
@Embedded
private AnEmbeddableObject anEmbeddableObject;
// ... rest of class
}
Finally the AnEmbeddedObject
class:
@Embeddable
public class AnEmbeddedObject {
@Column(name = "column_1")
private Long column1;
@Column(name = "column_2")
private Long column2;
@Column(name = "column_3")
private Long column3;
@Column(name = "column_4")
private Long column4;
// ... rest of class
}
Obviously the schema is poorly normalised, it is a restriction that AnEmbeddedObject
's fields are repeated in each table.
The problem I have is that I receive this error when I try to start up Hibernate:
org.hibernate.AnnotationException: referencedColumnNames(column_1, column_2, column_3, column_4) of Foo.bar referencing Bar not mapped to a single property
I have tried marking the JoinColumns are not insertable and updatable, but with no luck. Is there a way to express this with Hibernate/JPA annotations?
This question is related to
java
hibernate
jpa
annotations
If this doesn't work I'm out of ideas. This way you get the 4 columns in both tables (as Bar
owns them and Foo
uses them to reference Bar
) and the generated IDs in both entities. The set of 4 columns has to be unique in Bar
so the many-to-one relation doesn't become a many-to-many.
@Embeddable
public class AnEmbeddedObject
{
@Column(name = "column_1")
private Long column1;
@Column(name = "column_2")
private Long column2;
@Column(name = "column_3")
private Long column3;
@Column(name = "column_4")
private Long column4;
}
@Entity
public class Foo
{
@Id
@Column(name = "id")
@GeneratedValue(generator = "seqGen")
@SequenceGenerator(name = "seqGen", sequenceName = "FOO_ID_SEQ", allocationSize = 1)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumns({
@JoinColumn(name = "column_1", referencedColumnName = "column_1"),
@JoinColumn(name = "column_2", referencedColumnName = "column_2"),
@JoinColumn(name = "column_3", referencedColumnName = "column_3"),
@JoinColumn(name = "column_4", referencedColumnName = "column_4")
})
private Bar bar;
}
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = {
"column_1",
"column_2",
"column_3",
"column_4"
}))
public class Bar
{
@Id
@Column(name = "id")
@GeneratedValue(generator = "seqGen")
@SequenceGenerator(name = "seqGen", sequenceName = "BAR_ID_SEQ", allocationSize = 1)
private Long id;
@Embedded
private AnEmbeddedObject anEmbeddedObject;
}
This worked for me . In my case 2 tables foo and boo have to be joined based on 3 different columns.Please note in my case ,in boo the 3 common columns are not primary key
i.e., one to one mapping based on 3 different columns
@Entity
@Table(name = "foo")
public class foo implements Serializable
{
@Column(name="foocol1")
private String foocol1;
//add getter setter
@Column(name="foocol2")
private String foocol2;
//add getter setter
@Column(name="foocol3")
private String foocol3;
//add getter setter
private Boo boo;
private int id;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "brsitem_id", updatable = false)
public int getId()
{
return this.id;
}
public void setId(int id)
{
this.id = id;
}
@OneToOne
@JoinColumns(
{
@JoinColumn(updatable=false,insertable=false, name="foocol1", referencedColumnName="boocol1"),
@JoinColumn(updatable=false,insertable=false, name="foocol2", referencedColumnName="boocol2"),
@JoinColumn(updatable=false,insertable=false, name="foocol3", referencedColumnName="boocol3")
}
)
public Boo getBoo()
{
return boo;
}
public void setBoo(Boo boo)
{
this.boo = boo;
}
}
@Entity
@Table(name = "boo")
public class Boo implements Serializable
{
private int id;
@Column(name="boocol1")
private String boocol1;
//add getter setter
@Column(name="boocol2")
private String boocol2;
//add getter setter
@Column(name="boocol3")
private String boocol3;
//add getter setter
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "item_id", updatable = false)
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
}
Hibernate is not going to make it easy for you to do what you are trying to do. From the Hibernate documentation:
Note that when using referencedColumnName to a non primary key column, the associated class has to be Serializable. Also note that the referencedColumnName to a non primary key column has to be mapped to a property having a single column (other cases might not work). (emphasis added)
So if you are unwilling to make AnEmbeddableObject
the Identifier for Bar then Hibernate is not going to lazily, automatically retrieve Bar for you. You can, of course, still use HQL to write queries that join on AnEmbeddableObject
, but you lose automatic fetching and life cycle maintenance if you insist on using a multi-column non-primary key for Bar.
Source: Stackoverflow.com