You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot+Hibernate中merge()导致created_date字段为空的问题

SpringBoot+Hibernate中更新现有记录时created_date变为null的问题分析

问题场景

我有一个SpringBoot + Hibernate Web项目,相关代码如下:

Course实体类

@Entity
public class Course {
    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    
    @UpdateTimestamp
    private LocalDateTime lastUpdatedDate;
    
    @CreationTimestamp
    private LocalDateTime createdDate;
    // 省略getter/setter等方法
}

CourseRepository类

@Repository
@Transactional
public class CourseRepository {
    
    @Autowired
    EntityManager em;
    
    public Course save(Course course) {
            em.merge(course);
    }
    // 省略其他方法
}

data.sql初始化数据

insert into course(id, name, created_date, last_updated_date) values (10001, 'abc', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP());
insert into course(id, name, created_date, last_updated_date) values (10002, 'abc', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP());
insert into course(id, name, created_date, last_updated_date) values (10003, 'abc', CURRENT_TIMESTAMP(), CURRENT_TIMESTAMP());

DemoApplication启动类

public class DemoApplication implements CommandLineRunner{

    @Autowired
    private CourseRepository repository;
    
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        repository.save(new Course(10002L, "abcdefghfi"));
    }
}

现象

运行程序后,H2数据库中Course表数据如下:

IDCREATED_DATELAST_UPDATED_DATENAME
100012022-11-02 23:13:51.9267622022-11-02 23:13:51.926762abc
10002null2022-11-02 23:13:52.325534abcdefghfi
100032022-11-02 23:13:51.9307612022-11-02 23:13:51.930761abc

补充:当传入不存在的ID(如1L)时,新增的记录created_date字段正常赋值,不会为null。

原因分析

  1. @CreationTimestamp的触发逻辑:该注解仅在实体**首次被持久化(插入操作)**时自动生成时间戳,更新操作不会触发它的赋值逻辑。
  2. merge方法的行为:当调用em.merge(course)时,若数据库中已存在对应ID的记录,Hibernate会执行更新操作。你创建的new Course(10002L, "abcdefghfi")对象中createdDate属性为null,merge操作会将这个null值覆盖到数据库的现有记录中,导致原有的created_date被置空。
  3. 新增场景的差异:当ID不存在时,merge会执行插入操作,此时@CreationTimestamp生效,自动填充时间戳,所以created_date不会为null。

解决方案

方案1:查询后更新指定字段

修改save方法,先查询现有实体,仅更新需要修改的字段,避免覆盖createdDate:

public Course save(Course course) {
    if (course.getId() != null) {
        Course existingCourse = em.find(Course.class, course.getId());
        if (existingCourse != null) {
            // 仅更新需要修改的字段,比如name
            existingCourse.setName(course.getName());
            // @UpdateTimestamp会自动更新lastUpdatedDate,无需手动设置
            return em.merge(existingCourse);
        }
    }
    // 新增逻辑
    em.persist(course);
    return course;
}

方案2:标记createdDate为不可更新

在实体类的createdDate字段上添加@Column(updatable = false),确保该字段不会被更新操作修改:

@CreationTimestamp
@Column(updatable = false)
private LocalDateTime createdDate;

这样即使merge时传入的实体createdDate为null,也不会覆盖数据库中的原有值。

方案3:启用动态更新

在实体类上添加@DynamicUpdate注解,Hibernate会仅生成包含修改字段的SQL语句,不会触及未修改的createdDate:

@Entity
@DynamicUpdate
public class Course {
    @Id
    @GeneratedValue
    private Long id;
    
    private String name;
    
    @UpdateTimestamp
    private LocalDateTime lastUpdatedDate;
    
    @CreationTimestamp
    private LocalDateTime createdDate;
    // 省略其他方法
}

内容的提问来源于stack exchange,提问作者Ryannnnnnnnn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 07:05:34