web-dev-qa-db-ja.com

多対多の一方向マッピングでの列挙型の永続化

永続性マッピングを指定するために、アノテーション付きのHibernate3.5.2-FINALを使用しています。アプリケーションと一連のプラットフォーム間の関係のモデル化に苦労しています。各アプリケーションは、一連のプラットフォームで使用できます。

私が行ったすべての読み取りと検索から、プラットフォームの列挙型クラスをエンティティとして永続化し、多対多の関係を表す結合テーブルを用意する必要があると思います。関係をオブジェクトレベルで単方向にしたい、つまり、特定のアプリケーションのプラットフォームのリストを取得できるようにしたいのですが、特定のプラットフォームのアプリケーションのリストを見つける必要はありません。

これが私の簡略化されたモデルクラスです:

@Entity
@Table(name = "TBL_PLATFORM")
public enum Platform {
    Windows,
    Mac,
    Linux,
    Other;

    @Id
    @GeneratedValue
    @Column(name = "ID")
    private Long id = null;

    @Column(name = "NAME")
    private String name;

    private DevicePlatform() {
        this.name = toString();
    }

    // Setters and getters for id and name...
}

@Entity
@Table(name = "TBL_APP")
public class Application extends AbstractEntity implements Serializable {
    private static final long serialVersionUID = 1L;

    @Column(name = "NAME")
    protected String _name;

    @ManyToMany(cascade = javax.persistence.CascadeType.ALL)
    @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
    @JoinTable(name = "TBL_APP_PLATFORM", 
              joinColumns = @JoinColumn(name = "APP_ID"),
              inverseJoinColumns = @JoinColumn(name = "PLATFORM_ID"))
    @ElementCollection(targetClass=Platform.class)
    protected Set<Platform> _platforms;

    // Setters and getters...
}

Hibernate hbm2ddlツールを実行すると、次のように表示されます(MySQLを使用しています)。

create table TBL_APP_PLATFORM (
    APP_ID bigint not null,
    PLATFORM_ID bigint not null,
    primary key (APP_ID, PLATFORM_ID)
);

適切な外部キーも、このテーブルからアプリケーションテーブルとプラットフォームテーブルに作成されます。ここまでは順調ですね。

私が遭遇している問題の1つは、アプリケーションオブジェクトを永続化しようとしたときです。

Application newApp = new Application();
newApp.setName("The Test Application");
Set<DevicePlatform> platforms = EnumSet.of(Platform.Windows, Platform.Linux);
newApp.setPlatforms(platforms);
applicationDao.addApplication(newApp);

私がしたいのは、Platformテーブルの適切な行を作成することです。つまり、WindowsとLinuxの行がまだ存在しない場合は、それらを作成します。次に、新しいアプリケーションの行を作成してから、新しいアプリケーションと結合テーブル内の2つのプラットフォーム間のマッピングを作成する必要があります。

私が直面している問題の1つは、次のランタイム例外が発生することです。

2010-06-30 13:18:09,382 6613126-0 ERROR FlushingEventListener Could not synchronize database state with session org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.example.model.Platform

どういうわけか、アプリケーションを永続化しようとすると、プラットフォームセットが永続化されません。カスケードアノテーションがそれを処理することになっていますが、何が問題なのかわかりません。

だから私の質問は:

  1. 私がやりたいことをモデル化するためのより良い方法はありますか?列挙型を使用するのは適切ですか?
  2. モデルに問題がない場合、すべてのオブジェクトを適切に永続化するにはどうすればよいですか?

私はこれに何時間も苦労していて、上記のすべてのコードを再作成しようとしましたが、完全ではないか、正確ではない可能性があります。誰かが明白なことを指摘してくれることを願っています!

20
gnuf

Platformentityであるかどうかを判断する必要があります。

エンティティの場合、可能なプラットフォームのリストはアプリケーションではなくデータベースに保存されるため、enumにすることはできません。 @Entityアノテーションが付いた通常のクラスである必要があり、通常の多対多の関係になります。

エンティティでない場合は、TBL_PLATFORMテーブルは必要なく、多対多の関係もありません。この場合、Platformsのセットを、ビットフラグ付きの整数フィールドとして、または単純な1対多の関係として表すことができます。 JPA 2.0は、後者のケースを@ElementCollectionで単純化します。

@ElementCollection(targetClass = Platform.class) 
@CollectionTable(name = "TBL_APP_PLATFORM",
    joinColumns = @JoinColumn(name = "APP_ID"))
@Column(name = "PLATFORM_ID")
protected Set<Platform> _platforms; 

-

create table TBL_APP_PLATFORM (   
    APP_ID bigint not null,   
    PLATFORM_ID bigint not null, -- the ordinal number of enum value   
    primary key (APP_ID, PLATFORM_ID)   
);

およびenum Platform注釈なし。

39
axtavt

エンティティのマッピングの下で​​簡単に使用できます。次のようなものがあるとします。

public enum TestEnum { A, B }

次に、エンティティクラスで:

@ElementCollection(targetClass = TestEnum.class)
@CollectionTable(
        name = "yourJoinTable", 
        joinColumns = @JoinColumn(name = "YourEntityId")
)
@Column(name = "EnumId")
private final Set<TestEnum> enumSet= new HashSet<TestEnum >();
2