2010-06-30 101 views
17

我使用Hibernate 3.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) 
); 

適當的外鍵也從此表創建到應用程序表和平臺表。到現在爲止還挺好。

一個問題我運行到是,當我試圖堅持一個應用對象:

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

我想發生在所創建的平臺表是適當的行,即創建一個行對於Windows和Linux,如果它們不存在。然後,應創建新應用程序的一行,然後創建新應用程序與連接表中兩個平臺之間的映射。當我試圖堅持應用

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. 有沒有更好的方式來建模是我想做的事,例如使用Enum是否合適?
  2. 如果我的模型沒問題,我該如何正確地堅持所有的對象?

我一直在掙扎幾個小時,我試圖重新創建上面的所有代碼,但它可能不完整和/或準確。我希望有人會指出一些明顯的東西!

回答

32

您應該決定您的Platform是否爲實體或不是。

如果它是一個實體,則它不能是enum,因爲可能平臺的列表存儲在數據庫中,而不是存儲在應用程序中。它應該是一個帶有@Entity註釋的常規類,並且您將擁有正常的多對多關係。

如果它不是一個實體,那麼你不需要TBL_PLATFORM表,並且你沒有多對多的關係。在這種情況下,您可以將一組Platform表示爲具有位標誌的整數字段,或者表示爲簡單的一對多關係。 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和無註釋。

+0

我使用Hibernate的舊版本生成的架構。通過遵循http://stackoverflow.com/questions/2734971/hibernate3-maven-plugin-dependencies-for-newer-version-of-hibernate,我將依賴項設置爲3.5.3-FINAL和「org.hibernate.MappingException:無法確定類型:java.util.Set「消失了。 我遵循了你所建議的@ElementCollection方法,使用一個普通的非實體枚舉,現在將設置映射到一個連接表。並且,通過在屬性上設置@Enumerated,我可以設置枚舉值是否作爲整數或字符串存儲在數據庫中。 – gnuf 2010-07-01 16:24:22

+0

@axtavt你可以請幫助我與http://stackoverflow.com/questions/7687409/hibernate-criteria-query-for-collection-table/7693793#7693793我附上了一個賞金那裏..很需要幫助.. – 2011-10-10 06:19:18

+0

@ axtavt你可以在你的文章中添加principale表「TBL_APP」的結構嗎?日Thnx – 2017-12-11 16:30:48

2

簡單使用下面的映射對您的實體。假設我們有:

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 >();