Запрос по родительскому полю, но возврат дочернего класса

1

Можно ли написать Service который предоставляет метод, который выбирает сущности родительским полем, но возвращает дочерний класс, который передается параметром (или общим методом)?

Предполагая, что Parent имеет поле String, называемое флагом, и два Child1 и Child2, ParentService должен использоваться следующим образом:

Child1 child1 = parentService.findFlag(Child1.class, "de");
Child2 child2 = parentService.findFlag(Child2.class, "de");

или

Child1 child1 = parentService.findFlag<Child1>("de");
Child2 child2 = parentService.findFlag<Child2>("de");

Подробный пример:

Поскольку это довольно сложно объяснить, этот код должен объяснить сценарий сверху:

Java-классы:

  • Parameter - абстрактный родительский класс, имеющий общее поле для всех дочерних элементов
    • UiParameter расширяет параметр
    • Параметр BatchParameter расширяет параметр
  • ParameterService - предоставляет метод save(Parameter) который может использоваться для UiParameter и BatchParameter Этот метод используется другими службами для сохранения всех видов детей

Теперь было бы удобно добавить find(UiParameter.class, commonFieldSelector) или find(BatchParameter.class, commonFieldSelector) в ParameterService.

В настоящее время существует 4 класса коллаборации (UiParameterService, UiParameterRepository, BatchParameterService, BatchParameterRepository) всего лишь одним методом, который выглядит почти аналогичным.

@Data
@EqualsAndHashCode(callSuper = true)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(discriminatorType = DiscriminatorType.STRING)
@DiscriminatorValue("PARAMETER")
public abstract class Parameter extends AbstractEntity
{
    // all children will share that
    @ManyToOne
    private Application commonField;
}

@Data
@Entity
@EqualsAndHashCode(callSuper = true)
public class UiParameter extends Parameter
{
    private String foo;
}

@Data
@Entity
@EqualsAndHashCode(callSuper = true)
public class BatchParameter extends Parameter
{
    private int size;
}

public interface ParameterRepository extends JpaRepository<Parameter, Long>
{
    // That works great, but it returns all kind of children...
    List<Parameter> findByCommonField(final Application commonField);
}

@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class ParameterService
{
    private final ParameterRepository parameterRepository;

    public ... findByCommonField...(...)
    {
        return ... magic ...;
    }
}

Обычный подход "Сервис/Репозиторий для всех детей" ведет в нашем случае в классы шаблонов, имеющие только метод findFlag который копируется из Child1 в ChildN.

Изменение домена (стратегия наследования) будет проблемой.

Spring-Solution на основе ответа Maciejs:

public interface ParameterRepository extends JpaRepository<Parameter, Long>
{
    @Query("SELECT e FROM Parameter e WHERE TYPE(e) = ?1 AND e.commonField = ?2")
    <E extends Parameter> List<E> findByTopic(final Class<E> e, final Application commonField);
}
Теги:
spring
jpa
spring-data-jpa
spring-data

1 ответ

2
Лучший ответ

Там TYPE оператор в JPQL, вы можете использовать его следующим образом:

SELECT e
FROM EntityClass
WHERE TYPE(e) = EntityClassSubclass

Вы можете использовать этот запрос, а затем написать общий метод, который приводит результаты к желаемому типу.

т.е.

private static <T extends SuperClass> List<T> queryForClass(Class<T> myClass) {

    Query q = em.createQuery("SELECT p FROM SuperClass p WHERE TYPE(p) = :myClass");
    q.setParameter("myClass", myClass);
    List<? extends SuperClass> resultList = q.getResultList();

    return (List<T>) resultList;
}
  • 0
    На основании вашего предложения я добавил весеннее решение в мой вопрос. Каким-то образом добавление его в наш ответ было отклонено ... Спасибо.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню