1. 概述

在本教程中,我们将探讨在 JPA 中设置数据库列默认值的几种方式。

我们会介绍如何在实体类中直接设置默认值、通过数据库表定义设置默认值,以及使用 JPA 提供的注解和生命周期回调来实现默认值的设定。

2. 在实体类中设置默认值

最简单的方法是在实体类字段中直接赋值:

@Entity
public class User {
    @Id
    private Long id;
    private String firstName = "John Snow";
    private Integer age = 25;
    private Boolean locked = false;
}

这样,每次通过 new 创建对象时,都会带上这些默认值:

@Test
void givenUser_whenUsingEntityValue_thenSavedWithDefaultValues() {
    User user = new User();
    user = userRepository.save(user);
    
    assertEquals(user.getName(), "John Snow");
    assertEquals(user.getAge(), 25);
    assertFalse(user.getLocked());
}

优点:简单直接,无需数据库干预。

缺点:这些默认值不会反映在数据库表结构中,因此如果字段被设置为 null,依然可以保存成功。

create table user
(
    id     bigint not null constraint user_pkey primary key,
    name   varchar(255),
    age    integer,
    locked boolean
);

也就是说,即使我们显式设置为 null,也不会报错:

@Test
void givenUser_whenNameIsNull_thenSavedNameWithNullValue() {
    User user = new User();
    user.setName(null);
    user.setAge(null);
    user.setLocked(null);
    user = userRepository.save(user);

    assertNull(user.getName());
    assertNull(user.getAge());
    assertNull(user.getLocked());
}

⚠️ 所以,如果你希望数据库层面也强制默认值,这种方法并不合适。

3. 在数据库表定义中设置默认值

我们可以使用 @Column 注解的 columnDefinition 属性来指定列的默认值:

@Entity
public class User {
    @Id
    Long id;

    @Column(columnDefinition = "varchar(255) default 'John Snow'")
    private String name;

    @Column(columnDefinition = "integer default 25")
    private Integer age;

    @Column(columnDefinition = "boolean default false")
    private Boolean locked;
}

生成的数据库表结构如下:

create table user
(
    id     bigint not null constraint user_pkey primary key,
    name   varchar(255) default 'John Snow',
    age    integer      default 25,
    locked boolean      default false
);

这样即使字段未赋值,保存时也会自动填充默认值:

@Test
void givenUser_whenUsingSQLDefault_thenSavedWithDefaultValues() {
    User user = new User();
    user = userRepository.save(user);

    assertEquals(user.getName(), "John Snow");
    assertEquals(user.getAge(), 25);
    assertFalse(user.getLocked());
}

⚠️ 注意:使用这种方式后,首次保存实体时,不能将字段设置为 null,否则会违反数据库约束,具体行为取决于数据库配置。

4. 使用 @ColumnDefault 注解

Hibernate 提供了一个非标准但非常实用的注解 @ColumnDefault,用于在 JPA 层面定义默认值,并反映在生成的 SQL 表结构中:

@Entity
@Table(name="users_entity")
public class UserEntity {
    @Id
    private Long id;

    @ColumnDefault("John Snow")
    private String name;

    @ColumnDefault("25")
    private Integer age;

    @ColumnDefault("false")
    private Boolean locked;
}

生成的建表语句如下:

create table users_entity 
(
    age integer default 25,
    locked boolean default false,
    id bigint not null,
    name varchar(255) default 'John Snow',
    primary key (id)
)

插入数据时,如果只传入 id,其他字段将自动使用默认值:

INSERT INTO users_entity (id) VALUES (?);
void givenUser_whenSaveUsingQuery_thenSavedWithDefaultValues() {
    EntityManager entityManager = // 初始化 entityManager

    Query query = entityManager.createNativeQuery("INSERT INTO users_entity (id) VALUES(?) ");
    query.setParameter(1, 2L);
    query.executeUpdate();

    user = userEntityRepository.find(2L);
    assertEquals(user.getName(), "John Snow");
    assertEquals(25, (int) user.getAge());
    assertFalse(user.getLocked());
}

⚠️ 注意:如果插入语句包含了所有字段,而某些字段值为 null,数据库不会自动应用默认值。也就是说,只有未显式包含的字段才会使用默认值

5. 使用 @PrePersist 生命周期回调

如果你希望在 Java 层控制默认值,可以使用 @PrePersist 注解,在实体保存前自动设置默认值:

@Entity
public class UserEntity {
    @Id
    private Long id;

    private String name;
    private Integer age;
    private Boolean locked;

    @PrePersist
    public void prePersist() {
        if (name == null) {
            name = "John Snow";
        }
        if (age == null) {
            age = 25;
        }
        if (locked == null) {
            locked = false;
        }
    }
}

优点

  • 不依赖数据库配置
  • 可以进行更复杂的逻辑判断
  • 即使字段被显式设为 null,也可以重新设置默认值

测试代码如下:

@Test
void givenUser_whenSaveUsingPrePersist_thenSavedWithDefaultValues() {
    UserEntity user = new UserEntity();
    userEntityRepository.save(user, 3L);

    user = userEntityRepository.find(3L);
    assertEquals(user.getName(), "John Snow");
    assertEquals(25, (int) user.getAge());
    assertFalse(user.getLocked());
}

⚠️ 缺点:需要手动编写逻辑,维护成本略高。

6. 总结

方法 是否反映在数据库 是否支持 null 适用场景
实体字段默认值 简单默认值,不依赖数据库
@Column(columnDefinition) 需要数据库强制默认值
@ColumnDefault JPA + 数据库联合控制
@PrePersist ❌(可自定义) 复杂逻辑控制,或避免数据库依赖

根据你的业务需求和持久化策略,选择合适的方式设置默认值即可。


原始标题:Default Column Values in JPA | Baeldung