2

私は次のビジネスモデルを持っています:

食事-名前と価格が含まれています

メニュー-名前と食事のセット、およびその金額と代替価格(通常はデフォルト価格よりも低い)が含まれています。

過去に、私はを使用して同様の問題を解決しHashMap<Entity, Value>ました。この場合、価格と金額の2つの値があります。JavaとJPAはマルチマップをサポートしていないため、同じキーを含む2つのHashMapを使用することにしました。

@Entity
public class Menu {
    @Column
    private String name;

    @ElementCollection
    @CollectionTable(name = "MENU_MEALS", joinColumns = @JoinColumn(name = "MENU_ID"))
    @MapKeyJoinColumn(name = "MEAL_ID", referencedColumnName = "ID")
    @Column(name="AMOUNT")
    private Map<Meal, BigDecimal> amounts = new LinkedHashMap<Meal, BigDecimal>();

    @ElementCollection
    @CollectionTable(name = "MENU_MEALS", joinColumns = @JoinColumn(name = "MENU_ID"))
    @MapKeyJoinColumn(name = "MEAL_ID", referencedColumnName = "ID")
    @Column(name="PRICE")
    private Map<Meal, BigDecimal> prices = new LinkedHashMap<Meal, BigDecimal>();
}

生成されたテーブルは正常に見えます:MENU_ID、MEAL_ID、AMOUNT、PRICE。

このように(エンティティ内のメソッドで)新しい食事をメニューに追加し、それを切り離した状態で追加します(食事をすぐにDBに保存したくない-GUIプレビューが最初に来ます):

menu.prices.add(meal, price);
menu.amounts.add(meal, amount);

切り離されたメニューに十分な食事を追加し、「OK」ボタンをクリックすると、私のメニューがマージされます。

操作はem.merge(menu)次のSQLにマップされます。

insert into MENU_MEAL (MENU_ID, MEAL_ID, PRICE) values (?, ?, ?)
insert into MENU_MEAL (MENU_ID, MEAL_ID, AMOUNT) values (?, ?, ?)

そしてここに問題があります。主キー制約違反の例外が発生します。2番目の呼び出しでは、(MENU_ID、MEAL_ID)エントリを更新する必要があります。代わりに、同じ外部キーを使用して新しいエントリを挿入しようとします。

「update-if-exists-insert-otherwise」を強制する方法について何か提案はありますか?

各HashMapを異なるテーブルにマッピングすることで、問題を一時的に修正しました。しかし、私はその解決策が好きではありません。

編集:このように動作するには、マージが必要です:

insert into MENU_MEAL (MENU_ID, MEAL_ID, PRICE) values(?, ?, ?) on duplicate key update PRICE=values(price)

それを達成する方法はありますか?

4

3 に答える 3

3

私は遅れていることを知っていますが、これらは私の解決策です:

1)@Embeddable値を持つ単一のマップを使用します。

@Entity
public class Menu
{
    @Column
    private String name;

    @ElementCollection
    @CollectionTable(name = "MENU_MEALS", joinColumns = @JoinColumn(name = "MENU_ID"))
    @MapKeyJoinColumn(name = "MEAL_ID", referencedColumnName = "ID")
    private Map<Meal, Detail> details = new LinkedHashMap<Meal, Detail>();
}

@Embeddable
public class Detail
{
    @Column(name = "AMOUNT")
    private BigDecimal amount;

    @Column(name = "PRICE")
    private BigDecimal price;
}

このマッピングは、すでに持っているものと同じテーブル構造を持っています。

2)@Entityリレーションを持つ単一のマップを使用します。

@Entity
public class Menu
{
    @Column
    private String name;

    @OneToMany(mappedBy = "menu", cascade = CascadeType.ALL, orphanRemoval = true)
    @MapKey(name = "meal")
    private Map<Meal, MealDetail> details = new LinkedHashMap<Meal, MealDetail>();
}

@Entity
public class MealDetail
{
    @ManyToOne
    @JoinColumn(name = "MENU_ID")
    private Menu menu;

    @ManyToOne
    @JoinColumn(name = "MEAL_ID")
    private Meal meal;

    @Column(name = "AMOUNT")
    private BigDecimal amount;

    @Column(name = "PRICE")
    private BigDecimal price;
}

このマッピングはID列を追加しますが、@ PrimaryKeyJoinColumnsを使用すると、以前と同じマッピングを取得できると思います。

于 2014-10-13T07:49:37.327 に答える
1

正しく思い出せば、variable = em.findその変数を使用して更新すると、永続化せずにentitymanagerの値を更新できます。

値を変更または追加する場所でコードを囲むか、次の方法で値を追加できます。

em.getTransaction().begin();
//change
menu.prices.add(meal, price);
menu.amounts.add(meal, amount);
em.getTransaction().commit();

試すこともできますem.flush()

作成したコードを表示すると、より確実な答えが得られますmenu。使用しましたem.find()か?

于 2013-02-14T09:11:01.647 に答える
0

HibernateをJPAORMプロバイダーとして使用すると、問題の解決策が見つかりました。@SQLInsertHashMapsにHibernateアノテーションを追加するデフォルトのSQLINSERTコマンドをオーバーライドするだけです。

H2データベースのソリューション:

@SQLInsert(sql="MERGE INTO MENU_MEAL(MENU_ID, MEAL_ID, PRICE) VALUES (?, ?, ?)")
private Map<Meal, BigDecimal> prices = new LinkedHashMap<Meal, BigDecimal>();

@SQLInsert(sql="MERGE INTO MENU_MEAL(MENU_ID, MEAL_ID, AMOUNT) VALUES (?, ?, ?)")
private Map<Meal, BigDecimal> amounts = new LinkedHashMap<Meal, BigDecimal>();

他のデータベースのソリューション(ON DUPLICATE KEYをサポート):

@SQLInsert(sql="INSERT INTO MENU_MEAL(MENU_ID, MEAL_ID, PRICE) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE set PRICE = VALUES(PRICE)")
private Map<Meal, BigDecimal> prices = new LinkedHashMap<Meal, BigDecimal>();

@SQLInsert(sql="INSERT INTO MENU_MEAL(MENU_ID, MEAL_ID, AMOUNT) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE set AMOUNT = VALUES(AMOUNT)")
private Map<Meal, BigDecimal> amounts = new LinkedHashMap<Meal, BigDecimal>();

ソリューション(データベースとORMプロバイダーの両方に依存)にはまだ完全には満足していませんが、HashMapごとに1つのテーブルを用意するよりはましです。

于 2013-02-14T10:39:47.733 に答える