Play Framework 2.3.8 中与 Ebeans 的 OneToOne 关系

Posted

技术标签:

【中文标题】Play Framework 2.3.8 中与 Ebeans 的 OneToOne 关系【英文标题】:OneToOne relationship with Ebeans in Play Framework 2.3.8 【发布时间】:2015-11-18 07:21:46 【问题描述】:

如果这是微不足道的,请原谅我 - 我白天是一名 C#/.NET 后端工程师,并且对 Play Framework 和 EBean 以及与此环境相关的所有其他东西都是新手。

我正在使用内存中的 H2 数据库和 javax.persistence 库来管理我的业务层/持久层。我已经定义了三个实体(课程、学生、成绩单),并试图通过使用带有初始数据的 yaml 文件来播种。

到目前为止,我已经成功地在 Course 和 Student 之间创建、持久化和检索多对多关系。

但是,我无法在 Student 和 Transcript 之间创建 OneToOne 关系。在使用初始数据播种数据库后,我尝试通过查找来检索学生或成绩单对象,例如

FIND.where().eq(ID, id).findUnique()

对另一个对象的引用为 null(Transcript 对象的 Student 属性有一个 null 引用,Student 对象的 Transcript 属性有一个 null 引用)。

当我保存到数据库时,学生似乎确实有对成绩单的引用(请参阅下面的 Global.java),只是在调试器单步执行并检查属性时。

这里是 Student 实体(注意 @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 + "";
    


这里是 Transcript 实体(注意 @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 关系的方式?调试/故障排除的其他步骤?任何信息都值得赞赏,因为我已经碰壁了。

提前非常感谢!

【问题讨论】:

你错过了什么?您缺少了解您正在使用的内容。您使用 EBean。 EBean!= JPA。 EBean != 休眠。他们完全不同。因此建议您编辑您的问题并删除 JPA、Hibernate 引用并将其仅集中在 EBean 上 谢谢 Neil,我已经删除了对 JPA 和 Hibernate 的引用,因此讨论可以集中在 EBean 和 Play 上 【参考方案1】:

我发现了问题所在。未能检索 OneToOne 属性值的查询是:

FIND.where().eq(ID, id).findUnique();

因此,例如,这将返回一个 Student,其中包含 Student.transcript 属性的有效 Transcript 引用,但该 Transcript 引用上的所有属性都是 null 或默认值,例如creditsEarned = 0 而不是 42 应该是基于我的种子数据。

看来,为了检索缺失值,您必须在查询中明确“获取”它们。因此,对于按 ID 进行的 Student 查询,它变为:

FIND.fetch("transcript").where().eq(ID, id).findUnique();

这将返回完全填充的学生,包括所有成绩单值。我读到设置 @OneToOne(fetch = FetchType.EAGER) 应该自动执行此操作,但我在一些来源中发现 EBeans 不支持 @OneToOne 的 fetch 参数。

【讨论】:

以上是关于Play Framework 2.3.8 中与 Ebeans 的 OneToOne 关系的主要内容,如果未能解决你的问题,请参考以下文章

Play Framework 2.5,到 Web 套接字的路由无法编译

无法在 heroku 上使用 ClearDB 运行 Play Framework 1.2.4 应用程序

如何通过json暴露play framework 1.2.7中的内部错误消息id?

Update() 方法在带有 h2 数据库的 Play Framework 中不起作用

Google Play Expo w/ React - Expo 的 app.json 文件中与 Android 对应的 plist 是啥?

在 Heroku 托管的 Play Framework 2 应用程序上通过 Mandrill 对电子邮件进行错误的内容传输编码。在本地工作