【问题标题】:Play framework "find" (Finder) results missing OneToMany relationship data播放框架“查找”(Finder)结果缺少 OneToMany 关系数据
【发布时间】:2014-01-01 05:01:19
【问题描述】:

执行摘要:

当我从播放框架的“Finder”中取回一个对象时,该对象的 ManyToOne 字段已正确填充,但它指向自身的对象具有错误为 null 的 OneToMany 字段。

在我的应用程序(“场景存储库”)中,一个场景有一个游戏,而游戏有很多场景。

当我“找到”一个场景时

  Scenario retrieved_scenario = Scenario.find.where().eq("name", "Test Scenario").findUnique();

游戏对象 (retrived_scenario.game) 缺少它的场景:

    assertNotNull(retrieved_scenario);   // passes

    assertEquals("CM Test", retrieved_scenario.game.name); //passes

    assertNotNull("retrived scenario's game's scenarios", retrieved_scenario.game.scenarios);  // fails

但如果我使用游戏的 find 来检索游戏本身,并测试它的场景列表,那就没问题了。

我做错了什么?

详情:

我有一个名为 Game 的 ebean 持久对象,它可以有许多场景:

import play.db.ebean.*;
import com.avaje.ebean.*;

@Entity
public class Game extends Model {

    @Id
    public String name;

    @OneToMany
    public List<Scenario> scenarios;

Scenario 对象(也是 ebean 持久化的)在创建时将其自身添加到游戏的场景列表中:

@Entity
public class Scenario extends Model {

    @Id
    public String name;

    @ManyToOne
    public Game game;

    @OneToOne
    public User author;

    public Scenario(String name, String game_name, String author_name) {
        Logger.info(String.format("Constructing scenario '%s' of type '%s'", name, game_name));
        this.name = name;
        this.game = Game.find.where().eq("name", game_name).findUnique();
        Logger.info(String.format("...found it's game: '%s', which has %d scenarios", this.game.name, this.game.scenarios.size()));

        this.game.scenarios.add(this);
        Logger.info(String.format("now ... it's game has %d scenarios", this.game.scenarios.size()));
        this.author = User.find.where().eq("username", author_name).findUnique();
    }

    public static Scenario create(String name, String game_name, String author_name) {
        Logger.info(String.format("Creating scenario '%s' of '%s' by '%s'", name, game_name, author_name));
        Scenario it = new Scenario(name, game_name, author_name);
        it.save();
        it.game.save();
        it.game.saveManyToManyAssociations("scenarios");  // doesn't help (or apparently hurt)
        return it;
    }

    public static Scenario.Finder<String,Scenario> find = 
    new Scenario.Finder<String,Scenario>(String.class, Scenario.class); 

}

但是,当我使用此查找器“找到”一个场景时,相关游戏的场景并未初始化:

@Test
public void createAndRetrieveScenario() {
    Logger.info("testing createAndRetrieveScenario...");

    Logger.info("setting up...");
    Game created_game = Game.create("CM Test");

    Logger.info("creating scenario...");
    Scenario created_scenario = Scenario.create("Test Scenario", "CM Test", "GreenAsJade");
    assertNotNull(created_scenario);
    assertEquals("Test Scenario", created_scenario.name);
    assertEquals("CM Test", created_scenario.game.name);
    assertNotNull(created_scenario.game.scenarios);

    Logger.info("checking that the scenario was added properly to the game...");

    Game retrieved_game = Game.find.where().eq("name", "CM Test").findUnique();
    assertNotNull("retrieved game", retrieved_game);
    assertNotNull("retrieved game scenarios", retrieved_game.scenarios);

    Logger.info("retrieving...");

    Scenario retrieved_scenario = Scenario.find.where().eq("name", "Test Scenario").findUnique();
    assertNotNull(retrieved_scenario);
    assertEquals("Test Scenario", retrieved_scenario.name);

    assertEquals("CM Test", retrieved_scenario.game.name);

    Logger.info("checking scenario's game's scenarios...");
    assertNotNull("retrived scenario's game's scenarios", retrieved_scenario.game.scenarios);
    assertFalse(retrieved_scenario.game.scenarios.isEmpty());
}

IE 最后两个断言失败。但到目前为止,一切似乎都按我预期的方式工作。您可以在其中看到的所有日志消息都报告“您所期望的”...除了有趣的是没有与 find() 操作关联的 Scenario 构造函数调用,它确实返回了一个 Scenario!

我在这里做错了什么?

谢谢!

额外信息:

【问题讨论】:

