# OneToMany and Composite Primary Key

**URL:** <https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032>\
**Category:** Hibernate ORM\
**Created:** [February 14, 2024, 8:03pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032 "2024-02-14T20:03:41Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![patient-developer](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/patient-developer/32/3014_2.png) [@patient-developer](https://discourse.hibernate.org/u/patient-developer)\
**Post date:** [February 14, 2024, 8:03pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/1 "2024-02-14T20:03:41Z")

</div>

Hi,

I am trying to understand why my implementation works. As a basic setup, I have a OneToMany mapping with each user having multiple authorities:

```auto
CREATE TABLE users
(
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255) NOT NULL UNIQUE,
    password TEXT NOT NULL,
    is_enabled BOOLEAN NOT NULL
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

CREATE TABLE user_authorities
(
    user_id BIGINT UNSIGNED NOT NULL,
    authority VARCHAR(255) NOT NULL,
    CONSTRAINT uc_user_id_authority UNIQUE (user_id, authority),
    CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users (id)
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8;

```

As an ORM represenation, I have a `UserEntity` class

```auto
package kbh.qata.storage;

import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
@Table(name = "users")
public class UserEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Long id;

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

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

  @Column(name = "is_enabled")
  private boolean enabled;

  @OneToMany(
      cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE},
      mappedBy = "user",
      orphanRemoval = true)
  private Set<UserAuthorityEntity> userAuthorities = new HashSet<>();

  public void addUserAuthority(UserAuthorityEntity userAuthority) {
    this.userAuthorities.add(userAuthority);
    userAuthority.setUser(this);
  }
}

```

together with a `UserAuthorityEntity`class

```auto
package kbh.qata.storage;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Entity
// @IdClass(UserAuthorityId.class)
@Table(name = "user_authorities")
public class UserAuthorityEntity {

  @Id
  @Column(name = "user_id")
  private Long userId;

  // @Id
  @Column(name = "authority")
  private String authority;

  @ManyToOne
  @MapsId
  @JoinColumn(name = "user_id")
  private UserEntity user;
}

```

Now, what bothers me: Why is it enough to have a simply `@Id` annotation in the `UserAuthorityEntity` class? As can be seen from the SQL statements, this does not suffice as a primary key, since only the `user_id` _and_ `authority` describe a unique tuple.

As you can see in the commented out section, I tried to setup a [UserAuthorityId](https://github.com/patient-developer/qata/blob/main/src/main/java/kbh/qata/storage/UserAuthorityId.java) class to be able to to set both `userId` and `authority` as a primary key tuple.

However, this approach does not work. When starting the application, calling the [AdminInitializer](https://github.com/patient-developer/qata/blob/main/src/main/java/kbh/qata/security/AdminInitializer.java) class, Hibernate complains that the `userId` of the `UserAuthorityEntity` is `null`. Which is correct, but why doesn’t Hibernate simply set it as the `id` it references to from the `users` table? Is Hibernate “confused” about the two `@Id` annotations? Do I do something wrong?

Thanky in advance for an support.  
pd

---

<div class="post-metadata">

**Author:** ![mbladel](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/mbladel/32/2128_2.png) [@mbladel](https://discourse.hibernate.org/u/mbladel)\
**Post date:** [February 15, 2024, 8:20am UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/2 "2024-02-15T08:20:55Z")

</div>

Hi @patient-developer, when using `@MapsId` with a composite identifier you should specify which property should correspond to the associated entity’s id: in your case, I believe it should be `@MapsId("userId")`. If that doesn’t work, you might have found a bug when using `@IdClass`.

You could also try using an `@EmbeddedId` mapping for the `UserAuthorityEntity` instead and see if that works as well. Something like:

```java
@Embeddable
public class UserAuthorityId {
  @Column(name = "user_id")
  private Long userId;

  @Column(name = "authority")
  private String authority;
}

@Entity
@Table(name = "user_authorities")
public class UserAuthorityEntity {
  @EmbeddedId
  private UserAuthorityId id;

  @ManyToOne
  @MapsId("userId")
  @JoinColumn(name = "user_id")
  private UserEntity user;
}

```

---

<div class="post-metadata">

**Author:** ![patient-developer](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/patient-developer/32/3014_2.png) [@patient-developer](https://discourse.hibernate.org/u/patient-developer)\
**Post date:** [February 15, 2024, 7:42pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/3 "2024-02-15T19:42:00Z")

</div>

Hi @mbladel,  
thank you very much for your reponse.

The case with `@IdClass` is not working, i.e., see the branch [hibernate-id-class](https://github.com/patient-developer/qata/tree/hibernate-id-class). As said, I just want to know if my understanding is correct and thus my expectations accurate: JPA needs a primary key in order to uniquely identify database rows. Since the table `user_authorities` does not provide such a key but both columns combined describe a unique entry, JPA expects something similar, i.e., providing (annotating) the columns with `@Id` which serve as uniquely identify a row.

So, somehow file a bug report? 🤔

In contrast to that, thank you very much for your example with the approach using `@Embeddable`. I tested it on another branch [hibernate-embedded-id](https://github.com/patient-developer/qata/tree/hibernate-embedded-id) and it seems to work.

---

<div class="post-metadata">

**Author:** ![mbladel](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/mbladel/32/2128_2.png) [@mbladel](https://discourse.hibernate.org/u/mbladel)\
**Post date:** [February 16, 2024, 8:07am UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/4 "2024-02-16T08:07:45Z")

</div>

> So, somehow file a bug report? 🤔

Yes please, you can open a new ticket in our [issue tracker](https://hibernate.atlassian.net) since I believe the `@IdClass` mapping should also work correctly. It would be great if you could attach a simplified reproducer that uses only Hibernate, to ensure the bug is on our side, you can start from our [test case templates](https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java).

---

<div class="post-metadata">

**Author:** ![patient-developer](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/patient-developer/32/3014_2.png) [@patient-developer](https://discourse.hibernate.org/u/patient-developer)\
**Post date:** [February 16, 2024, 8:51pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/5 "2024-02-16T20:51:27Z")

</div>

Hi @mbladel,

I am struggeling to understand how to use your shared [Test Case Template](https://github.com/hibernate/hibernate-test-case-templates/blob/main/orm/hibernate-orm-6/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java). ☹

How do I apply my database schema?

From my side, I already provide a fully working example on the public [hibernate-id-class](https://github.com/patient-developer/qata/tree/hibernate-id-class) branch.

Simply

- checkout the repository
- run a local MariaDB database via docker-compose with the provided `docker-compose.yml`file
- initialize the tables via `./gradlew update`
- start the Spring Boot app and the `AdminInitializer.class` will crash

Would that also suffice as a test-case? 🙂

---

<div class="post-metadata">

**Author:** ![mbladel](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/mbladel/32/2128_2.png) [@mbladel](https://discourse.hibernate.org/u/mbladel)\
**Post date:** [February 19, 2024, 1:49pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/6 "2024-02-19T13:49:11Z")

</div>

> How do I apply my database schema?

The test case templates are configured to run by default on H2 database and with [hibernate.hbm2ddl.auto](https://docs.jboss.org/hibernate/orm/6.4/javadocs/org/hibernate/cfg/SchemaToolingSettings.html#HBM2DDL_AUTO) enabled. You should just add your entity mappings to the classpath (e.g. in the same package as `JPAUnitTestCase.java`), and the schema will be generated for you.

> Would that also suffice as a test-case?

It would be great if you could isolate the problem to a simple case that uses only Hibernate, to make sure the bug is indeed caused by the library and not some third party framework or component that happens to use it. If you really can’t come up with a simplified reproducer you can still open a Jira and attach what you already have, but it probably take more time to troubleshoot the bug so less chances it will be solved soon.

---

<div class="post-metadata">

**Author:** ![patient-developer](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/patient-developer/32/3014_2.png) [@patient-developer](https://discourse.hibernate.org/u/patient-developer)\
**Post date:** [February 19, 2024, 8:47pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/7 "2024-02-19T20:47:24Z")

</div>

Hi @mbladel,  
I did setup a Bug report with the jira issue [HHH-17755](https://hibernate.atlassian.net/browse/HHH-17755) - let’s see what happens. 🙂

Thanks for your support  
– pd

---

<div class="post-metadata">

**Author:** ![patient-developer](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.hibernate.org/patient-developer/32/3014_2.png) [@patient-developer](https://discourse.hibernate.org/u/patient-developer)\
**Post date:** [February 20, 2024, 9:09pm UTC](https://discourse.hibernate.org/t/onetomany-and-composite-primary-key/9032/8 "2024-02-20T21:09:54Z")

</div>

For the sake of completeness, issue [HHH-17755](https://hibernate.atlassian.net/browse/HHH-17755) has been resolved. 🙂
