I have a database where the primary key is a composite key which is composed of a varchar and a timestamp. Each time an entity is modified or inserted, a new entry is added to the database.
How can I model this with a JPA repository? This is what I have right now.
Relevant parts of entity class:
@Entity
@Table(name = "permissions", schema = "auth")
@IdClass(PermissionId.class)
public class Permission implements Serializable {
@Id
@Column(name = "permissionid")
private String permissionId;
@Id
@Column(name = "createdat")
@UpdateTimestamp
private Instant createdAt;
...
}
ID class:
public class PermissionId implements Serializable {
private String permissionId;
private Instant createdAt;
}
When I create a new entity, I get an error saying that createdAt cannot be null. How can I achieve my desired behavior? I cannot change the database schema.
Edit
I found a workaround
@Query(
value = "INSERT INTO auth.permissions (permissionID, createdAt,...) VALUES (:permissionID, NOW(), ...)",
nativeQuery = true
)
@Modifying
recordPermission(String permissionID, ...);
This works, but I'm still interested in knowing if there is a "correct " way to do it.
A generic and JPA-standard way to modify the content of an entity in response to its storage operations (create, update, read, delete) is the mechanism of Entity Listeners, described in ch 3.5 of the JPA 2.1 specification. For this case specifically, the @PrePersist
listener method, that JPA calls just before persisting an entity (creating/inserting the DB records).
You can place the listener in the entity class itself, e.g.:
@Entity
@Table(name = "permissions", schema = "auth")
@IdClass(PermissionId.class)
public class Permission implements Serializable {
...
@Id
@Column(name = "createdat")
private Instant createdAt;
@PrePersist
void prePersist() {
this.createdAt = Instant.now();
}
...
}
Or in a different class like:
public class PermissionEntityListener {
@PrePersist
void prePersist(Permission entity) { // method name can be anything
entity.setCreatedAt(Instant.now());
}
}
If the listener is in a separate class, you have to activate it either with an annotation in the entity class:
@Entity
@Table(name = "permissions", schema = "auth")
@IdClass(PermissionId.class)
@EntityListeners(PermissionEntityListener.class) // <----- HERE
public class Permission implements Serializable {
...
}
Or in the XML descriptor, META-INF/orm.xml
:
<entity-mappings>
...
<entity-listeners><!-- can be found at different places, please check specs -->
<entity-listener class="com.yourpackage.PermissionEntityListener">
<pre-persist method-name="prePersist" />
</entity-listener>
</entity-listeners>
...
</entity-mappings>
There are lots of information online and in the specs for the Entity Listeners.