2010-06-30 52 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 >();