Я работаю над приложением, которое работает с несколькими различными типами продуктов, а именно с турами (как в обзорных турах или аналогичных), вариантами этих туров и опциями (дополнительные дополнения, которые можно забронировать вместе с туром) .
Существует бесчисленное множество свойств, которые существуют во всех вышеперечисленных типах продуктов, названии, описании, продолжительности и т. Д., Поэтому наиболее подходящим способом для их моделирования будет использование класса (абстрактных) 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
объединить столбец?