播放框架“查找”(Finder)结果缺少OneToMany关系数据

时间:2014-01-01 05:01:19

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

执行摘要:

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

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

当我“找到”一个场景

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

Game对象(retrieve_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 for the 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!

我在这里做错了什么?

谢谢!

额外信息:

3 个答案:

答案 0 :(得分:3)

发生的事情是,您不会让JPA知道one-to-many关系Game -< 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堆内存中的对象,并查看其中的关系。

答案 1 :(得分:2)

我在游戏框架中找到了答案谷歌组。正确的咒语必须特别要求游戏的一对一加载如下:

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

我没有正确理解这是什么意思 - 我无法找到关于如何以这种方式使用此Finder帮助程序的指示。但它确实有效。

答案 2 :(得分:0)

[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 db配置从您上传的应用中。我唯一做的就是清理数据库无关的问题,这些问题阻止了它的编译;测试和模型没有变化。