Подтвердить что ты не робот

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);
}
4b9b3361

Ответ 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);
}