如何在 jsonb 列上使用 Spring JPA 进行查询?

Posted

技术标签:

【中文标题】如何在 jsonb 列上使用 Spring JPA 进行查询?【英文标题】:How to query with Spring JPA on jsonb columns? 【发布时间】:2020-01-13 23:47:02 【问题描述】:

我正在使用 Spring JPA 和 PostgreSQL 数据库。我有一个实体如下:

@Entity
@TypeDef(name="json_binary", typeClass = com.vladmihalcea.hibernate.type.json.JsonBinaryType.class)
public class LiveTokens 

   @Id
   @GeneratedValue()
   private Long id;

   private String username;

   @Type(type="json_binary")
   @Column(columnDefinition = "jsonb")
   private Token token

Token:

public class Token 
   private Long expireTime;
   private String accessToken;

为了使用 Hibernate 将对象保存到列,我使用 Hibernate Types 项目。 现在我想获取所有过期的LiveTokens。我不能用 Spring JPA 做到这一点。如何使用 Spring data JPA 查询 posgresql jsonb 列?

【问题讨论】:

【参考方案1】:

SQL JSON 函数

如果你想调用一个处理 JSON 列的 SQL 函数,那么你只需要在使用 Hibernate 之前确保你register the function。

JSON 运算符

对于 JSON 运算符,例如 PostgreSQL 运算符(例如,->>),您唯一的选择是使用原生 SQL 查询。 JPQL 或 HQL 不支持特定于数据库的 JSON 运算符。

【讨论】:

【参考方案2】:

使用 EclipseLink 和 spring data jpa,如果您在 db 中的数据类似于:"expireTime":102020230201, "accessToken":"SOMETHING" ,我的第一个问题是为什么要为您的日期使用长数字而不是时间戳(例如'2019-09-14 12:05:00')。如果您使用时间戳,还可以选择管理时区(来自 postgresql 或来自您的源代码)。 关于您的问题,您可以使用FUNC JPQL keyword of EclipseLink(Hibernate 可能有类似的东西)来运行特定于数据库的功能。在下面的示例中,我使用 FUNC('jsonb_extract_path_text', lt.token, 'expireTime') 来获取 token.expireTime 的 json 值。 PostgreSql 方法jsonb_extract_path_text 返回文本,因此你不能做更少的条件,所以我使用JPQL CAST keyword 和(CAST -data- TO -type-) 转换函数的输出。

@Repository
public interface MyRepository extends JpaRepository<LiveTokens, Integer> 

    @Query(value = "SELECT lt FROM LiveTokens lt WHERE CAST(FUNC('jsonb_extract_path_text', lt.token, 'expireTime') AS LongType) < :expirirationThreshold")
    List<LiveTokens> findByExpireTime(@Param("expirirationThreshold") Long expirirationThreshold);

同样,这未经测试。

【讨论】:

这个错误Could not resolve requested type for CAST : int8[select lt from ...] 你得到的错误是关于 INT8 类型的 CAST 使用。您可以将 INT8 替换为其他类型。你也可以试试 LongType 或 Long。【参考方案3】:

这是使用 postgres JSON 查询运算符的本机查询的样子,结合您的示例:

@Query(value="SELECT t.* FROM LiveTokens t WHERE CAST(t.token ->> 'expireTime' AS LONG) < now()", native=true)

假设您的 real 表名是 LiveTokens,本机查询不再使用 JPA 转换,并且表名必须与数据库中的表名匹配。 (您可能还需要指定它的架构。)

【讨论】:

【参考方案4】:

试试看:

服务类:

Long currentTime = new Date().getTime();

存储库:

@Query("SELECT lt FROM LiveTokens lt WHERE lt.token.expireTime <= :currentTime")

List<LiveTokens> findExpiredLiveTokens(@Param("currentTime") long currentTime)

【讨论】:

以上是关于如何在 jsonb 列上使用 Spring JPA 进行查询?的主要内容,如果未能解决你的问题,请参考以下文章

如何根据postgres的jsonb列的where条件(无本机查询)使用jpa在spring boot中选择数据?

Spring Data JPA JSONB参数化

如何在具有空值的列上使用`jsonb_set`

jsonb字段上的Spring-Data-JPA本机查询

如何在 JPA 中使用 Postgres JSONB 数据类型?

DB 列上的 Spring JPA 错误返回与列名相关的错误