Hibernate: двунаправленное рекурсивное отношение «один ко многим» с одной полиморфной таблицей и одним и тем же столбцом соединения - PullRequest
0 голосов
/ 09 июля 2019

Я работаю над приложением, которое работает с несколькими различными типами продуктов, а именно с турами (как в обзорных турах или аналогичных), вариантами этих туров и опциями (дополнительные дополнения, которые можно забронировать вместе с туром) .

Существует бесчисленное множество свойств, которые существуют во всех вышеперечисленных типах продуктов, названии, описании, продолжительности и т. Д., Поэтому наиболее подходящим способом для их моделирования будет использование класса (абстрактных) Product и таблицы одного продукта и трех отдельные подклассы для Tour, TourVariant и Option, каждый из которых непосредственно расширяется Product. И варианты, и варианты тура относятся только к одному родительскому туру, в свою очередь, каждый тур может иметь любое количество вариантов и опций.

Опущение несущественных битов - это наша текущая настройка:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="subclazz", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue(value="-") // this "root" class is essentially abstract
public class Product implements Comparable<Product> {

    private Long id;
    //more properties that are used for any type of product

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    //getters & setters, hashcode, equals and compareTo methods

}

@Entity
@DiscriminatorValue(value="TOUR")
public class Tour extends Product {

    private SortedSet<Option> options = new TreeSet<Option>();
    private SortedSet<TourVariant> variants = new TreeSet<TourVariant>();
    //some tour specific properties


    @OneToMany(mappedBy="tour",fetch=FetchType.LAZY)
    @SortNatural
    @OnDelete(action=OnDeleteAction.CASCADE)
    @Cascade({CascadeType.PERSIST, CascadeType.MERGE, CascadeType.SAVE_UPDATE})
    public SortedSet<Option> getOptions() {
        return this.options;
    }
    public void setOptions(SortedSet<Option> options) {
        this.options = options;
    }
    public boolean addOption(Option option) {
        option.setTour(this);
        return this.options.add(option);
    }
    public boolean removeOption(Option option) {
        option.setTour(null);
        return this.options.remove(option);
    }

    @OneToMany(mappedBy="tour",fetch=FetchType.LAZY)
    @SortNatural
    @OnDelete(action=OnDeleteAction.CASCADE)
    @Cascade({CascadeType.PERSIST, CascadeType.MERGE, CascadeType.SAVE_UPDATE})
    public SortedSet<TourVariant> getVariants() {
        return this.variants;
    }
    public void setVariants(SortedSet<TourVariant> variants) {
        this.variants = variants;
    }
    public boolean addVariant(TourVariant variant) {
        variant.setTour(this);
        return this.variants.add(variant);
    }
    public boolean removeVariant(TourVariant variant) {
        variant.setTour(null);
        return this.variants.remove(variant);
    }

}

@Entity
@DiscriminatorValue(value="VARIANT")
public class TourVariant extends Product {

    private Tour tour;
    //some tour variant specific properties

    @ManyToOne
    @JoinColumn(name = "variant_tour_id") // <- Any way to make this work with the same join column, e.g. tour_id?
    public Tour getTour() {
        return this.tour;
    }
    public void setTour(Tour tour) {
        this.tour = tour;
    }

}


@Entity
@DiscriminatorValue(value="OPTION")
public class Option extends Product {

    private Tour tour;
    //some option specific properties

    @ManyToOne
    @JoinColumn(name = "option_tour_id") // <- Any way to make this work with the same join column, e.g. tour_id?
    public Tour getTour() {
        return this.tour;
    }
    public void setTour(Tour tour) {
        this.tour = tour;
    }

}

Как видно выше, нам приходилось использовать отдельные столбцы variant_tour_id и option_tour_id, и мы до сих пор не смогли найти решение, которое позволило бы нам использовать один и тот же столбец соединения, например, tour_id для присоединения классов Option и TourVariant к их родительскому туру.

Это, к сожалению, всегда приводит к тому, что Hibernate плюет на пустышку, когда мы пытаемся получить доступ к отношениям tour.getVariants() или tour.getOptions().

org.hibernate.WrongClassException: Object [id=2] was not of the specified subclass [xyz.model.TestOption] : loaded object was of wrong class class xyz.model.TestVariant

Учитывая, что продукт, которому необходимо такое отношение к родительскому туру, может быть только TourVariant или Option, и каждый продукт четко идентифицируется дискриминатором subclazz, есть ли способ заставить это работать и использовать только один tour_id объединить столбец?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...