Как использовать JPA для сохранения объекта Map (java.util.Map) внутри объекта и обеспечения постоянства каскадов? - PullRequest
8 голосов
/ 23 сентября 2011

Я недавно начал играть с Play!Framework для Java, версия 1.2.3 (последняя).При тестировании фреймворка я столкнулся со странной проблемой при попытке сохранить объект Map внутри объекта Hibernate с именем FooSystem.Объект Map отображает long на объект Hibernate, который я назвал Foo, с объявлением 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();
    }
}

Игра!Framework автоматически создал транзакцию для HTTP-запроса.Хотя данные вставляются в таблицы foo и foosystem, в таблицу foomap ничего не вставляется, что является желаемым результатом.Что я могу сделать по этому поводу?Чего мне не хватает?

1 Ответ

4 голосов
/ 23 сентября 2011

Мне удалось решить эту проблему по совету Java Ka Baby. Проблема была на самом деле не в моих Model классах; проблема лежала в пределах Controller. В частности, я сохранял объекты в неправильном порядке. Как только я понял, что использование аннотации @ElementCollection на Map<Long, Foo> произвело те же эффекты, что и таблица соединения, которую я указывал вручную, я попытался провести эксперимент, в котором я переосмыслил, как сохранять свои сущности.

В приведенном выше коде вы можете видеть в конструкторе FooSystem, что два Foo объекта, f1 и f2, помещаются в fooMap до того, как объекты Foo будут сохранены. Я понял, что если f1 нет в базе данных, когда он помещен в карту, как JPA может использовать свой идентификатор в качестве внешнего ключа в таблице соединения?

Если вы видите, куда я иду с этой линией рассуждений, вы можете увидеть, что очевидный ответ заключается в том, что JPA не способен выполнить это удивительное умение использования внешнего ключа для ссылки на несуществующий ключ. Странно то, что игра! консоль не заметила никаких ошибок в исходном коде, который я разместил, хотя он и не был корректным. Либо фреймворк проглотил каждый 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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...