0

I have a table with a JSONB column in Postgres. Let's call it pet.

id name details
1 Cat {"furColour": "brown"}
2 Dog {"coatColour": "black", "bark": "loud"}
3 Parrot {"beakColour": "red", "featherColour": "green"}

I'm trying to come up with a Spring Data JPA specification that can filter the records of this table based on the attributes of the details column. However, I need to be able to filter by attributes ending with Colour, not the exact attribute name. I was able to achieve this by using a Postgres query such as this one:

select * from pet 
where exists (
    select from jsonb_each_text(details) as kv 
    where kv.key like '%Colour' 
    and kv.value = 'red');

I'm finding it extremely challenging to come up with a JPA specification for the above where clause. I have tried this:

(root, criteria, builder) -> {
    Expression<Tuple> function = 
        builder.function("jsonb_each_text", Tuple.class, root.get("details"));
            
    // Not sure how to set alias or if it's even 
    // necessary as in my SQL
    var kv = function.alias("kv");

    var subQuery = builder.createQuery().subquery(Tuple.class).select(function);

    // I'm unable to work out how to determine this root. 
    // I cannot use Tuple as a root as it's obviously not 
    // an entity and thus I get an appropriate error.
    var functionRoot = criteria.from(Tuple.class); 

    return builder.exists(subQuery.where(builder.like(functionRoot.get("key"), "%Colour"), builder.equal(functionRoot.get("value"), "red")));
};

My Pet entity class looks like this:

import com.vladmihalcea.hibernate.type.json.JsonType;
import lombok.Getter;
import lombok.Setter;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;

import javax.persistence.Column;
import javax.persistence.Entity;

// Other imports skipped for brevity

@Getter
@Setter
@Entity
@TypeDef(name = "json", typeClass = JsonType.class)
public class Pet {

    @Id
    @GeneratedValue
    private UUID id;

    private String name;

    @Type(type = "json")
    @Column(columnDefinition = "jsonb")
    private Map<String, Object> details = Collections.emptyMap();

}

What am I doing wrong here?

Any tips, pointers, guidance here would be greatly appreciated.

15
  • How are you defining your entity class? I'm trying to replicate to see what I can try. Commented Feb 27, 2023 at 18:05
  • @CalvinP. thanks. I've updated my question with the entity class. Commented Feb 27, 2023 at 22:54
  • Sorry, not sure what happened here... I wanted to edit and endup deleting it... here is it back: pastebin.com/EHdSRMTu Commented Feb 28, 2023 at 0:19
  • 1
    I want to add for you helpful information based on your provided question: "JPA doesn't natively support converting JSON or JSONB fields." as stated here. Commented Feb 28, 2023 at 21:54
  • 1
    Thanks a lot for your time and help @JorgeCampos. Unfortunately the 2nd option doesn't work as it gives me a Requested attribute was not a map error and I cannot use the first option because it uses internal Hibernate API (even if I move to JPA API, it doesn't compile). I think for now I'll write a custom DB function and revisit this later when we migrate to Spring Boot 3 and JPA 3 (Hibernate 6). Commented Mar 1, 2023 at 16:03

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.