执行摘要:
当我从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!
我在这里做错了什么?
谢谢!
额外信息:
在应用SpartanElite建议的更改并运行SQL登录后,运行测试的输出位于:http://pastebin.com/BbMrCtZL
这些是产生结果的ScenarioTest.java Scenario.java和Game.java的确切来源。
- 整件事的tarball在这里:http://bit.ly/onetomanytest。你应该能够提取它,cd到它,运行play,并做仅测试模型.ScenarioTest来查看结果。除了这些东西和用户模型之外,没有其他内容。
答案 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配置从您上传的应用中。我唯一做的就是清理数据库无关的问题,这些问题阻止了它的编译;测试和模型没有变化。