由父字段查询但返回子类

时间:2014-07-03 20:13:15

标签: java spring jpa spring-data spring-data-jpa

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

假设Parent有一个名为flag的字符串字段和两个孩子Child1Child2ParentService应该可以这样使用:

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),只有1个方法看起来几乎相似。

@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。

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

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

1 个答案:

答案 0 :(得分:2)

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