是否可以编写Service,提供一种方法,该方法通过父字段选择一个实体,但返回通过参数(或方法的泛型)传递的子类

假设Parent有一个名为flag的String字段,并且有两个孩子Child1Child2,那么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)方法,可用于UiParameterBatchParameter。此方法由其他服务用于保存各种子类

  • 现在可以在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 ...;
        }
    }
    

    对于所有Childs方法,通常的“服务/存储库”方法在我们的案例中会导致仅具有findFlag方法的样板类,该方法从Child1复制到ChildN。

    更改域(继承策略)将是一个问题。

    基于Maciejs的Spring-Solution解决方案:
    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);
    }
    

    最佳答案

    JPQL中有TYPE运算符,您可以按以下方式使用它:

    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;
    }
    

    07-27 21:19