使用Elasticsearch分页查询全部文档时遭遇JsonMappingException空指针异常
I’ve run into this exact issue before—here’s what’s going on and how to fix it:
Root Cause
When you call findAll(Pageable) on your ElasticsearchRepository, Spring Data Elasticsearch returns an AggregatedPageImpl instance by default. This class is built to handle aggregation results (like facets), but when you’re doing a simple fetch-all without any aggregations, the facets field ends up null. Older versions of Spring Data Elasticsearch have a bug where Jackson tries to serialize this null field, triggering the NullPointerException you’re seeing.
Solutions
1. Upgrade Spring Data Elasticsearch (Recommended)
The cleanest fix is to upgrade to a newer version where this issue is resolved. Versions 4.0 and above properly handle the facets field (either marking it as nullable or using a different implementation for non-aggregated queries).
If you’re using Maven, update your dependency in pom.xml:
<dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-elasticsearch</artifactId> <version>4.4.0</version> <!-- Use the latest version compatible with your Spring Boot setup --> </dependency>
For Gradle:
implementation 'org.springframework.data:spring-data-elasticsearch:4.4.0'
2. Use ElasticsearchOperations to Fetch Pages Without Aggregations
If upgrading isn’t feasible, bypass the repository’s findAll method and use ElasticsearchOperations to run a match-all query, then wrap results in a standard PageImpl (which doesn’t have the problematic facets field).
Modify your ConventionServiceImpl like this:
import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchOperations; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.Query; import java.util.stream.Collectors; @Service @Transactional public class ConventionServiceImpl implements ConventionService { private final ConventionSearchRepository repository; private final ElasticsearchOperations elasticsearchOperations; // Constructor injection (preferred over @Autowired) public ConventionServiceImpl(ConventionSearchRepository repository, ElasticsearchOperations elasticsearchOperations) { this.repository = repository; this.elasticsearchOperations = elasticsearchOperations; } @Override public Page<Convention> getAllConventions(Pageable pageable) { // Build a match-all query with pagination Query query = new NativeSearchQueryBuilder() .withQuery(org.springframework.data.elasticsearch.core.query.QueryBuilders.matchAllQuery()) .withPageable(pageable) .build(); // Execute the query SearchHits<Convention> searchHits = elasticsearchOperations.search(query, Convention.class); // Convert SearchHits to a list of Convention entities var conventions = searchHits.stream() .map(SearchHit::getContent) .collect(Collectors.toList()); // Return a standard PageImpl instead of AggregatedPageImpl return new PageImpl<>(conventions, pageable, searchHits.getTotalHits()); } }
3. Ignore the "facets" Field in Jackson Serialization
If you need to keep using the repository’s findAll method, configure Jackson to ignore the null facets field. Create a Jackson mix-in to exclude the field:
import com.fasterxml.jackson.databind.annotation.JsonIgnoreProperties; // Mix-in to ignore the facets field in AggregatedPageImpl @JsonIgnoreProperties(value = {"facets"}, ignoreUnknown = true) public abstract class AggregatedPageImplMixIn {}
Then register this mix-in in your Jackson configuration:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl; @Configuration public class JacksonConfig { @Bean public ObjectMapper elasticsearchObjectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.addMixIn(AggregatedPageImpl.class, AggregatedPageImplMixIn.class); return mapper; } }
This tells Jackson to skip the facets field during serialization, preventing the NPE.
内容的提问来源于stack exchange,提问作者Aymen Kanzari

