Skip to content

Unified Criteria API support

Uragami Taichi edited this page Jun 8, 2025 · 3 revisions

This document describes the support for the Unified Criteria API in doma-spring-boot, which provides seamless integration between Spring Data's Pageable interface and Doma's Criteria API.

Overview

The Unified Criteria API support bridges the gap between Spring Data pagination (Pageable) and Doma's Criteria API, allowing you to:

  • Convert Pageable pagination parameters into Doma's limit() and offset() methods
  • Transform Spring Data's Sort information into Doma's orderBy() clauses
  • Handle property name mapping between Spring Data and Doma entity metamodels

Core Components

UnifiedCriteriaPageable

The UnifiedCriteriaPageable class is the main adapter that converts Pageable objects into Doma Criteria API specifications.

Key Methods

  • offset() - Converts Pageable page information to offset value
  • limit() - Converts Pageable page size to limit value
  • orderBy() - Creates ordering consumer based on sort specifications

PropertyMetamodelResolver

A functional interface that maps property names to Doma's PropertyMetamodel instances:

@FunctionalInterface
public interface PropertyMetamodelResolver {
    Optional<PropertyMetamodel<?>> resolve(String propertyName);
}

Auto-Configuration

The Unified Criteria API support is automatically configured when the following classes are present on the classpath:

  • org.seasar.doma.jdbc.criteria.Entityql
  • org.seasar.doma.jdbc.criteria.NativeSql

The auto-configuration provides:

@Bean
@ConditionalOnMissingBean(Entityql.class)
public Entityql entityql(Config config) {
    return new Entityql(config);
}

@Bean
@ConditionalOnMissingBean(NativeSql.class)  
public NativeSql nativeSql(Config config) {
    return new NativeSql(config);
}

Usage Examples

Basic Usage with Entity Metamodel

The simplest way to use UnifiedCriteriaPageable is with an entity metamodel:

@RestController
public class MessageController {
    
    private final QueryDsl queryDsl;
    
    @GetMapping("/messages")
    public List<Message> list(@PageableDefault(size = 10, sort = "id,asc") Pageable pageable) {
        var message = new Message_(); // Entity metamodel
        var p = UnifiedCriteriaPageable.from(pageable, message);
        
        return queryDsl.from(message)
                .offset(p.offset())
                .limit(p.limit())
                .orderBy(p.orderBy())
                .fetch();
    }
}

Custom Property Mapping

For more control over property name resolution:

@GetMapping("/messages")
public List<Message> list(Pageable pageable) {
    var message = new Message_();
    
    var p = UnifiedCriteriaPageable.of(pageable, propertyName -> {
        return switch (propertyName) {
            case "messageText" -> Optional.of(message.text);
            case "messageId" -> Optional.of(message.id);
            default -> Optional.empty();
        };
    });
    
    return queryDsl.from(message)
            .offset(p.offset())
            .limit(p.limit())
            .orderBy(p.orderBy())
            .fetch();
}

With Default Ordering

Specify a default ordering when no sort is provided:

@GetMapping("/messages")
public List<Message> list(Pageable pageable) {
    var message = new Message_();
    
    var p = UnifiedCriteriaPageable.from(pageable, message, 
        orderBy -> orderBy.desc(message.id)); // Default ordering
    
    return queryDsl.from(message)
            .offset(p.offset())
            .limit(p.limit())
            .orderBy(p.orderBy())
            .fetch();
}

Handling Missing Properties

Handle cases where sort properties don't exist in the entity:

@GetMapping("/messages")
public List<Message> list(Pageable pageable) {
    var message = new Message_();
    var p = UnifiedCriteriaPageable.from(pageable, message);
    
    return queryDsl.from(message)
            .offset(p.offset())
            .limit(p.limit())
            .orderBy(p.orderBy(missingProperties -> {
                if (!missingProperties.isEmpty()) {
                    throw new IllegalArgumentException(
                        "Invalid sort properties: " + String.join(", ", missingProperties)
                    );
                }
            }))
            .fetch();
}

Complete Pagination Example

For full pagination support with total count:

@GetMapping("/messages")
public Page<Message> getPage(Pageable pageable) {
    var message = new Message_();
    var p = UnifiedCriteriaPageable.from(pageable, message);
    
    // Get paginated content
    var content = queryDsl.from(message)
            .offset(p.offset())
            .limit(p.limit())
            .orderBy(p.orderBy())
            .fetch();
    
    // Get total count
    var total = queryDsl.from(message)
            .select(Expressions.count())
            .fetchOne();
    
    return new PageImpl<>(content, pageable, total);
}

Configuration Options

SortConfig

For advanced configurations, use SortConfig:

var sortConfig = new UnifiedCriteriaPageable.SortConfig(
    propertyName -> { /* custom property resolution */ },
    orderBy -> { /* default ordering */ }
);

var p = UnifiedCriteriaPageable.of(pageable, sortConfig);