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

Spring Boot 3.0.5中UserRepository测试Client保存失败问题

问题:Spring Boot 3.0.5中保存Client子类失败,提示'specialization'字段无默认值

我正在开发基于Spring Boot 3.0.5的项目,测试UserRepository保存User的子类Client和Hairdresser时,Hairdresser保存正常,但保存Client时出错。

UserRepositoryTest.java代码

package com.edoyou.k2sbeauty.repositories;

import com.edoyou.k2sbeauty.entities.model.Client;
import com.edoyou.k2sbeauty.entities.model.Hairdresser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.TestPropertySource;

import java.time.LocalDateTime;

import static org.assertj.core.api.Assertions.assertThat;

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@TestPropertySource(locations = "classpath:application-test.properties")
public class UserRepositoryTest {
    @Autowired
    private UserRepository userRepository;
    private Client client;
    private Hairdresser hairdresser;

    @BeforeEach
    void setUp() {
        client = new Client();
        client.setFirstName("Barbara");
        client.setLastName("O'Conner");
        client.setEmail("myemail@gmail.com");
        client.setPassword("password");
        client.setPhone("36707733373");
        client.setCreatedAt(LocalDateTime.now());
        client.setUpdatedAt(LocalDateTime.now());

        hairdresser = new Hairdresser();
        hairdresser.setFirstName("Jane");
        hairdresser.setLastName("Doe");
        hairdresser.setEmail("jane.doe@gmail.com");
        hairdresser.setPassword("password");
        hairdresser.setPhone("36707733444");
        hairdresser.setCreatedAt(LocalDateTime.now());
        hairdresser.setUpdatedAt(LocalDateTime.now());
        hairdresser.setSpecialization("Hairstyling");
    }

    @Test
    public void shouldSaveClient() {
        Client savedClient = userRepository.save(client);
        assertThat(savedClient).isNotNull();
        assertThat(savedClient.getId()).isNotNull();
    }

    @Test
    public void shouldSaveHairdresser() {
        Hairdresser savedHairdresser = userRepository.save(hairdresser);
        assertThat(savedHairdresser).isNotNull();
        assertThat(savedHairdresser.getId()).isNotNull();
    }
}

其中shouldSaveHairdresser()测试正常,shouldSaveClient()测试失败。

User.java代码

package com.edoyou.k2sbeauty.entities.model;

import jakarta.persistence.*;
import java.time.LocalDateTime;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "users")
public class User {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(nullable = false)
  private String firstName;

  @Column(nullable = false)
  private String lastName;

  @Column(nullable = false, unique = true)
  private String email;

  @Column(nullable = false)
  private String password;

  @Column(nullable = false)
  private String phone;

  @Column(nullable = false)
  private LocalDateTime createdAt;

  @Column(nullable = false)
  private LocalDateTime updatedAt;

  @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(name = "user_roles",
      joinColumns = @JoinColumn(name = "user_id"),
      inverseJoinColumns = @JoinColumn(name = "role_id"))
  private Set<Role> roles = new HashSet<>();

  public void setId(Long id) {
    this.id = id;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public void setPassword(String password) {
    this.password = password;
  }

  public void setPhone(String phone) {
    this.phone = phone;
  }

  public void setCreatedAt(LocalDateTime createdAt) {
    this.createdAt = createdAt;
  }

  public void setUpdatedAt(LocalDateTime updatedAt) {
    this.updatedAt = updatedAt;
  }

  public void setRoles(Set<Role> roles) {
    this.roles = roles;
  }

  public Long getId() {
    return id;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getEmail() {
    return email;
  }

  public String getPassword() {
    return password;
  }

  public String getPhone() {
    return phone;
  }

  public LocalDateTime getCreatedAt() {
    return createdAt;
  }

  public LocalDateTime getUpdatedAt() {
    return updatedAt;
  }

  public Set<Role> getRoles() {
    return roles;
  }
}

Hairdresser.java代码

package com.edoyou.k2sbeauty.entities.model;

import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "hairdressers")
public class Hairdresser extends User {
  @Column(nullable = false)
  private String specialization;

  @OneToMany(mappedBy = "hairdresser")
  private Set<Appointment> appointments = new HashSet<>();

  public void setSpecialization(String specialization) {
    this.specialization = specialization;
  }

  public void setAppointments(Set<Appointment> appointments) {
    this.appointments = appointments;
  }

  public String getSpecialization() {
    return specialization;
  }

  public Set<Appointment> getAppointments() {
    return appointments;
  }
}

Client.java代码

package com.edoyou.k2sbeauty.entities.model;

import jakarta.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = "clients")
public class Client extends User {
  @OneToMany(mappedBy = "client")
  private Set<Appointment> appointments = new HashSet<>();

  public void setAppointments(Set<Appointment> appointments) {
    this.appointments = appointments;
  }

  public Set<Appointment> getAppointments() {
    return appointments;
  }
}

错误信息

2023-04-27 10:20:42.145 ERROR 43965 --- [main] o.h.e.j.s.SqlExceptionHelper             : Field 'specialization' doesn't have a default value

问题分析与解决方案

问题根源在于JPA继承策略与数据库表结构不匹配:

  1. 当前User类未指定@Inheritance注解,JPA默认使用**单表继承(Single Table)**策略,即所有子类数据都存在父表users中。
  2. 但你给子类Client和Hairdresser都添加了@Table注解指定独立表,同时数据库users表包含了specialization字段(属于Hairdresser的属性)且该字段无默认值。保存Client时,Hibernate会尝试往users表插入数据,但Client没有specialization属性,导致数据库报错。

方案1:使用联合继承(Joined)(推荐,符合当前多表结构)

在父类User上添加联合继承策略,让父表和子表分别存储各自字段:

@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.JOINED) // 添加这行
public class User {
    // ... 原有代码不变
}

同时确保数据库中:

  • clients和hairdressers表有外键关联到users表的id字段
  • users表移除specialization字段(该字段属于hairdressers表)

方案2:使用单表继承(Single Table)

如果想使用单表存储所有用户数据:

  1. 移除子类Client和Hairdresser的@Table注解
  2. 修改父类User,添加鉴别列配置:
@Entity
@Table(name = "users")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "user_type", discriminatorType = DiscriminatorType.STRING)
public class User {
    // ... 原有代码不变
}
  1. 给子类添加鉴别值:
@Entity
@DiscriminatorValue("CLIENT")
public class Client extends User {
    // ... 原有代码不变
}

@Entity
@DiscriminatorValue("HAIRDRESSER")
public class Hairdresser extends User {
    // ... 原有代码不变
}
  1. 修改数据库users表:
    • 添加user_type字段(字符串类型,用于区分用户类型)
    • 将specialization字段设置为允许为null(因为Client不需要该字段)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 14:47:08