如何在 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 项目。
现在我想获取所有过期的LiveToken
s。我不能用 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中选择数据?