如何使用 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 20:19:40  来源:igfitidea点击:

How do I use JPA to persist a Map (java.util.Map) object inside an entity and ensure the persistence cascades?

javahibernatejpamapplayframework

提问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 Mapobject 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 FooSystemobject fsis persisted, the data in fs.fooMapis 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 Controllerclass 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 fooand foosystemtables, nothing is ever inserted into the foomaptable, which is the desired result. What can I do about this? What am I missing?

表演!框架自动为 HTTP 请求创建了一个事务。尽管数据被插入到foofoosystem表中,但从未向foomap表中插入任何内容,这是所需的结果。我该怎么办?我错过了什么?

采纳答案by fruchtose

I managed to solve this problem using the advice of Java Ka Baby. The issue was actually not in my Modelclasses; the problem lay within the Controller. Specifically, I was saving the entities in the wrong order. Once I realized that using the @ElementCollectionannotation 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 FooSystemconstructor that two Fooobjects, f1and f2, are put into fooMapbefore the Fooobjects are persisted. I realized that if f1is 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用作连接表中的外键?f2fooMapFoof1

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 Exceptionthrown during the process, or I've written code that shouldproduce an Exception.

如果您能看到我的推理思路,您会发现显而易见的答案是 JPA无法完成使用外键引用不存在的键的惊人壮举。奇怪的是,播放!控制台根本没有注意到我发布的原始代码有任何错误,即使它根本不正确。要么框架吞下了Exception在这个过程中抛出的每一个,要么我写的代码应该产生一个Exception.

So to fix the problem, I persisted the Fooentities before any operations were performed on them. Only then did I put them into fooMap. Finally, once fooMapwas populated, I persisted the FooSystementity.

所以为了解决这个问题,我Foo在对它们执行任何操作之前保留了实体。直到那时我才将它们放入fooMap. 最后,一旦fooMap被填充,我就坚持FooSystem实体。

Here is the corrected TestControllerclass:

这是更正后的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);
    }
}