Saving an entity with @ManyToOne JPA Composite Key used as Ids adds an extra parameter
Asked Answered
C

1

1

Similar to @ManyToOne Referencing a composite key in JPA. This is with an older version of Spring Boot using Hibernate 5.6.

I have three entity classes:

@Entity class A { Long getId(); ... }
@Entity class B { Long getId(); ... }

@Entity
@IdClass(ABPK.class)
@Data
class AB {
  @Id
  @Column(name="a_id", insertable=false, updatable=false)
  private Long aId;
  @Id
  @Column(name="b_id", insertable=false, updatable=false)
  private Long bId;
  @ManyToOne
  @JoinColumn(name="a_id")
  private A a;
  @ManyToOne
  @JoinColumn(name="b_id")
  private B b;
  public AB() { }
  public AB(A a, B b) {
    this.a = a;
    this.aId = a.getId();
    this.b = b;
    this.bId = b.getId();
  }
}

@Data
class ABPK implements Serializable {
  Long aId;
  Long bId;
}

When I try to create and save it

var a = new A("a");
var b = new B("b");
var ab = new AB(a,b);
abDao.saveAndFlush(ab);

I get the oddest error

Caused by: java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 2).

The insert statement it generated is correct in the logs

Hibernate: insert into ab (a_id, b_id) values (?, ?)

I ran through the debugger and it seems to add a third "field" to map when I am only expecting two, the first one appears to be the AB type, followed by A and then B.

Cravens answered 30/4 at 5:36 Comment(0)
N
2

I reproduced the problem with Hibernate 5.6.15 - although exact version should not matter, I think this is a complex area in the specs, and its details commonly misunderstood. Anyway, @MapsId in the relation fields solves the problem for me. I.e.:

@Entity
@IdClass(ABPK.class)
@Data
class AB {
  @Id
  @Column(name="a_id", insertable=false, updatable=false)
  private Long aId;

  @Id
  @Column(name="b_id", insertable=false, updatable=false)
  private Long bId;

  @MapsId("aId")             // <--- HERE
  @ManyToOne
  @JoinColumn(name="a_id")
  private A a;

  @MapsId("bId")             // <--- AND HERE
  @ManyToOne
  @JoinColumn(name="b_id")
  private B b;

  public AB() { }

  public AB(A a, B b) {
    this.a = a;
    this.aId = a.getId();
    this.b = b;
    this.bId = b.getId();
  }
}

Niu answered 30/4 at 7:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.