【问题标题】:JPA: OneToMany relationship keeps the Collection emptyJPA:OneToMany 关系使 Collection 为空
【发布时间】:2019-01-02 12:10:10
【问题描述】:

我似乎很难理解 JPA 以及 OneToMany 关系的实际运作方式。

例如,假设我有一个对象Class

@Entity
public class Class {
    @Id
    private String className;

    @OneToMany(cascade = Cascade.ALL, orphanRemoval = true)
    private Set<Student> students;

    // Constructors, Getters, Setter
}

我还有一个对象 Student,它包含 Class。

@Entity
public class Student {
    @Id
    private String studentName;

    @ManyToOne
    private Class class;

    // Constructors, Getters, Setter
}

显然,一个学生可以有多个班级,但忘了这一点。

为什么当我创建一个班级,然后使用该班级创建几个学生时,ClassRepository 上的findAll() 会返回一组空的学生。

Class class = new Class("CS", new HashSet<>());
classRepository.save(class); // repository has no special functions

Student student1 = new Student("1", class);
Student student2 = new Student("2", class);

studentRepository.save(student1);
studentRepository.save(student2);

classRepository.findAll() // Returns me a List<Class> with only one class object that has an empty set.

我在想上面的代码应该自动看到这两个学生来自那个班级,所以当我调用 buildingRepository.findAll() 时,它将返回一个 Class 对象,其中学生设置正确。

那我理解错了吗?还是我的代码错了?我怎样才能改变它来修复它?

【问题讨论】:

  • 几个原因:1.你的映射不正确; 2.您正在设置学生班级,但您没有将学生添加到班级。以下是必须映射双向 OneToMany 的方式:docs.jboss.org/hibernate/orm/current/userguide/html_single/…
  • 默认情况下,@...Many 是延迟加载的,这是解决性能问题的最佳选择。当您使用Cascade.ALL 时,您可以让Classpersist 您的学生。对我来说,classRepository.findAll() 只返回 1 个类是正常的,因为你只坚持了 1 个。对于性能问题,我建议你在单独的请求中加载学生,而不是使用 classRepository.findAll()。喜欢classRepository.findClassWithAllStudentsById() 并使用EntityGraph 加载学生。
  • 您应该将 from 对象 class 映射到 student。不分开!例如,您可以使用 Json 映射格式。
  • @JBNizet 我不应该使用班级对象保存两个学生,将学生添加到班级吗?我希望它在这样做时将那些学生保存到课堂上。就像我最后提供的示例代码是我希望我的逻辑工作的方式。因此,当我执行classRepo.findAll() 时,它应该返回一个class,而该集合具有两个student 对象。
  • 如果您的映射是正确的,那么您所做的就足以将关联保存到数据库。如果您清除了持久性上下文或者如果您开始一个新的,那么检索该类将检索其学生。但是在该持久性上下文中,由于您保存的是一个没有任何学生的班级,因此您将检索到的是那个没有任何学生的精确班级:您有责任在持久性上下文(即事务)中保持图形的连贯性

标签: java spring-boot spring-data-jpa


【解决方案1】:

您可以选择:

1.单向@OneToMany:

@Entity
public class Class {
    @Id
    private String className;

    @OneToMany(cascade = Cascade.ALL, orphanRemoval = true)
    private List<Student> students=new ArrayList<>();

    // Constructors, Getters, Setter
}

@Entity
public class Student {
    @Id
    private String studentName;

    // Constructors, Getters, Setter
}

现在,如果我们坚持一个Class

Class class1=new Class("name1");
class1.getStudents().add(new Student("student1Name"));
// then you can make a save of class1 in db
classRepository.save(class);

2。单向@OneToMany@JoinColumn

要解决上述额外的连接表问题,我们只需在组合中添加@JoinColumn

@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "class_id")
private List<Student> students = new ArrayList<>();

3.双向@OneToMany

映射@OneToMany 关联的最佳方式是依靠@ManyToOne 端来传播所有实体状态更改:

@Entity
public class Class {
    @Id
    private String className;

    @OneToMany(
        mappedBy = "class",
        cascade = CascadeType.ALL,
        orphanRemoval = true
    )
    private List<Student> students=new ArrayList<>();

    // Constructors, Getters, Setter
    public void addStudent(Student student) {
        students.add(student);
        student.setClass(this);
    }

    public void removeStudent(Student student) {
        students.remove(student);
        student.setClass(null);
    }
}


@Entity
public class Student {
    @Id
    private String studentName;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "class_id")
    private Class class;

}

并且坚持:

Class c1=new Class("className1");
c1.addStudent(new Student("StudentNAme1"));
c1.addStudent(new Student("StudentNAme2"));
c1.addStudent(new Student("StudentNAme3"));
classRepository.save(c1);

【讨论】:

    猜你喜欢
    • 2017-09-09
    • 2016-05-20
    • 1970-01-01
    • 2020-02-06
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 1970-01-01
    • 2015-12-25
    相关资源
    最近更新 更多