如何使用JPA在实体内部持久保存Map(java.util.Map)对象并确保持久性级联?
我最近开始玩Play! Java框架,版本1.2.3(最新版本)。 在测试框架时,我在尝试在名为FooSystem
的Hibernate实体中持久保存Map
对象时遇到了一个奇怪的问题。 Map对象将一个long映射到一个名为Foo
的Hibernate实体,其声明为Map fooMap;
我的问题如下:正确的表创建,因为我已经注释了它们。 但是,当FooSystem
对象fs
被持久化时, FooSystem
的数据不是!
这是我为实体使用的代码。 首先是Foo
:
package models.test; import javax.persistence.Entity; import javax.persistence.ManyToOne; import play.db.jpa.Model; @Entity public class Foo extends Model { @ManyToOne private FooSystem foosystem; public Foo(FooSystem foosystem) { this.foosystem = foosystem; } }
这是FooSystem
:
package models.test; import java.util.HashMap; import java.util.Map; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.JoinTable; import javax.persistence.ManyToMany; import play.db.jpa.Model; @Entity public class FooSystem extends Model { @ManyToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST}) @JoinTable( name = "fooMap", joinColumns = @JoinColumn(name = "foosystem"), inverseJoinColumns = @JoinColumn(name = "foo") ) private Map fooMap = new HashMap(); public FooSystem() { Foo f1 = new Foo(this); Foo f2 = new Foo(this); fooMap.put(f1.getId(), f1); fooMap.put(f2.getId(), f2); } public Map getFooMap() { return fooMap; } }
这是我用来测试我的设置的Controller
类:
package controllers; import javax.persistence.EntityManager; import models.test.FooSystem; import play.db.jpa.JPA; import play.mvc.Controller; public class TestController extends Controller { public static void index() { EntityManager em = JPA.em(); FooSystem fs = new FooSystem(); em.persist(fs); render(); } }
表演! 框架自动为HTTP请求创建了一个事务。 虽然数据被插入到foo
和foosystem
表中,但是没有任何内容插入到foomap
表中,这是所需的结果。 我该怎么办? 我错过了什么?
我设法使用Java Ka Baby的建议解决了这个问题。 问题实际上不在我的Model
类中; 问题在于Controller
。 具体来说,我是以错误的顺序保存实体。 一旦我意识到在Map
上使用@ElementCollection
注释Map
产生了与我手动指定的连接表相同的效果,我试过我想实验,我重新思考我是如何保存我的实体的。
在上面发布的代码中,您可以在FooSystem
构造函数中看到,在FooSystem
Foo
对象之前,将两个Foo
对象f1
和f2
放入fooMap
。 我意识到如果f1
在放入地图时不在数据库中,JPA如何在连接表中使用其ID作为外键?
如果您可以通过这种推理看到我的目标,您可以看到明显的答案是JPA 无法完成使用外键引用不存在的键这一惊人的壮举。 奇怪的是Play! 控制台没有记录我发布的原始代码的任何错误,即使它根本不正确。 框架吞噬了在进程中抛出的每个Exception
,或者我编写了应该产生Exception
代码。
因此,为了解决这个问题,我在对它们执行任何操作之前保留了Foo
实体。 只有这样我才将它们放入fooMap
。 最后,一旦填充了FooSystem
,我就坚持了FooSystem
实体。
这是更正的TestController
类:
package controllers; import javax.persistence.EntityManager; import models.test.Foo; import models.test.FooSystem; import play.db.jpa.JPA; import play.mvc.Controller; public class TestController extends Controller { public static void index() { EntityManager em = JPA.em(); FooSystem fs = new FooSystem(); Foo f1 = new Foo(fs); Foo f2 = new Foo(fs); f1.save(); f2.save(); fs.put(f1.getId(), f1); fs.put(f2.getId(), f2); fs.save(); render(); } }
而且,由于我更改了FooSystem
,以下是该类的最终代码:
package models.test; import java.util.HashMap; import java.util.Map; import javax.persistence.ElementCollection; import javax.persistence.Entity; import play.db.jpa.Model; @Entity public class FooSystem extends Model { @ElementCollection private Map fooMap = new HashMap(); public FooSystem() { } public Map getFooMap() { return fooMap; } public void put(Long l, Foo f) { fooMap.put(l, f); } }