  • 注意:mappedBy 没有帮助:@OneToMany(mappedBy="game", cascade = CascadeType.ALL) public List 场景;
  • 而且 - 奇怪的是,即使 Game 中的场景列表有启动器,assertNotNull 也会失败: public List scenario = new ArrayList();

标签: java jpa playframework-2.0 one-to-many ebean


【解决方案1】:

发生的情况是您没有让 JPA 知道 one-to-many 关系 Game -&lt; Scenario 是一个单一的关系。

目前您没有使用mappedBy 属性或@JoinColumn 注释。所以 JPA 认为这两者都是两个独立的关系。因此,基本上当您使用...查找场景时。

Scenario retrieved_scenario = Scenario.find.where().eq("name", "Test Scenario").findUnique();

会找到场景("Test Scenario"),会找到相关游戏("CM Test")。但是当它尝试填充游戏("CM Test") 的字段时,它必须在场景表中查找子场景。但由于您尚未定义关系的反面,因此它将为空。

因此,您可以通过使用...指定反面来将关系设置为单个双向关系。

@Entity
public class Game extends Model {

    @Id
    public String name;

    @OneToMany(mappedBy="game", cascade=CascadeType.ALL)
    public List<Scenario> scenarios;
}

@Entity
public class Scenario extends Model {

    @Id
    public String name;

    @ManyToOne
    @JoinColumn(name="gameId", nullable=false)
    public Game game;

现在当游戏("CM Test") 尝试进行查找时,它会知道相关的场景。

有时将调试实用程序连接到应用程序也会有所帮助。然后您可以浏览 JVP Heap Memory 上的对象,并查看它们之间的关系。

【讨论】:

  • 非常感谢您的回答:这听起来正是我正在寻找的东西。不幸的是,它还没有修复它(行为是相同的),所以肯定有一些细节丢失了,我仍然做错了。我在打开 SQL 调试的情况下添加了一个指向测试输出的链接。奇怪的是,当我将场景添加到游戏中时,我没有看到任何似乎维护关系的 SQL 语句,尽管事实上对此的测试是有效的。我还有很多东西要学 - 再次感谢您的帮助。
  • 我很好奇为什么这个答案没有解决问题却得到三票赞成?
【解决方案2】:

我在 play frameworks google 小组中找到了答案。正确的咒语必须明确要求像这样加载游戏的一对多:

Scenario retrieved_scenario = Scenario.find.fetch("game").where().eq("name", "Test Scenario").findUnique();

我没有正确理解这句话的真正含义 - 我无法找到指向哪里可以阅读有关如何以这种方式使用此 Finder 帮助程序的指针。但它有效。

【讨论】:

    【解决方案3】:
    [info] application - checking scenario's game's scenarios...
    [debug] c.j.b.PreparedStatementHandle - select t0.name c0, t1.name c1, t1.gameId c2, t1.author_username c3 from game t0 left outer join scenario t1 on t1.gameId = t0.name  where t0.name = 'CM Test'   order by t0.name
    [info] c.j.b.BoneCP - Shutting down connection pool...
    [debug] c.j.b.PoolWatchThread - Terminating pool watch thread
    [info] c.j.b.BoneCP - Connection pool has been shutdown.
    [debug] c.j.b.BoneCPDataSource - Connection pool has been shut down
    [info] models.ScenarioTest
    [info] + createAndRetrieveScenario
    [info] 
    [info] 
    [info] Total for test models.ScenarioTest
    [info] Finished in 0.0010 seconds
    [info] 1 tests, 0 failures, 0 errors
    

    从您上传的应用使用 H2 数据库配置。我做的唯一一件事就是清理阻止它编译的与数据库无关的问题;测试和模型没有改变。

    【讨论】:

    • 你能和我分享你到底做了什么(树的差异会很棒),这样我就可以重现了吗?我希望有一个与我发布的不同的解决方案,这很麻烦......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-10-24
    • 1970-01-01
    • 2013-01-26
    • 1970-01-01
    • 2018-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多