【问题标题】:Persisting set of Enums in a many-to-many unidirectional mapping在多对多单向映射中持久化枚举集
【发布时间】:2011-03-10 07:53:42
【问题描述】:

我正在使用带有注释的 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);

我希望在 Platform 表中创建适当的行,即为 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. 如果我的模型没问题,如何正确持久化所有对象?

我已经为此苦苦挣扎了好几个小时,并且尝试重新创建上面的所有代码,但它可能不完整和/或不准确。我希望有人能指出一些显而易见的事情!

【问题讨论】:

    标签: java hibernate jpa persistence annotations


    【解决方案1】:

    你应该决定你的Platform是否是一个实体

    如果是实体,则不能是enum,因为可能的平台列表存储在数据库中,而不是应用程序中。它应该是带有@Entity 注释的常规类,并且您将具有正常的多对多关系。

    如果它不是实体,那么您不需要TBL_PLATFORM 表,并且您没有多对多关系。在这种情况下,您可以将一组Platforms 表示为带有位标志的整数字段,或者表示为简单的一对多关系。 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 没有注释。

    【讨论】:

    • 我使用的是旧版本的 Hibernate 来生成模式。通过关注 stackoverflow.com/questions/2734971/… ,我将依赖项设置为 3.5.3-FINAL 并且“org.hibernate.MappingException:无法确定类型:java.util.Set”消失了。我遵循您建议的 @ElementCollection 方法,使用普通的非实体枚举,现在 Set 映射到连接表。而且,通过在属性上设置@Enumerated,我可以设置枚举值是作为整数还是字符串存储在数据库中。
    • @axtavt 你能帮我处理一下stackoverflow.com/questions/7687409/… 我在那里附上了一个赏金..非常需要帮助..
    • @axtavt 您可以在您的帖子中添加主体表“TBL_APP”的结构吗?谢谢
    【解决方案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<>();
    

    【讨论】:

      【解决方案3】:

      下面的例子展示了当 Module 是一个实体而 Langue 是一个枚举时的情况。

      @Entity
      public class Module {
      
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
      
        private String libelle;
      
        @ElementCollection(targetClass = Langue.class, fetch = FetchType.EAGER)
        @CollectionTable(name = "link_module_langue",
                  joinColumns = @JoinColumn(name = "module_id", referencedColumnName = "id"))
        @Enumerated(EnumType.STRING)
        @Column(name = "langue")
        private Set<Langue> langues;
      } 
      public enum Langue {
        FRANCAIS, ANGLAIS, ESPAGNOLE
      }
      

      你应该创建link_module_langue表,请看下面的sql代码:

      CREATE TABLE `link_module_langue` (
        `module_id` BIGINT(20) NOT NULL,
        `langue` VARCHAR(45) NOT NULL,
        PRIMARY KEY (`module_id`, `langue`),
        CONSTRAINT `module_fk`
          FOREIGN KEY (`module_id`)
          REFERENCES `module` (`id`)
          ON DELETE CASCADE
          ON UPDATE CASCADE);
      

      注意:Langue 不是实体,不会有自己的表格。

      【讨论】:

        猜你喜欢
        • 2021-10-16
        • 1970-01-01
        • 2013-06-02
        • 2020-12-19
        • 1970-01-01
        • 2011-01-20
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多