Spring Data JPA未触发DDL默认值填充问题求助
我在Oracle中定义了如下表,CREATED_ON和CREATED_BY字段配置了DDL默认值:
CREATE TABLE CONFIGSERVER.PROPERTIES ( ID NUMBER GENERATED ALWAYS AS IDENTITY ( START WITH 1 CACHE 20 ) , APPLICATION VARCHAR2 (4000) , PROFILE VARCHAR2 (4000) , LABEL VARCHAR2 (4000) , PROP_KEY VARCHAR2 (4000) NOT NULL, VALUE VARCHAR2 (4000) NOT NULL, CREATED_ON TIMESTAMP DEFAULT SYSDATE, CREATED_BY VARCHAR2 (100) DEFAULT COALESCE( REGEXP_SUBSTR(SYS_CONTEXT('USERENV','CLIENT_IDENTIFIER'),'^[^:]*'), SYS_CONTEXT('USERENV','SESSION_USER')), UPDATED_ON TIMESTAMP , UPDATED_BY VARCHAR2 (100) ) LOGGING;
用SQLCl执行插入语句时,这两个字段能通过DDL默认值正确填充:
insert into properties (application, profile, label, prop_key, value) values ('nisse', 'bosse', 'bengt', 'olle', 'korv med mos');
查询结果:
SQL> select * from properties where application = 'nisse'; ID APPLICATION PROFILE LABEL PROP_KEY VALUE CREATED_ON CREATED_BY UPDATED_ON UPDATED_BY _____ ______________ __________ ________ ___________ _______________ __________________________________ _______________ _____________ _____________ 25 nisse bosse bengt olle korv med mos 18-NOV-22 07.19.07.000000000 PM CONFIGSERVER
但使用Spring Data JPA插入数据时,这两个字段的默认值并未填充。我的Properties实体类如下:
package com.example.configcrud.model; import javax.persistence.*; import java.sql.Timestamp; @Entity @Table(name = "properties") public class Properties { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "application") private String application; @Column(name = "profile") private String profile; @Column(name = "label") private String label; @Column(name = "prop_key") private String propKey; @Column(name = "value") private String value; @Column(name = "created_on", updatable = false) private Timestamp createdOn; @Column(name = "created_by", updatable = false) private String createdBy; public Properties(String application, String profile, String label, String propKey, String value) { this.application = application; this.profile = profile; this.label = label; this.propKey = propKey; this.value = value; } public Properties() { } public String getApplication() { return application; } public void setApplication(String application) { this.application = application; } public String getProfile() { return profile; } public void setProfile(String profile) { this.profile = profile; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getPropKey() { return propKey; } public void setPropKey(String propKey) { this.propKey = propKey; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Timestamp getCreatedOn() { return createdOn; } public Timestamp setCreatedOn() { return createdOn; } public String getCreatedBy() { return createdBy; } public String setCreatedBy() { return createdBy; } @Override public String toString() { return "Properties [id=" + id + ", " + "application=" + application + ", " + "profile=" + profile + ", " + "label=" + label + "propkey=" + propKey + "value=" + value + "CreatedOn=" + createdOn + "CreatedBy=" + createdBy + "]"; } }
使用的Spring Data JPA依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
请问如何让JPA使用DDL定义的默认值,避免其被覆盖?
JPA默认会把实体类中未赋值的字段以NULL值插入数据库,覆盖DDL定义的默认值。要让数据库使用默认值,需要让JPA在生成INSERT语句时忽略这些字段,或者明确告知JPA使用数据库默认值。可以通过以下几种方式解决:
1. 修改@Column注解,添加insertable=false
在created_on和created_by字段的@Column注解中添加insertable = false,这样JPA生成INSERT语句时不会包含这两个字段,数据库就会自动使用DDL定义的默认值:
@Column(name = "created_on", updatable = false, insertable = false) private Timestamp createdOn; @Column(name = "created_by", updatable = false, insertable = false) private String createdBy;
注意:设置后这两个字段只能由数据库填充,无法通过JPA主动插入值。插入后调用saveAndFlush等方法,JPA会自动从数据库加载生成的字段值到实体对象中。
2. 使用@Generated注解(JPA 2.2+支持)
对于数据库生成的字段,用@Generated注解明确指定生成策略为INSERT时由数据库生成:
@Column(name = "created_on", updatable = false) @Generated(value = GenerationTime.INSERT) private Timestamp createdOn; @Column(name = "created_by", updatable = false) @Generated(value = GenerationTime.INSERT) private String createdBy;
这个注解告诉JPA,这些字段的值由数据库在INSERT操作时生成,JPA不会在INSERT语句中包含它们,同时会在插入后获取生成的值。
3. 修正实体类的setter方法
你的实体类中setCreatedOn和setCreatedBy方法存在问题:没有接收参数,也无法给字段赋值。正确的setter写法应该是:
public void setCreatedOn(Timestamp createdOn) { this.createdOn = createdOn; } public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
虽然这不是默认值不生效的直接原因,但修正后能确保JPA正确加载数据库生成的字段值到实体对象中。
内容的提问来源于stack exchange,提问作者Tallen67

