是否可以编写Service
,提供一种方法,该方法通过父字段选择一个实体,但返回通过参数(或方法的泛型)传递的子类?
假设Parent
有一个名为flag的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 ...;
}
}
对于所有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;
}