从零开始 Spring Boot 51:JPA 中的默认列值

这篇具有很好参考价值的文章主要介绍了从零开始 Spring Boot 51:JPA 中的默认列值。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

从零开始 Spring Boot 51:JPA 中的默认列值

从零开始 Spring Boot 51:JPA 中的默认列值

图源:简书 (jianshu.com)

JPA 是一个 ORM 框架,因此,通常我们需要在实体类中定义表结构,这其中就包含可能的字段默认值。

本文介绍如何在 Hibernate(JPA)中设置默认列值(Default Column Value)。

默认属性值

最简单的方式是对实体类指定一个默认的属性值,比如:

@Data
@Table(name = "USER_TREE")
@Entity
public class Tree {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private Integer age = 5;
}

测试用例:

@Test
void testAddTreeWithDefaultValue(){
    Tree tree = new Tree();
    treeRepository.save(tree);
    Assertions.assertEquals(5, tree.getAge());
}

这样做的缺点是由 Hibernate 自动生成的表结构中并不会体现字段的默认值:

CREATE TABLE `user_tree` (
  `id` bigint NOT NULL,
  `age` int NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

一般来说是不会产生什么影响的,但如果我们直接在数据库中执行 INSERT SQL,并且期望对拥有默认值的字段使用缺省,就无法实现。

columnDefinition

通过@ColumncolumnDefinition属性,我们可以指定表结构的字段定义,可以利用这一点指定 DDL 语句中的字段默认值:

@Data
@Table(name = "USER_TEACHER")
@Entity
public class Teacher {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(columnDefinition = "varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'icexmoon'")
    private String name;
}

Hibernate 生成的表结构中的确会出现字段的默认值:

CREATE TABLE `user_teacher` (
  `id` bigint NOT NULL,
  `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'icexmoon',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci

但遗憾的是,Hibernate 并不能识别columnDefinition属性中的表字段默认值,并像我们期待的那样在添加实体实例时自动使用:

@Test
void testAddTeacherWithDefaultValue() {
    Teacher teacher = new Teacher();
    teacherRepository.save(teacher);
    var findTeacher = teacherRepository.findAll().stream().findFirst().get();
    Assertions.assertNull(findTeacher.getName());
}

这个示例中添加到数据库中的Teacher实际上其namenull

@DynamicInsert

可以使用@@DynamicInsert解决上述问题,这个注解可以让实体的 INSERT SQL 排除值为null的字段:

@DynamicInsert
// ...
public class Teacher {
	// ...
}

测试用例:

@Test
void testAddTeacherWithDefaultValue() {
    Teacher teacher = new Teacher();
    teacherRepository.save(teacher);
    var findTeacher = teacherRepository.findAll().stream().findFirst().get();
    Assertions.assertNotNull(findTeacher.getName());
    Assertions.assertEquals("icexmoon", findTeacher.getName());
}

可以看到此时的 Hibernate SQL 日志:

Hibernate: insert into user_teacher (id) values (?)

所以自然而然的,插入的数据中name字段使用了 DDL name 字段的默认值。

但这样做依然有一个问题——不会体现在我们用于插入的实体上,需要重新从数据库加载实体才行:

@Test
void testAddTeacherWithDefaultValue2() {
    Teacher teacher = new Teacher();
    teacherRepository.save(teacher);
    Assertions.assertNull(teacher.getName());
    // ...
}

可以看到,Hibernate 并不会在这种情况下帮助我们自动重新加载实体(以获取通过数据库指定的字段默认值)。

默认属性+columnDefinition

更合理的做法是结合默认属性以及用columnDefinition指定 DDL 字段默认值:

@Getter
@Setter
@Entity
@ToString
@EqualsAndHashCode
@Table(name = "USER_STUDENT")
@Accessors(chain = true)
public class Student {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @EqualsAndHashCode.Exclude
    private Long id;

    public static final String DEFAULT_NAME = "icexmoon";
    @Column(name = "NAME", columnDefinition = "varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT 'icexmoon'")
    private String name = DEFAULT_NAME;

    public static final LocalDate DEFAULT_BIRTH_DAY = LocalDate.of(2022, 1, 1);
    @Setter(AccessLevel.NONE)
    @Column(name = "BIRTH_DAY", columnDefinition = "date DEFAULT '2002-01-01'")
    private LocalDate birthDay = DEFAULT_BIRTH_DAY;

    @Transient
    @Setter(AccessLevel.NONE)
    @Getter(AccessLevel.NONE)
    @EqualsAndHashCode.Exclude
    @Nullable
    private Integer age;

    public static final Gender DEFAULT_GENDER = Gender.MALE;
    @Enumerated(EnumType.ORDINAL)
    @Column(name = "gender", columnDefinition = "tinyint DEFAULT '0'")
    private Gender gender = DEFAULT_GENDER;
    // ...
}

这样做其实依然有个缺陷,如果要修改属性默认值,最好同时修改columnDefinition中的default值,否则就会出现语义上的不一致。我有尝试过在指定columnDefinition值时用表达式指定默认值,但只能使用常量表达式,无法实现复杂语义。

测试用例:

@Test
void testNewStudentWithDefaultValue() {
    Student student = new Student();
    studentRepository.save(student);
    Assertions.assertEquals(Student.DEFAULT_NAME, student.getName());
    Assertions.assertEquals(Student.DEFAULT_BIRTH_DAY, student.getBirthDay());
    Assertions.assertEquals(Student.DEFAULT_GENDER, student.getGender());
    var findStudent = studentRepository.findOne(Example.of(new Student().setName(Student.DEFAULT_NAME))).get();
    Assertions.assertNotNull(findStudent);
    Assertions.assertEquals(Student.DEFAULT_NAME, findStudent.getName());
    Assertions.assertEquals(Student.DEFAULT_BIRTH_DAY, findStudent.getBirthDay());
    Assertions.assertEquals(Student.DEFAULT_GENDER, findStudent.getGender());
}

The End,谢谢阅读。

可以从这里获取本文的完整测试用例。文章来源地址https://www.toymoban.com/news/detail-510945.html

参考资料

  • Default Column Values in JPA | Baeldung
  • hibernate设置默认值

到了这里,关于从零开始 Spring Boot 51:JPA 中的默认列值的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 从零开始学Spring Boot系列-前言

    在数字化和信息化的时代,Java作为一种成熟、稳定且广泛应用的编程语言,已经成为构建企业级应用的首选。而在Java生态系统中,Spring框架无疑是其中最为耀眼的一颗明星。它提供了全面的编程和配置模型,用于构建企业级应用。随着Spring Boot的出现,这一框架变得更加易于

    2024年02月22日
    浏览(56)
  • 从零开始学Spring Boot系列-SpringApplication

    SpringApplication类提供了一种从main()方法启动Spring应用的便捷方式。在很多情况下, 你只需委托给 SpringApplication.run这个静态方法 : 当应用启动时, 你应该会看到类似下面的东西: 默认情况下会显示INFO级别的日志信息, 包括一些相关的启动详情, 比如启动应用的用户等。 通过

    2024年04月08日
    浏览(60)
  • 从零开始 Spring Boot 63:Hibernate 继承映射

    图源:简书 (jianshu.com) 关系型数据库设计中是不存在继承概念的,但实体类可以用继承来组织代码结构,所以需要用一种方式将实体类的继承结构映射到表结构。 本文将介绍几种在 JPA(Hibernate)中映射实体类继承层次的方式。 第一种方式是用 @MappedSuperclass 标记超类(Super

    2024年02月12日
    浏览(49)
  • 从零开始学Spring Boot系列-集成mybatis

    在Spring Boot的应用开发中,MyBatis是一个非常流行的持久层框架,它支持定制化SQL、存储过程以及高级映射。在本篇文章中,我们将学习如何在Spring Boot项目中集成MyBatis,以便通过MyBatis进行数据库操作。 首先,我们需要在项目中添加MyBatis的依赖。在Spring Boot中,我们通常会使

    2024年03月10日
    浏览(126)
  • 从零开始学Spring Boot系列-集成Kafka

    Apache Kafka是一个开源的分布式流处理平台,由LinkedIn公司开发和维护,后来捐赠给了Apache软件基金会。Kafka主要用于构建实时数据管道和流应用。它类似于一个分布式、高吞吐量的发布-订阅消息系统,可以处理消费者网站的所有动作流数据。这种动作流数据包括页面浏览、搜

    2024年03月21日
    浏览(59)
  • Spring Boot(04):让你的Spring Boot应用“火力全开”,从零开始学习starter

            Spring Boot是一款非常流行的Java开发框架,其具有快速开发、自动化配置、内嵌服务器、易于扩展等特点,因此备受开发者欢迎。在日常开发中,我们经常需要在不同的环境中进行测试和部署,此时,如何实现开发、测试、生产环境的快速切换,成为了我们需要解决

    2024年04月13日
    浏览(57)
  • 从零开始 Spring Boot 37:初始化 ApplicationContext

    图源:简书 (jianshu.com) 从前文可以知道,作为 Ioc 容器的 ApplicationContext,需要进行一系列步骤来初始化以最终就绪(对于 Web 应用来说就是可以提供Http服务)。 这些步骤大概可以分为以下内容: 准备上下文关联的 Environment 。 初始化 ApplicationContext( ApplicationContextInitializers

    2024年02月08日
    浏览(42)
  • 从零开始 Spring Boot 52:@Embedded 和 @Embeddable

    图源:简书 (jianshu.com) 这篇文章会介绍 @Embedded 和 @Embeddable 两个注解在 JPA 中的用法。 先看一个示例: 这里使用了 Lombok 相关注解(比如 @Builder )帮助构建实体类,详细内容可以阅读我的相关文章。 user_student 是一个学生表,其中的 contacts_ 开头的字段保存联系人信息,这体

    2024年02月12日
    浏览(65)
  • 从零开始学Spring Boot系列-外部化配置

    Spring Boot 允许你将配置外部化,以便可以在不同的环境中使用相同的应用程序代码。可以使用属性文件、YAML文件、环境变量和命令行参数将配置外部化。属性值可以通过使用 @Value 注解直接注入 bean,可以通过 Spring 的 Environment 抽象访问,也可以通过 @ConfigurationProperties。 Sp

    2024年04月10日
    浏览(105)
  • 从零开始 Spring Boot 38:Lombok 与依赖注入

    图源:简书 (jianshu.com) 在之前的文章中,我详细介绍了 Lombok 的用法,考虑到在 Spring 中使用依赖注入(DI)是如此的频繁,因此有必要讨论使用 Lombok 时可能对依赖注入造成的影响。 我们都知道,Spring 中的依赖注入分为三种情况: 通过属性进行依赖注入。 通过构造器进行依

    2024年02月08日
    浏览(66)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包