Play Framework 2.3.8 中与 Ebeans 的 OneToOne 关系
OneToOne relationship with Ebeans in Play Framework 2.3.8
请原谅我,如果这是微不足道的 - 我白天是一名 C#/.NET 后端工程师,并且是 Play Framework 和 EBean 以及与此环境相关的所有其他事物的新手。
我正在使用内存中的 H2 数据库和 javax.persistence 库来管理我的业务 layer/persistence 层。我定义了三个实体(课程、学生、成绩单),并试图通过使用带有初始数据的 yaml 文件来播种。
到目前为止,我已经设法创建、保留和检索课程和学生之间的多对多关系。
但是,我在创建学生和成绩单之间的一对一关系时遇到了问题。在用初始数据播种数据库后,我尝试通过查找检索 Student 或 Transcript 对象,例如
FIND.where().eq(ID, id).findUnique()
对另一个对象的引用为空(Transcript 对象对其 Student 属性 有一个空引用,Student 对象对其 Transcript 属性 有一个空引用)。
当我保存到数据库时(请参阅下面的 Global.java),学生似乎确实引用了成绩单,就在使用调试器逐步执行并检查属性时。
这是学生实体(注意@OneToOne 成绩单 属性):
@Entity
@Table(name="students")
public class Student extends Model {
private static final String ID = "id";
@Id
@Constraints.Required
@Formats.NonEmpty
public String id;
@Constraints.Required
public String username;
@Constraints.Required
public String password;
@Constraints.Required
public String fullname;
@Constraints.Required
@ManyToMany
public List<Course> coursesPreferred = new ArrayList<Course>();
@Constraints.Required
public int numCoursesPreferred;
@Constraints.Required
@OneToOne
public Transcript transcript;
// -- Queries
private static final Model.Finder<String, Student> FIND =
new Model.Finder<>(String.class, Student.class);
/**
* Returns all students.
*/
public static List<Student> findAll() {
return FIND.all();
}
/**
* Returns the student with the given ID.
*
* @param id student ID
*/
public static Student findById(String id) {
return FIND.where().eq(ID, id).findUnique();
}
// --
public String toString() {
return "Student{" + fullname + "}";
}
}
这是成绩单实体(注意@OneToOne Student 属性,按成绩单映射):
@Entity
@Table(name="transcripts")
public class Transcript extends Model {
private static final String ID = "id";
@Id
@Constraints.Required
@Formats.NonEmpty
public String id;
@Constraints.Required
@OneToOne(mappedBy="transcript")
public Student student;
@Constraints.Required
public int creditsEarned;
// -- Queries
private static final Model.Finder<String, Transcript> FIND =
new Model.Finder<>(String.class, Transcript.class);
/**
* Returns all transcripts.
*/
public static List<Transcript> findAll() {
return FIND.all();
}
/**
* Returns the transcript with the given ID.
*
* @param id transcript ID
*/
public static Transcript findById(String id) {
return FIND.where().eq(ID, id).findUnique();
}
// --
public String toString() {
return "Transcript{" + id + "}";
}
}
这是包含初始数据的 yaml 文件:
# Courses
courses:
- &course6476 !!models.Course
id: 6476
tag: "CS 6476"
name: "Computer Vision"
abbrev: "CV"
core: Yes
- &course6035 !!models.Course
id: 6035
tag: "CS 6035"
name: "Introduction to Information Security"
abbrev: "IIS"
core: No
- &course6210 !!models.Course
id: 6210
tag: "CS 6210"
name: "Advanced Operating Systems"
abbrev: "AOS"
core: Yes
# Transcripts
transcripts:
- &transcript0001 !!models.Transcript
id: 0001
creditsEarned: 42
# Students
students:
- &student0001 !!models.Student
id: 0001
username: "joeschmoe"
password: "password1234"
fullname: "Joe Schmoe"
numCoursesPreferred: 2
coursesPreferred:
- *course6210
- *course6035
transcript: *transcript0001
最后,这是读取和保存数据的 Global.java 文件:
public class Global extends GlobalSettings {
private static final String INITIAL_DATA_FILE = "initial-data.yml";
private static final String COURSES = "courses";
private static final String STUDENTS = "students";
private static final String TRANSCRIPTS = "transcripts";
@Override
public void onStart(Application app) {
InitialData.insert(app);
}
private static class InitialData {
private static void insert(Application app) {
if (Student.findAll().size() == 0) {
Map<String, List<?>> all =
(Map<String, List<?>>) Yaml.load(INITIAL_DATA_FILE);
Ebean.save(all.get(COURSES));
Ebean.save(all.get(TRANSCRIPTS));
Ebean.save(all.get(STUDENTS));
}
}
}
}
有什么我可能在这里遗漏的想法吗?我是否需要更改指定 OneToOne 关系的方式? debug/troubleshoot 的额外步骤?任何信息都非常感谢,因为我碰壁了。
在此先致谢!
我想通了。检索 OneToOne 属性 值失败的查询是:
FIND.where().eq(ID, id).findUnique();
因此,例如,这将 return 一个具有 Student.transcript 属性 的有效成绩单参考的学生,但该成绩单参考的所有属性都为空或默认,例如creditsEarned = 0 而不是 42,就像它应该基于我的种子数据一样。
看来,为了检索缺失值,您必须在查询中明确 "fetch" 它们。因此,对于按 ID 进行的学生查询,它变成了:
FIND.fetch("transcript").where().eq(ID, id).findUnique();
这 return 是完全填充的学生,包括所有成绩单值。我读到设置 @OneToOne(fetch = FetchType.EAGER) 应该自动执行此操作,但我在一些来源中发现 EBeans 不支持 @OneToOne.
的获取参数
请原谅我,如果这是微不足道的 - 我白天是一名 C#/.NET 后端工程师,并且是 Play Framework 和 EBean 以及与此环境相关的所有其他事物的新手。
我正在使用内存中的 H2 数据库和 javax.persistence 库来管理我的业务 layer/persistence 层。我定义了三个实体(课程、学生、成绩单),并试图通过使用带有初始数据的 yaml 文件来播种。
到目前为止,我已经设法创建、保留和检索课程和学生之间的多对多关系。
但是,我在创建学生和成绩单之间的一对一关系时遇到了问题。在用初始数据播种数据库后,我尝试通过查找检索 Student 或 Transcript 对象,例如
FIND.where().eq(ID, id).findUnique()
对另一个对象的引用为空(Transcript 对象对其 Student 属性 有一个空引用,Student 对象对其 Transcript 属性 有一个空引用)。
当我保存到数据库时(请参阅下面的 Global.java),学生似乎确实引用了成绩单,就在使用调试器逐步执行并检查属性时。
这是学生实体(注意@OneToOne 成绩单 属性):
@Entity
@Table(name="students")
public class Student extends Model {
private static final String ID = "id";
@Id
@Constraints.Required
@Formats.NonEmpty
public String id;
@Constraints.Required
public String username;
@Constraints.Required
public String password;
@Constraints.Required
public String fullname;
@Constraints.Required
@ManyToMany
public List<Course> coursesPreferred = new ArrayList<Course>();
@Constraints.Required
public int numCoursesPreferred;
@Constraints.Required
@OneToOne
public Transcript transcript;
// -- Queries
private static final Model.Finder<String, Student> FIND =
new Model.Finder<>(String.class, Student.class);
/**
* Returns all students.
*/
public static List<Student> findAll() {
return FIND.all();
}
/**
* Returns the student with the given ID.
*
* @param id student ID
*/
public static Student findById(String id) {
return FIND.where().eq(ID, id).findUnique();
}
// --
public String toString() {
return "Student{" + fullname + "}";
}
}
这是成绩单实体(注意@OneToOne Student 属性,按成绩单映射):
@Entity
@Table(name="transcripts")
public class Transcript extends Model {
private static final String ID = "id";
@Id
@Constraints.Required
@Formats.NonEmpty
public String id;
@Constraints.Required
@OneToOne(mappedBy="transcript")
public Student student;
@Constraints.Required
public int creditsEarned;
// -- Queries
private static final Model.Finder<String, Transcript> FIND =
new Model.Finder<>(String.class, Transcript.class);
/**
* Returns all transcripts.
*/
public static List<Transcript> findAll() {
return FIND.all();
}
/**
* Returns the transcript with the given ID.
*
* @param id transcript ID
*/
public static Transcript findById(String id) {
return FIND.where().eq(ID, id).findUnique();
}
// --
public String toString() {
return "Transcript{" + id + "}";
}
}
这是包含初始数据的 yaml 文件:
# Courses
courses:
- &course6476 !!models.Course
id: 6476
tag: "CS 6476"
name: "Computer Vision"
abbrev: "CV"
core: Yes
- &course6035 !!models.Course
id: 6035
tag: "CS 6035"
name: "Introduction to Information Security"
abbrev: "IIS"
core: No
- &course6210 !!models.Course
id: 6210
tag: "CS 6210"
name: "Advanced Operating Systems"
abbrev: "AOS"
core: Yes
# Transcripts
transcripts:
- &transcript0001 !!models.Transcript
id: 0001
creditsEarned: 42
# Students
students:
- &student0001 !!models.Student
id: 0001
username: "joeschmoe"
password: "password1234"
fullname: "Joe Schmoe"
numCoursesPreferred: 2
coursesPreferred:
- *course6210
- *course6035
transcript: *transcript0001
最后,这是读取和保存数据的 Global.java 文件:
public class Global extends GlobalSettings {
private static final String INITIAL_DATA_FILE = "initial-data.yml";
private static final String COURSES = "courses";
private static final String STUDENTS = "students";
private static final String TRANSCRIPTS = "transcripts";
@Override
public void onStart(Application app) {
InitialData.insert(app);
}
private static class InitialData {
private static void insert(Application app) {
if (Student.findAll().size() == 0) {
Map<String, List<?>> all =
(Map<String, List<?>>) Yaml.load(INITIAL_DATA_FILE);
Ebean.save(all.get(COURSES));
Ebean.save(all.get(TRANSCRIPTS));
Ebean.save(all.get(STUDENTS));
}
}
}
}
有什么我可能在这里遗漏的想法吗?我是否需要更改指定 OneToOne 关系的方式? debug/troubleshoot 的额外步骤?任何信息都非常感谢,因为我碰壁了。
在此先致谢!
我想通了。检索 OneToOne 属性 值失败的查询是:
FIND.where().eq(ID, id).findUnique();
因此,例如,这将 return 一个具有 Student.transcript 属性 的有效成绩单参考的学生,但该成绩单参考的所有属性都为空或默认,例如creditsEarned = 0 而不是 42,就像它应该基于我的种子数据一样。
看来,为了检索缺失值,您必须在查询中明确 "fetch" 它们。因此,对于按 ID 进行的学生查询,它变成了:
FIND.fetch("transcript").where().eq(ID, id).findUnique();
这 return 是完全填充的学生,包括所有成绩单值。我读到设置 @OneToOne(fetch = FetchType.EAGER) 应该自动执行此操作,但我在一些来源中发现 EBeans 不支持 @OneToOne.
的获取参数