DEV Community

NT
NT

Posted on

Spring Boot: Multiple Primary Keys via JPA

Goal

I wanted to have a simple database record with 2 primary keys in certain order, with a field that contains a simple value.

Java Persistent API with Spring

Using Java Persistent API (JPA), I thought that this would automatically set up primary keys in the order in the class, but this doesn't work the way intended.

@Entity
public class DataEntry {
    @Id
    private Integer deviceID;
    @Id
    private LocalDateTime measuredOn;

    private Integer value;

/// getters and setters
}
Enter fullscreen mode Exit fullscreen mode

Looking at this web page, I figured I need an ID Class.
https://www.baeldung.com/jpa-composite-primary-keys

public class DataEntryId implements Serializable {
    private Integer deviceID;
    private LocalDateTime measuredOn;

    public DataEntryId(Integer deviceID, LocalDateTime measuredOn) {
        this.deviceID = deviceID;
        this.measuredOn = measuredOn;
    }

/// equals and hashCode
}
Enter fullscreen mode Exit fullscreen mode

With the ID Class defined, now that I can use it as an ID class in the annotation.

@Entity
@IdClass(DataEntryId.class)
public class DataEntry {
///....
}
Enter fullscreen mode Exit fullscreen mode

Running the Spring Boot app with the above would create a table structure correctly when the app started.

mysql> desc data_entry;
+-------------+-------------+------+-----+---------+-------+
| Field       | Type        | Null | Key | Default | Extra |
+-------------+-------------+------+-----+---------+-------+
| deviceid    | int         | NO   | PRI | NULL    |       |
| measured_on | datetime(6) | NO   | PRI | NULL    |       |
| value       | int         | YES  |     | NULL    |       |
+-------------+-------------+------+-----+---------+-------+

mysql> show create table data_entry;
| data_entry | CREATE TABLE `data_entry` (
  `deviceid` int NOT NULL,
  `measured_on` datetime(6) NOT NULL,
  `value` int DEFAULT NULL,
  PRIMARY KEY (`deviceid`,`measured_on`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci |
Enter fullscreen mode Exit fullscreen mode

Problem

However, in trying to store a data entry to this table, the following code generate an exception.

        DataEntry dataEntry = new DataEntry();
        dataEntry.setDeviceID(deviceId);
        dataEntry.setMeasuredOn(measuredOn);
        dataEntry.setValue(value);
        dataEntryRepository.save(dataEntry);
Enter fullscreen mode Exit fullscreen mode

Error:
org.hibernate.InstantiationException: Unable to locate constructor for embeddable : com.example.AccessData.DataEntryId

Solution

A subtle requirement in the ID class is that it has to have a default (no arg) constructor.

    public DataEntryId() {
    }
Enter fullscreen mode Exit fullscreen mode

Top comments (0)