Spring Данные JPA (Hibernate): Как получить конкретный объект, используя только поле в его абстрактном суперклассе?
Рассмотрим следующую иерархию, где сущности WidgetA
и WidgetB
расширяют абстрактный Widget
суперкласс:
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Widget implements Serializable {
@Column(name="serialNumber", length=64, nullable=false, unique=true)
private String serialNumber;
...
и
@Entity
public class WidgetA extends Widget implements Serializable {
...
и
@Entity
public class WidgetB extends Widget implements Serializable {
...
Мне нужно искать Widgets на serialNumber
, но я не знаю конкретного типа виджета, который я ищу во время выполнения. Каков правильный способ поиска виджетов с помощью serialNumber
, так что если serialNumber
соответствует WidgetA
, возвращается экземпляр WidgetA
и т.д.?
Я пытаюсь использовать findyBySerialNumber()
в DAO Widget
, и я получаю сообщение об ошибке, указывающее, что я не могу создать экземпляр абстрактного класса, что имеет смысл, но я думал, что поставщик постоянства знает, как просматривать в конкретных таблицах дочерних сущностей и возвращать правильный экземпляр. Могу ли я это сделать?
Я использую "Spring Data JPA", поэтому Widget DAO очень прост:
public interface WidgetDAO extends JpaRepository<Widget, Long> {
public Widget findBySerialNumber(String serialNumber);
}
Ответы
Ответ 1
Кажется, вы не указали дискриминатор явно для вашей иерархии виджетов. Я думаю, вы можете попытаться определить его явно, потому что Spring Data будет манипулировать байт-кодом для генерации запросов, и поэтому я подозреваю, что SpringData должны явно определять эти значения.
Кроме того, в подклассах необходимо указать значение дискриминатора для каждого подкласса.
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name="WIDGET_TYPE")
public abstract class Widget implements Serializable {
@Column(name="serialNumber", length=64, nullable=false, unique=true)
private String serialNumber;
...
-
@Entity
@DiscriminatorValue("A")
public class WidgetA extends Widget implements Serializable {
...
-
@Entity
@DiscriminatorValue("B")
public class WidgetB extends Widget implements Serializable {
...
Ответ 2
Ваши объекты и аннотации выглядят хорошо для меня. Я смог взять их, сохранить виджет в базу данных и извлечь его без проблем.
Я думаю, проблема в ваших данных. Hibernate, вероятно, находит строку в таблице Widget
, которая не имеет соответствующей строки в любой из таблиц подкласса и поэтому пытается создать экземпляр суперкласса и не удается.
С InheritanceType.JOINED
вы можете указать дискриминатор или позволить ему сделать это за вас (я полагаю, проверяя, является ли строка с этот идентификатор существует в таблице подкласса). Но в любом случае вам нужно проверить свои данные, чтобы убедиться, что в таблице суперкласса нет записи без соответствующей строки подкласса.
Как правило, я поддерживаю рекомендацию @ben75 о том, что вы явно указываете @Discriminator
для иерархии классов. Лучше иметь контроль над вашими значениями дискриминатора, чтобы последующие изменения кода не изменяли значения неожиданными способами.
Ответ 3
Hibernate поддерживает этот запрос просто отлично и с радостью вернет экземпляр правильного подкласса. Не зная, что происходит в реализации Spring Data, которая пытается создать экземпляр Widget, вы должны просто объявить запрос и запустить его напрямую, а не использовать синтаксический анализатор.
public interface WidgetDAO extends JpaRepository<Widget, Long> {
@Query("select w from Widget w where w.serialNumer = ?1")
public Widget findBySerialNumber(String serialNumber);
}