如何使用 JPA 在实体内持久化 Map (java.util.Map) 对象并确保持久性级联?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/7521218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How do I use JPA to persist a Map (java.util.Map) object inside an entity and ensure the persistence cascades?
提问by fruchtose
I have recently started playing around with the Play! Framework for Java, version 1.2.3 (the latest). While testing out the framework, I came across a strange problem when trying to persist a Map
object inside a Hibernate entity called FooSystem
. The Map object maps a long to a Hibernate entity I have called Foo
, with the declaration Map<Long, Foo> fooMap;
我最近开始玩 Play!Java 框架,版本 1.2.3(最新)。在测试框架时,我在尝试将一个Map
对象持久化到一个名为FooSystem
. Map 对象将 long 映射到我调用的 Hibernate 实体Foo
,并带有声明Map<Long, Foo> fooMap;
My problem is as follows: The correct tables are created as I have annotated them. However, when the FooSystem
object fs
is persisted, the data in fs.fooMap
is not!
我的问题如下: 正确的表已创建,因为我已经对它们进行了注释。但是,当FooSystem
对象fs
被持久化时,里面的数据fs.fooMap
不是!
Here is the code I am using for the entities. First is Foo
:
这是我用于实体的代码。首先是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;
}
}
And here is 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<Long, Foo> fooMap = new HashMap<Long, Foo>();
public FooSystem()
{
Foo f1 = new Foo(this);
Foo f2 = new Foo(this);
fooMap.put(f1.getId(), f1);
fooMap.put(f2.getId(), f2);
}
public Map<Long, Foo> getFooMap()
{
return fooMap;
}
}
Here is the Controller
class I am using to test my set-up:
这是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();
}
}
The Play! framework automatically created a transaction for the HTTP request. Although data is inserted into the foo
and foosystem
tables, nothing is ever inserted into the foomap
table, which is the desired result. What can I do about this? What am I missing?
表演!框架自动为 HTTP 请求创建了一个事务。尽管数据被插入到foo
和foosystem
表中,但从未向foomap
表中插入任何内容,这是所需的结果。我该怎么办?我错过了什么?
采纳答案by fruchtose
I managed to solve this problem using the advice of Java Ka Baby. The issue was actually not in my Model
classes; the problem lay within the Controller
. Specifically, I was saving the entities in the wrong order. Once I realized that using the @ElementCollection
annotation on the Map<Long, Foo>
produced the same effects as the join table I was manually specifying, I tried I thought experiment where I re-thought how I was saving my entities.
我设法使用 Java Ka Baby 的建议解决了这个问题。这个问题实际上不在我的Model
课堂上;问题出在Controller
. 具体来说,我以错误的顺序保存实体。一旦我意识到在 上使用@ElementCollection
注释Map<Long, Foo>
产生了与我手动指定的连接表相同的效果,我尝试了我的思想实验,我重新思考了如何保存我的实体。
In the code I posted above, you can see in the FooSystem
constructor that two Foo
objects, f1
and f2
, are put into fooMap
before the Foo
objects are persisted. I realized that if f1
is not in the database when it is put into the map, how is JPA able to use its ID as a foreign key in the join table?
在我上面发布的代码中,您可以在FooSystem
构造函数中看到,在对象持久化之前放入了两个Foo
对象f1
和。我意识到如果它在放入地图时不在数据库中,那么JPA如何能够将其ID用作连接表中的外键?f2
fooMap
Foo
f1
If you can see where I'm going with this line of reasoning, you can see that the obvious answer is that JPA is notable to accomplish this amazing feat of using a foreign key to reference a nonexistent key. The bizarre thing is that the Play! console did not note any errors at all for the original code I posted, even though it was not correct at all. Either the framework swallowed every Exception
thrown during the process, or I've written code that shouldproduce an Exception
.
如果您能看到我的推理思路,您会发现显而易见的答案是 JPA无法完成使用外键引用不存在的键的惊人壮举。奇怪的是,播放!控制台根本没有注意到我发布的原始代码有任何错误,即使它根本不正确。要么框架吞下了Exception
在这个过程中抛出的每一个,要么我写的代码应该产生一个Exception
.
So to fix the problem, I persisted the Foo
entities before any operations were performed on them. Only then did I put them into fooMap
. Finally, once fooMap
was populated, I persisted the FooSystem
entity.
所以为了解决这个问题,我Foo
在对它们执行任何操作之前保留了实体。直到那时我才将它们放入fooMap
. 最后,一旦fooMap
被填充,我就坚持FooSystem
实体。
Here is the corrected TestController
class:
这是更正后的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();
}
}
And, since I changed FooSystem
, here is the final code for that class:
而且,由于我更改了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<Long, Foo> fooMap = new HashMap<Long, Foo>();
public FooSystem()
{
}
public Map<Long, Foo> getFooMap()
{
return fooMap;
}
public void put(Long l, Foo f)
{
fooMap.put(l, f);
}
}