Постоянный набор Enums в однонаправленном отображении «многие ко многим» - PullRequest
20 голосов
/ 30 июня 2010

Я использую 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. Если с моей моделью все в порядке, как мне правильно сохранить все объекты?

Я боролся с этим часами и пытался воссоздать весь приведенный выше код, но он может быть неполным и / или точным. Я надеюсь, что кто-то укажет на что-то очевидное!

Ответы [ 2 ]

39 голосов
/ 01 июля 2010

Вы должны решить, является ли ваш Platform субъектом или нет.

Если это сущность, она не может быть enum, поскольку список возможных платформ хранится в базе данных, а не в приложении. Это должен быть обычный класс с аннотацией @Entity, и у вас будет нормальное отношение «многие ко многим».

Если это не сущность, то вам не нужна таблица TBL_PLATFORM, и у вас нет отношения «многие ко многим». В этом случае вы можете представить набор Platform s либо в виде целочисленного поля с битовыми флагами, либо в виде простого отношения «один ко многим». 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 без аннотаций.

2 голосов
/ 23 октября 2015

Простое использование ниже сопоставления на вашей сущности. Предположим, что у нас есть:

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