17


I have to work with Hibernate and I am not very sure how to solve this problem, I have 2 tables with a 1..n relationship like this:

-------
TABLE_A
-------
col_b (pk)
col_c (pk)
[other fields]

-------
TABLE_B
-------
col_a (pk)
col_b (pk) (fk TABLE_A.col_b)
col_c (fk TABLE_A.col_c)
[other fields]

How can I manage this with Hibernate?

I do not have any idea how to declare a foreign key that would contain a part of primary key.

My database schema is generated from the Hibernate model.

Jagger
  • 10,350
  • 9
  • 51
  • 93
  • 3
    If you want to fight hibernate... go ahead and use composite keys. If you want a much easier life, ensure each table has a single primary key column. Trust me, it's not worth the fight... it's a battle which will cost you many hours in the future – lance-java Sep 17 '15 at 08:13
  • What do you mean by "part of primary key"? Composite primary keys in Hibernate are quite simple, but you shouldn't try to complicate that by trying to use only a part of it. Basicly you need a composite key class. There is an answer here with all basic details: http://stackoverflow.com/questions/3585034/how-to-map-a-composite-key-with-hibernate – Karolis Sep 17 '15 at 08:15
  • There's a fairly thorough answer here: http://stackoverflow.com/a/3588400/3166303 – leeor Sep 17 '15 at 08:16
  • For one-to-many mappings you don't require a composite key. It is so so easier to manage this situation without using composite key. – We are Borg Sep 17 '15 at 08:18
  • @LanceJava In this case I need to use composite keys because of readability. – Jagger Sep 17 '15 at 08:20
  • @leeor This answer does not help at all in my case. It is only about how to define a composite key. – Jagger Sep 17 '15 at 08:22
  • https://stackoverflow.com/questions/47890019/hibernate-model Could you please suggest on this query. – Java Techie Dec 19 '17 at 15:21

2 Answers2

15

I have found two solutions to this problem.

The first one is rather a workaround and is not so neat as the second one.

Define the primary key of the B entity as composite key containing col_a, col_b, and col_c and what was supposed to be the primary key in the first place, define as unique constraint. The disadvantage is that the column col_c is not really conceptually a part of primary key.

@Entity
class A {
  @Id
  private int b;
  @Id
  private int c;
}

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = { "a", "b" }) })
class B {
  @Id
  private int a;

  @Id
  @ManyToOne(optional = false)
  @JoinColumns(value = {
          @JoinColumn(name = "b", referencedColumnName = "b"),
          @JoinColumn(name = "c", referencedColumnName = "c") })
  private A entityA;
}

The second uses @EmbeddedId and @MapsId annotations and does exactly what I wanted to be done at the very beginning.

@Entity
class A {
  @Id
  private int b;
  @Id
  private int c;
}

@Embeddable
class BKey {
  private int a;
  private int b;
}

@Entity
class B {
  @EmbeddedId
  private BKey primaryKey;

  @MapsId("b")
  @ManyToOne(optional = false)
  @JoinColumns(value = {
          @JoinColumn(name = "b", referencedColumnName = "b"),
          @JoinColumn(name = "c", referencedColumnName = "c") })
  private A entityA;
}
Jagger
  • 10,350
  • 9
  • 51
  • 93
  • To get above code work you also need to implement Serializable for all above classes – Baimyrza Shamyr Nov 21 '19 at 08:47
  • 2
    I tried this way and I'm getting `Unable to find column reference in the @MapsId mapping: c`. Any idea?. TIA – Yuliban Feb 28 '20 at 18:34
  • Is it a runtime or compile error? Could you post your code somewhere so that I can have a look at it? – Jagger Feb 29 '20 at 18:23
  • Is it possible to have MapsId for two attrributes? For example, if there was an attribute 'd' which is also part of both keys (same as b)? – schoenk Feb 04 '21 at 18:41
  • @schoenk Maybe try with another `@MapsId("d")` annotation, so that you will have two annotations in the end: `@MapsId("b")` and `@MapsId("d")`. – Jagger Feb 05 '21 at 08:10
  • 1
    Unfortunately it is not possible to have multiple ´@MapsId´ annotations on the same attribute. But I managed to solve it for my use case by using ´@JoinFormula´ instead of ´@JoinColumn´ for the columns that are used in Primary Key as well as Foreign Key. – schoenk Feb 10 '21 at 14:47
-1

Jagger's second solution that is my first reaction, with @EmbededId and @MapsId. The following is another way based on his second solution but without using @MapsId. `

@Entity
class A {
  @Id
  private int b;
  @Id
  private int c;
}

@Embeddable
class BKey {
  private int a;
  private int b;
}

@Entity
class B {
  @EmbeddedId
  private BKey primaryKey;

  @ManyToOne(optional = false)
  @JoinColumns(value = {
          @JoinColumn(name = "b", referencedColumnName = "b", insertable= false, updatable= false),
          @JoinColumn(name = "c", referencedColumnName = "c") }, )
  private A entityA;
}
Jagger
  • 10,350
  • 9
  • 51
  • 93
  • 1
    This won't work and ends with an exception `org.hibernate.AnnotationException: Mixing insertable and non insertable columns in a property is not allowed` There is a Hibernate [bug](https://hibernate.atlassian.net/browse/HHH-6221) for it . – Jagger Sep 25 '17 at 08:15