Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 LiveTokens that expired. I can't do it with Spring JPA. How do I query to posgresql jsonb column with Spring data JPA?

like image 966
Morteza Malvandi Avatar asked Sep 12 '19 06:09

Morteza Malvandi


3 Answers

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 (e.g., ->>), the only option you have is to use a native SQL query. JPQL or HQL don't support database-specific JSON operators.

like image 130
Vlad Mihalcea Avatar answered Nov 18 '22 00:11

Vlad Mihalcea


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.

like image 28
Georgios Syngouroglou Avatar answered Nov 18 '22 00:11

Georgios Syngouroglou


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.)

like image 1
rémy Avatar answered Nov 18 '22 00:11

rémy