【问题标题】:Using JPA query: search in postgres array and using 'in' clause in 1 query使用 JPA 查询:在 postgres 数组中搜索并在 1 个查询中使用 'in' 子句
【发布时间】:2021-09-08 16:48:01
【问题描述】:

有一个过滤器端点可以查询多个属性。 (使用的是Spring Boot 2.4.5) 这是实体:

@Data
@Entity
@Table(name = "organization")
@TypeDefs({@TypeDef(name = "dbArray", typeClass = CustomArrayType.class)})
public class Organization implements Serializable {

  @Id
  private Long id;

  @Column
  private String name;

  @Column
  private String otherName;

  @Column(name = "organization_types")
  @Type(type = "dbArray")
  private List<String> organizationTypes;
}

CustomArrayType 是一个自定义的 Hibernate UserType。在postgres中,该列的定义是varchar[]

这是存储库:

@Repository
public interface OrganizationRepository extends JpaRepository<Organization, Long> {

@Query(
      value =
          "select o from Organization o where"
              + " (:organizationType is null OR :organizationType in (o.organizationTypes)) AND"
              + " (:otherNames is null OR o.otherName in (:otherNames))")
  List<Organization> findOrganizationByTypeAndOtherNamesIn(
      @Param("organizationType") String organizationType,
      @Param("otherNames") List<String> otherNames);
}

但是,我得到一个异常,原因如下: Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = character varying[] Hint: No operator matches the given name and argument types. You might need to add explicit type casts. Position: 465

我尝试使用原生查询:

@Query(
        value =
            "select * from organization o where"
                + " (:organizationType is null OR :organizationType = ANY(o.organization_types)) AND"
                + " (:otherNames is not null AND o.otherName in (:otherNames))",
        nativeQuery = true)
    List<Organization> findOrganizationByTypeAndOtherNamesIn(
        @Param("organizationType") String organizationType,
        @Param("otherNames") List<String> otherNames);

但不幸的是,我得到以下原因:

Caused by: org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bytea
  Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
  Position: 116

(位置 116 是 otherNames 查询开始的位置)。 我不想将otherNames 转换为varchar,因为我提供了List,因此这也不会导致有效的查询。

有谁知道如何让这个查询在本机查询(postgres 10)或 JPQL 中工作?

【问题讨论】:

  • 您能否再次检查删除查询中的:otherNames is not null 部分?我认为这是它正在打破的地方
  • 不幸的是,这不起作用,得到了同样的错误

标签: java postgresql spring-boot hibernate jpa


【解决方案1】:

您需要一个自定义函数,例如my_any 包装 o.organizationTypes 并呈现 any “函数”。这个问题类似如下:unexpected token: '<function_name>' error when using hibernate, JPQL and postgres function

那么你可以这样使用它:

@Repository
public interface OrganizationRepository extends JpaRepository<Organization, Long> {

@Query(
      value =
          "select o from Organization o where"
              + " (:organizationType is null OR :organizationType = my_any(o.organizationTypes)) AND"
              + " (:otherNames is null OR o.otherName in (:otherNames))")
  List<Organization> findOrganizationByTypeAndOtherNamesIn(
      @Param("organizationType") String organizationType,
      @Param("otherNames") List<String> otherNames);
}

【讨论】:

    【解决方案2】:

    好的,即使您优雅地解决了这些问题,您还有一些额外的问题甚至还没有遇到。这是我的建议。您需要其中两个查询是原生的。

    @Query(value = "select o from Organization o where o.otherName in :otherNames")
    List<Organization> findOrganizationByOtherNamesIn(
          @Param("otherNames") List<String> otherNames);
    }
    
    @Query(value = "select * from organization o where"
            + " array_position(o.organization_types, :organizationType) is not null "
            + " AND o.otherName in (:otherNames)",
            nativeQuery = true)
    List<Organization> findOrganizationByTypeAndOtherNamesIn(
          @Param("organizationType") String organizationType,
          @Param("otherNames") List<String> otherNames);
    }
    
    @Query(value = "select * from organization o where"
            + " array_position(o.organization_types, :organizationType) is not null ",
            nativeQuery = true)
    List<Organization> findOrganizationByType(
          @Param("organizationType") String organizationType);
    }
    

    在您的控制器/服务中,您可以这样称呼它:

    if (organizationType != null && otherNames != null && !otherNames.isEmpty()) {
    // that isEmpty is vital to avoid a syntax error on an empty list
        repo.findOrganizationByTypeAndOtherNamesIn(organizationType, otherNames);
    }
    else if (otherNames != null && !otherNames.isEmpty()) {
        repo.findOrganizationByOtherNamesIn(otherNames);
    }
    else if (organizationType != null) {
        repo.findOrganizationByType(organizationType);
    }
    else {
        // this method already exists and is the default logic when both parameters are null
        repo.findAll();
    }
    

    从技术上讲,还有另一种可能的解决方法,但如果 otherNames 列表不为空,而是为空,它仍然会给您带来问题。

    【讨论】:

    • 我想避免控制器/服务中的 if-else 结构,只使用 1 个查询。我知道当我使用原始 postgres 时这是可能的,所以我希望 JPA 也可以。但不幸的是没有。无论如何感谢您的回答!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-20
    • 2010-10-24
    • 1970-01-01
    相关资源
    最近更新 更多