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

olahell picture olahell · Feb 25, 2020 · Viewed 16.6k times · Source

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);
    }
}

Answer

smythie picture smythie · Mar 4, 2020

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