Spring JPA中@EmbeddedId内使用时间戳注解的可行性问询
问题解答
结论:不能直接在@EmbeddedId中使用@CreationTimestamp和@UpdateTimestamp
JPA的主键字段(包括嵌入主键)执行save()操作时,要求必须有明确赋值。而@CreationTimestamp/@UpdateTimestamp依赖数据库自动生成值,此时JPA会判定嵌入主键中的时间戳字段为空,触发数据库非空约束错误。这也是时间戳移到主实体类就能正常工作的原因——非主键字段允许由数据库自动生成值,不会触发主键约束检查。
满足需求的解决方案(时间戳保留在@EmbeddedId中)
要实现数据库自动生成时间戳,同时让JPA在保存后获取到这些值,可通过数据库层配置默认值+触发器,配合JPA字段属性设置实现:
1. 数据库表结构配置(PostgreSQL)
在数据库中为时间戳字段设置默认值,并创建触发器自动更新update_timestamp:
-- 创建表 CREATE TABLE complex_entity ( pkey BIGINT NOT NULL, create_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, update_timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, field_example VARCHAR(255), PRIMARY KEY (pkey, create_timestamp, update_timestamp) ); -- 创建更新update_timestamp的触发器函数 CREATE OR REPLACE FUNCTION update_modified_column() RETURNS TRIGGER AS $$ BEGIN NEW.update_timestamp = CURRENT_TIMESTAMP; RETURN NEW; END; $$ LANGUAGE plpgsql; -- 绑定触发器到表 CREATE TRIGGER update_complex_entity_modtime BEFORE UPDATE ON complex_entity FOR EACH ROW EXECUTE FUNCTION update_modified_column();
2. 修改JPA实体代码
调整嵌入主键类的字段配置,告诉JPA不要自动插入/更新时间戳字段,同时添加方法在保存后刷新实体以获取数据库生成的时间戳:
@Data @Builder @NoArgsConstructor @AllArgsConstructor @Embeddable public class ComplexPkey { @Column(name = "pkey", nullable = false) private Long pkey; // 禁止JPA插入/更新该字段,由数据库负责生成 @Column(name = "create_timestamp", nullable = false, insertable = false, updatable = false) private Date createTimestamp; // 禁止JPA插入/更新该字段,由触发器负责更新 @Column(name = "update_timestamp", nullable = false, insertable = false, updatable = false) private Date updateTimestamp; } @Getter @Setter @Entity @Builder @NoArgsConstructor @AllArgsConstructor public class ComplexEntity { @EmbeddedId private ComplexPkey pkey; private String fieldExample; // 保存/更新后刷新实体,获取数据库生成的时间戳 @PostPersist @PostUpdate private void refreshTimestamps() { EntityManager em = PersistenceContextHolder.getEntityManager(); if (em != null) { em.refresh(this); } } }
说明
insertable = false, updatable = false:告知JPA在生成INSERT/UPDATE语句时忽略这两个时间戳字段,完全由数据库控制它们的值。@PostPersist/@PostUpdate:在实体保存或更新完成后,调用EntityManager.refresh()从数据库拉取最新的实体数据,确保嵌入主键中的时间戳字段被填充。
内容的提问来源于stack exchange,提问作者Bohdan Lishchuk
相关产品推荐
相关产品推荐

