Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring JPA Repository - Operator SIMPLE_PROPERTY on jsonObject requires a scalar argument

Tags:

I'm updating some Spring Boot Applications with JPA queries. Everything works fine except from one specific kind of queries (findByJsonNode). This worked fine in earlier versions, but after I upgraded my beans can't be created.

It worked fine up to Spring Boot 2.1.4.RELEASE and spring cloud version Greenwich.SR1, but when I upgrade to Spring Boot 2.2.4.RELEASE and spring cloud version Hoxton.RELEASE, Spring can't create my repository bean.

Caused by: java.lang.IllegalStateException: Operator SIMPLE_PROPERTY on searchDto requires a scalar argument, found class com.fasterxml.jackson.databind.JsonNode in method public abstract se.company.search.Search se.company.search.SearchRepository.findFirstBySearchDto(com.fasterxml.jackson.databind.JsonNode).
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.throwExceptionOnArgumentMismatch(PartTreeJpaQuery.java:171)
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.validate(PartTreeJpaQuery.java:147)
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:90)
    ... 73 common frames omitted.
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.throwExceptionOnArgumentMismatch(PartTreeJpaQuery.java:171) ~[spring-data-jpa-2.2.4.RELEASE.jar:2.2.4.RELEASE]
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.validate(PartTreeJpaQuery.java:147) ~[spring-data-jpa-2.2.4.RELEASE.jar:2.2.4.RELEASE]
    at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:90) ~[spring-data-jpa-2.2.4.RELEASE.jar:2.2.4.RELEASE]
    ... 74 common frames omitted

The repository class looks like below

@Repository
public interface SearchRepository extends PagingAndSortingRepository<Search, String> {

    Search findFirstBySearchDto(JsonNode searchDto);

}

Entity

/**
 * Entity for saving as search
 *
 * Users can save searches and every search will be stored in history for reference
 */
@Slf4j
@Data
@Entity(name = "search")
@Table(name = "tt_searches", indexes = {
        @Index(columnList = "searchDto", name = "searchdto_hidx")
})
@TypeDef(
        name = "json-node",
        typeClass = JsonNodeStringType.class
)
@EqualsAndHashCode(exclude = { "id", "savedSearches", "searchHistory" })
public class Search {

    public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().findAndRegisterModules();

    public Search() {
    }

    public Search(SearchDto searchDto, List<SavedSearch> savedSearches, List<SearchHistory> searchHistory) {
        this.searchDto = OBJECT_MAPPER.valueToTree(searchDto);
        this.savedSearches = savedSearches;
        this.searchHistory = searchHistory;
    }

    public Search(JsonNode searchDto, List<SavedSearch> savedSearches, List<SearchHistory> searchHistory) {
        this.searchDto = searchDto;
        this.savedSearches = savedSearches;
        this.searchHistory = searchHistory;
    }

    @Id
    @Column(columnDefinition = "CHAR(36)")
    @GeneratedValue(generator = "uuid")
    @GenericGenerator(name = "uuid", strategy = "uuid2")
    private String id;

    @Type(type = "json-node")
    @Column(columnDefinition = "VARCHAR(2000)", unique = true)
    private JsonNode searchDto;

    @OneToMany(mappedBy = "search", fetch = FetchType.LAZY)
    @OrderBy("name DESC")
    @JsonIgnore
    private List<SavedSearch> savedSearches;

    @OneToMany(mappedBy = "search", fetch = FetchType.LAZY)
    @OrderBy("timestamp DESC")
    @JsonIgnore
    private List<SearchHistory> searchHistory;

    public SearchDto getSearchDto() {
        try {
            return OBJECT_MAPPER.treeToValue(searchDto, SearchDto.class);
        } catch (JsonProcessingException e) {
            log.error("Could not convert JsonNode to SearchDto when retrieving data from entity: {}", this.id);
            return null;
        }
    }

    public void setSearchDto(SearchDto searchDto) {
        this.searchDto = OBJECT_MAPPER.valueToTree(searchDto);
    }
}
like image 551
olahell Avatar asked Feb 25 '20 14:02

olahell


2 Answers

I encountered this same problem. I worked around it by changing the name of my method by adding "In" to the end of it.

For your example, it would be

findAllByTagsIn(java.util.Set)

See the "supported keywords inside method names" table here: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation

like image 143
smythie Avatar answered Sep 16 '22 12:09

smythie


If you are using the repository and trying to get a list based on a parameter which is a type of Set you should be careful about writing queries! In my case:

findAllBySchoolId(Set<Long> schoolIds) // wrong
findAllBySchoolIdIn(Set<Long> schoolIds) // correct

Credits to user:8569305(smythie)!

like image 39
SagitSri Avatar answered Sep 19 '22 12:09

SagitSri