如何使用JPA在实体内部持久保存Map(java.util.Map)对象并确保持久性级联?

我最近开始玩Play! Java框架,版本1.2.3(最新版本).在测试框架时,我在尝试在名为FooSystem的Hibernate实体中持久保存Map对象时遇到了一个奇怪的问题. Map对象将一个long映射到一个名为Foo的Hibernate实体,其声明为Map< Long,Foo> fooMap;

我的问题如下:正确的表创建,因为我已经注释了它们.但是,当FooSystem对象fs被持久化时,fs.fooMap中的数据不是!

这是我为实体使用的代码.首先是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<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;
    }
}

这是我用来测试我的设置的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类中;问题在于控制器.具体来说,我是以错误的顺序保存实体.一旦我意识到在地图上使用@ElementCollection注释< Long,Foo>产生了与我手动指定的连接表相同的效果,我试过我想实验,我重新思考我是如何保存我的实体.

在上面发布的代码中,您可以在FooSystem构造函数中看到,在保留Foo对象之前,将两个Foo对象f1和f2放入fooMap.我意识到如果f1在放入地图时不在数据库中,JPA如何在连接表中使用其ID作为外键?

如果您可以通过这种推理看到我的目标,您可以看到明显的答案是JPA无法完成使用外键引用不存在的键这一惊人的壮举.奇怪的是Play!控制台没有记录我发布的原始代码的任何错误,即使它根本不正确.框架吞噬了在进程中抛出的每个Exception,或者我编写了应该产生Exception的代码.

因此,为了解决这个问题,我在对它们执行任何操作之前保留了Foo实体.只有这样我才将它们放入fooMap.最后,一旦填充了fooMap,我就坚持了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<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);
    }
}

转载注明原文:如何使用JPA在实体内部持久保存Map(java.util.Map)对象并确保持久性级联? - 代码日志