如何将@ConstructorResult与"设置<SomeEnum>"字段一起使用



我正在尝试为具有Set enum值的字段的类创建一个带有@ConstructorResult@NamedNativeQuery

兽医JPA.java:

@Entity
@Table(name = "veterinarians")
@Setter
@Getter
@NoArgsConstructor
@NamedNativeQueries({
        @NamedNativeQuery(
                name = VeterinarianJPA.FIND_ALL_VETS,
                query = "SELECT v.id, v.name, vs.specialisations " +
                        "FROM veterinarians v " +
                        "JOIN veterinarian_specialisations vs ON v.id = vs.vet_id",
                resultSetMapping = VeterinarianJPA.VETERINARIAN_RESULT_MAPPER
        )})
@SqlResultSetMappings({
        @SqlResultSetMapping(
                name = VeterinarianJPA.VETERINARIAN_RESULT_MAPPER,
                classes = @ConstructorResult(
                        targetClass = Veterinarian.class,
                        columns = {
                                @ColumnResult(name = "id", type = Long.class),
                                @ColumnResult(name = "name"),
                                @ColumnResult(name = "specialisations", type = Set.class)
                        }
                )
        )})
class VeterinarianJPA {
    static final String FIND_ALL_VETS = "net.kemitix.naolo.gateway.data.jpa.findAllVets";
    static final String VETERINARIAN_RESULT_MAPPER = "net.kemitix.naolo.gateway.data.jpa.Veterinarian";
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    @ElementCollection
    @Enumerated(EnumType.STRING)
    @CollectionTable(
            name = "veterinarian_specialisations",
            joinColumns = @JoinColumn(name = "vet_id")
    )
    private final Set<VetSpecialisation> specialisations = new HashSet<>();
}

兽医.java:

public final class Veterinarian {
    private Long id;
    private String name;
    private Set<VetSpecialisation> specialisations;
    public Veterinarian() {
    }
    public Veterinarian(final long id,
                        final String name,
                        final Set<VetSpecialisation> specialisations) {
        this.id = id;
        this.name = name;
        this.specialisations = new HashSet<>(specialisations);
    }
    public long getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public Set<VetSpecialisation> getSpecialisations() {
        return new HashSet<>(specialisations);
    }
}

兽医专业化.java:

public enum VetSpecialisation {
    RADIOLOGY,
    DENTISTRY,
    SURGERY
}

当我尝试执行命名查询时:

entityManager.createNamedQuery(VeterinarianJPA.FIND_ALL_VETS, Veterinarian.class)
             .getResultStream()

我得到以下异常:

java.lang.IllegalArgumentException: Could not locate appropriate constructor on class : net.kemitix.naolo.entities.Veterinarian
    at org.hibernate.loader.custom.ConstructorResultColumnProcessor.resolveConstructor(ConstructorResultColumnProcessor.java:92)
    at org.hibernate.loader.custom.ConstructorResultColumnProcessor.performDiscovery(ConstructorResultColumnProcessor.java:45)
    at org.hibernate.loader.custom.CustomLoader.autoDiscoverTypes(CustomLoader.java:494)
    at org.hibernate.loader.Loader.processResultSet(Loader.java:2213)
    at org.hibernate.loader.Loader.getResultSet(Loader.java:2169)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1930)
    at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1892)
    at org.hibernate.loader.Loader.scroll(Loader.java:2765)
    at org.hibernate.loader.custom.CustomLoader.scroll(CustomLoader.java:383)
    at org.hibernate.internal.SessionImpl.scrollCustomQuery(SessionImpl.java:2198)
    at org.hibernate.internal.AbstractSharedSessionContract.scroll(AbstractSharedSessionContract.java:1058)
    at org.hibernate.query.internal.NativeQueryImpl.doScroll(NativeQueryImpl.java:217)
    at org.hibernate.query.internal.AbstractProducedQuery.scroll(AbstractProducedQuery.java:1462)
    at org.hibernate.query.internal.AbstractProducedQuery.stream(AbstractProducedQuery.java:1486)
    at org.hibernate.query.Query.getResultStream(Query.java:1110)

我希望 SQL 为多值Set返回多行而不是单个值,这会导致构造函数不匹配。如何更改 SQL 以生成构造函数的正确输入,或者是否需要进行其他配置更改?

好吧,我不确定这是否可能以您想要的方式进行。但是,您可以使用specialisations表上使用LISTAGG函数,通过使用某种分隔符将specialisationsveterinarians内联。

因此,查询应如下所示:

SELECT v.id, v.name 
(SELECT LISTAGG(vs.type, ';') 
  WITHIN GROUP (ORDER BY vs.type) 
  FROM veterinarian_specialisations vs 
  WHERE vs.vet_id = v.id) specialisations
FROM veterinarians v;

查询将返回兽医及其分号分隔的专业:

1   NAME   DENTISTRY;RADIOLOGY

然后在Veterinarian类构造函数中,您必须将字符串结果重新映射回VetSpecialisation集。我使用 Java 8 stream api 只是为了方便。

public final class Veterinarian {
private Long id;
private String name;
private Set<VetSpecialisation> specialisations;
public Veterinarian() {
}
public Veterinarian(final long id,
                    final String name,
                    final String specialisations) {
    this.id = id;
    this.name = name;
    this.specialisations = Arrays.asList(specialisations.split(";"))
        .stream()
        .map(VetSpecialisation::valueOf) //Map string to VetSpecialisation enum.
        .collect(Collectors.toSet());
}

相关内容

  • 没有找到相关文章

最新更新