Можно ли написать 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);
}
Там 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;
}