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继承策略与数据库表结构不匹配:
- 当前User类未指定
@Inheritance注解,JPA默认使用**单表继承(Single Table)**策略,即所有子类数据都存在父表users中。 - 但你给子类
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)
如果想使用单表存储所有用户数据:
- 移除子类
Client和Hairdresser的@Table注解 - 修改父类
User,添加鉴别列配置:
@Entity @Table(name = "users") @Inheritance(strategy = InheritanceType.SINGLE_TABLE) @DiscriminatorColumn(name = "user_type", discriminatorType = DiscriminatorType.STRING) public class User { // ... 原有代码不变 }
- 给子类添加鉴别值:
@Entity @DiscriminatorValue("CLIENT") public class Client extends User { // ... 原有代码不变 } @Entity @DiscriminatorValue("HAIRDRESSER") public class Hairdresser extends User { // ... 原有代码不变 }
- 修改数据库
users表:- 添加
user_type字段(字符串类型,用于区分用户类型) - 将
specialization字段设置为允许为null(因为Client不需要该字段)
- 添加
内容的提问来源于stack exchange,提问作者EDOYou
相关产品推荐
相关产品推荐

