简体   繁体   中英

How to query with Spring JPA on jsonb columns?

I'm using spring JPA with PostgreSQL database. I have an Entity as follow:

@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
}

and Token :

public class Token {
   private Long expireTime;
   private String accessToken;
}

For saving object to column with Hibernate, I use Hibernate Types project. Now I want to get All LiveToken s that expired. I can't do it with Spring JPA. How do I query to posgresql jsonb column with Spring data JPA?

SQL JSON functions

If you want to call a SQL function that processes the JSON column, then you just need to make sure you register the function with Hibernate prior to using it.

JSON operators

For JSON operators, like the PostgreSQL ones (eg, ->> ), the only option you have is to use a native SQL query. JPQL or HQL don't support database-specific JSON operators.

Using EclipseLink and spring data jpa, if your data in db is something like: {"expireTime":102020230201, "accessToken":"SOMETHING" }, my first question is why to use long numbers for your dates instead of timestamps (ex '2019-09-14 12:05:00'). If you use timestamps there are also options to manage timezones (either from postgresql or from you source code).
Regarding your issue you may use the FUNC JPQL keyword of EclipseLink (Hibernate may have something similar) in order to run a database specific function. In the example below I use FUNC('jsonb_extract_path_text', lt.token, 'expireTime') to get the values of the json for token.expireTime.
PostgreSql method jsonb_extract_path_text returns text, thus you cannot do a less that condition, so I cast the output of the function using JPQL CAST keyword with (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);
}

Again, this is not tested.

This is how a native query using postgres JSON query operators would look like, incoorporating your example:

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

Assuming your real tablename is LiveTokens, native queries do no longer use the JPA translations, and the tablename has to match the one in the DB. (You may also need to specify it's schema.)

Try it:

Service class:

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

Repository:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM