SpringBoot中单向One-to-Many映射无法正确建立的问题
场景说明
使用SpringBoot 2、Spring-Data-JPA和H2数据库,实现多学生关联单大学的单向一对多映射。实体类为University(大学)和Student(学生),当前遇到两个核心问题:
实体与业务代码
University.java
@Entity @Table(name = "univ") public class University { @Id @GeneratedValue private Integer Id ; private String Name ; @OneToMany( cascade = CascadeType.ALL ) private List <Student> std = new ArrayList<>() ; public String getName() { return Name; } public void setName(String name) { Name = name; } public List<Student> getStd() { return std; } public void setStd(List<Student> std) { this.std = std; } public University(String name, List<Student> std) { this.Name = name; this.std = std; } }
Student.java
@Entity @Table(name = "student") public class Student { @Id @GeneratedValue private Integer rollno ; private String Name ; @ManyToOne @JoinColumn(name = "univ") private University univ ; public String getName() { return Name; } public void setName(String name) { Name = name; } public University getUniv() { return univ; } public void setUniv(University univ) { this.univ = univ; } public Student(String name) { this.Name = name; } }
主方法代码
public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(H2SetupApplication.class, args); StdRepository stdRepository = context.getBean(StdRepository.class ); UnivRepository univRepository = context.getBean(UnivRepository.class); Student std1 = new Student("John" ) ; Student std2 = new Student("Sam" ) ; List <Student> students = Arrays.asList(std1, std2) ; University university = new University("USCIT", students) ; univRepository.save(university) ; }
核心问题与解决
问题:Student表的UNIV列值为null
原因:单向一对多映射中,仅在University中维护学生列表,未同步设置Student实体的univ关联属性。JPA不会自动双向关联,即使配置了cascade = CascadeType.ALL,也只会级联保存学生实体,但不会填充学生的外键字段。
解决方式:在University的构造方法中,为每个学生设置关联的大学对象:
public University(String name, List<Student> std) { this.Name = name; this.std = std; // 给每个学生设置关联的大学 std.forEach(student -> student.setUniv(this)); }
修改后保存大学时,学生的univ属性会被赋值,Student表的UNIV列将自动填充对应的大学ID。
移动@JoinColumn后的疑问解答
将@JoinColumn(name = "univ")从Student.java移到University.java的@OneToMany注解上后,出现两个异常现象,解析如下:
疑问1:未使用mappedBy()仍为单向映射,为何无第三张关联表?
默认情况下,单向@OneToMany如果不指定@JoinColumn,JPA会生成一张中间关联表。但当在@OneToMany上添加@JoinColumn(name = "univ")时,JPA会将外键直接设置在多的一方(Student表),指定用Student表的univ列作为外键关联University的主键,因此无需生成中间表。
疑问2:Student表中为何出现UNIV_ID列且值为null?
因为Student.java中的@ManyToOne注解依然存在。@ManyToOne默认会自动生成一个外键列,命名规则为关联实体名+_id(此处关联实体为University,因此生成univ_id列,即UNIV_ID)。由于代码中未给学生的univ属性赋值,同时原来的@JoinColumn会在Student表额外生成univ列,最终导致Student表存在两个外键列,且均未被赋值,因此UNIV_ID列值为null。
内容的提问来源于stack exchange,提问作者humblefool

