Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package org.seasar.doma.boot;

import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.seasar.doma.jdbc.criteria.declaration.OrderByNameDeclaration;
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
import org.seasar.doma.jdbc.criteria.statement.EntityQueryable;
import org.springframework.data.domain.Pageable;

/**
* Converts Utilities for {@link Pageable} to be used with Doma Criteria.
*
* @author mazeneko
*/
public class PageablesForCriteria {
/**
* Converts {@link Pageable} to {@link EntityQueryable#limit(Integer)}
*
* @param pageable {@link Pageable} object to convert
* @return the limit.
* if {@link Pageable#isUnpaged()} is {@code true} then null.
*/
public static Integer limit(Pageable pageable) {
return pageable.isUnpaged() ? null : pageable.getPageSize();
}

/**
* Converts {@link Pageable} to {@link EntityQueryable#offset(Integer)}
*
* @param pageable {@link Pageable} object to convert
* @return the offset.
* if {@link Pageable#isUnpaged()} is {@code true} then null.
*/
public static Integer offset(Pageable pageable) {
return pageable.isUnpaged() ? null : Math.multiplyExact(pageable.getPageNumber(), pageable.getPageSize());
}

/**
* Creates an {@link OrderByNameDeclaration} consumer for a single entity based
* on the {@link Pageable}'s sort information.
* <p>
* This method resolves property names for ordering within the specified entity
* using its metamodel, and generates
* a consumer that can be used to apply ascending/descending orders.
* <p>
* If the {@link Pageable} is unsorted, no ordering is applied.
*
* @param pageable the {@link Pageable} containing sorting information
* @param entityMetamodel the {@link EntityMetamodel} corresponding to the
* target entity
* @return a consumer that configures ordering on the target entity
*/
public static Consumer<OrderByNameDeclaration> orderBySingleEntity(
Pageable pageable,
EntityMetamodel<?> entityMetamodel) {
return orderBySingleEntity(pageable, entityMetamodel, c -> {
});
}

/**
* Creates an {@link OrderByNameDeclaration} consumer for a single entity based
* on the {@link Pageable}'s sort information.
* <p>
* This method resolves property names for ordering within the specified entity
* using its metamodel, and generates
* a consumer that can be used to apply ascending/descending orders.
* <p>
* a default ordering via {@code defaultOrder} if the given {@link Pageable} is
* unsorted.
*
* @param pageable the {@link Pageable} containing sorting information
* @param entityMetamodel the {@link EntityMetamodel} corresponding to the
* target entity
* @param defaultOrder a consumer that applies default ordering if the
* {@link Pageable} is unsorted
* @return a consumer that configures ordering on the target entity
*/
public static Consumer<OrderByNameDeclaration> orderBySingleEntity(
Pageable pageable,
EntityMetamodel<?> entityMetamodel,
Consumer<OrderByNameDeclaration> defaultOrder) {
final var nameToMetamodel = entityMetamodel
.allPropertyMetamodels()
.stream()
.collect(Collectors.toMap(PropertyMetamodel::getName, Function.identity()));
return orderBy(
pageable,
propertyName -> Optional.ofNullable(nameToMetamodel.get(propertyName)),
defaultOrder);
}

/**
* Creates an {@link OrderByNameDeclaration} consumer based on the
* {@link Pageable}'s sort information
* using the provided {@link PropertyMetamodelResolver}.
* <p>
* If the {@link Pageable} is unsorted, no ordering is applied.
*
* @param pageable the {@link Pageable} containing sorting
* information
* @param propertyMetamodelResolver a resolver that maps property names to
* {@link PropertyMetamodel}
* @return a consumer that configures ordering based on the resolved
* {@link PropertyMetamodel} instances
*/
public static Consumer<OrderByNameDeclaration> orderBy(
Pageable pageable,
PropertyMetamodelResolver propertyMetamodelResolver) {
return orderBy(pageable, propertyMetamodelResolver, c -> {
});
}

/**
* Creates an {@link OrderByNameDeclaration} consumer based on the
* {@link Pageable}'s sort information
* using the provided {@link PropertyMetamodelResolver}.
* <p>
* a default ordering via {@code defaultOrder} if the given {@link Pageable} is
* unsorted.
*
* @param pageable the {@link Pageable} containing sorting
* information
* @param propertyMetamodelResolver a resolver that maps property names to
* {@link PropertyMetamodel}
* @param defaultOrder a consumer that applies default ordering if
* the {@link Pageable} is unsorted
* @return a consumer that configures ordering based on the resolved
* {@link PropertyMetamodel} instances
*/
public static Consumer<OrderByNameDeclaration> orderBy(
Pageable pageable,
PropertyMetamodelResolver propertyMetamodelResolver,
Consumer<OrderByNameDeclaration> defaultOrder) {
if (pageable.getSort().isUnsorted()) {
return defaultOrder;
}
final var orderSpecifiers = pageable
.getSort()
.flatMap(order -> propertyMetamodelResolver
.resolve(order.getProperty())
.<Consumer<OrderByNameDeclaration>>map(propertyMetamodel -> switch (order.getDirection()) {
case ASC -> c -> c.asc(propertyMetamodel);
case DESC -> c -> c.desc(propertyMetamodel);
})
.stream())
.toList();
return c -> orderSpecifiers.forEach(orderSpecifier -> orderSpecifier.accept(c));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.seasar.doma.boot;

import java.util.Optional;

import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;

/**
* A resolver that maps property names to {@link PropertyMetamodel}
*/
@FunctionalInterface
public interface PropertyMetamodelResolver {
/**
* Resolves the specified property name into a {@link PropertyMetamodel}.
*
* @param propertyName the name of the property to resolve
* @return an {@link Optional} containing the resolved {@link PropertyMetamodel}
* if found,
* or an empty {@link Optional} if the property name cannot be resolved
*/
Optional<PropertyMetamodel<?>> resolve(String propertyName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.seasar.doma.boot;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;

import org.junit.jupiter.api.Test;
import org.seasar.doma.jdbc.criteria.declaration.OrderByNameDeclaration;
import org.seasar.doma.jdbc.criteria.metamodel.EntityMetamodel;
import org.seasar.doma.jdbc.criteria.metamodel.PropertyMetamodel;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

public class PageablesForCriteriaTest {
@Test
public void testOffsetAndLimit() {
Pageable pageable = PageRequest.of(0, 10);
Integer offset = PageablesForCriteria.offset(pageable);
Integer limit = PageablesForCriteria.limit(pageable);
assertThat(offset, is(0));
assertThat(limit, is(10));
}

@Test
public void testOffsetAndLimit2() {
Pageable pageable = PageRequest.of(2, 10);
Integer offset = PageablesForCriteria.offset(pageable);
Integer limit = PageablesForCriteria.limit(pageable);
assertThat(offset, is(20));
assertThat(limit, is(10));
}

@Test
public void testOffsetAndLimit3() {
Pageable pageable = PageRequest.of(2, 5);
Integer offset = PageablesForCriteria.offset(pageable);
Integer limit = PageablesForCriteria.limit(pageable);
assertThat(offset, is(10));
assertThat(limit, is(5));
}

@Test
public void testOffsetAndLimit4() {
Pageable pageable = Pageable.unpaged();
Integer offset = PageablesForCriteria.offset(pageable);
Integer limit = PageablesForCriteria.limit(pageable);
assertThat(offset, nullValue());
assertThat(limit, nullValue());
}

@Test
public void testOrderBy() {
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").ascending());

PropertyMetamodel<?> nameProp = mock(PropertyMetamodel.class);

Consumer<OrderByNameDeclaration> consumer = PageablesForCriteria.orderBy(
pageable,
propertyName -> switch (propertyName) {
case "name" -> Optional.of(nameProp);
default -> Optional.empty();
});
OrderByNameDeclaration orderByNameDeclaration = mock(OrderByNameDeclaration.class);
consumer.accept(orderByNameDeclaration);
verify(orderByNameDeclaration, times(1)).asc(nameProp);
}

@Test
public void testOrderBy2() {
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").descending().and(Sort.by("age").ascending()));

PropertyMetamodel<?> nameProp = mock(PropertyMetamodel.class);
PropertyMetamodel<?> ageProp = mock(PropertyMetamodel.class);

Consumer<OrderByNameDeclaration> consumer = PageablesForCriteria.orderBy(
pageable,
propertyName -> switch (propertyName) {
case "name" -> Optional.of(nameProp);
case "age" -> Optional.of(ageProp);
default -> Optional.empty();
});
OrderByNameDeclaration orderByNameDeclaration = mock(OrderByNameDeclaration.class);
consumer.accept(orderByNameDeclaration);
verify(orderByNameDeclaration, times(1)).desc(nameProp);
verify(orderByNameDeclaration, times(1)).asc(ageProp);
}

@Test
public void testOrderByWhenNonSort() {
Pageable pageable = PageRequest.of(0, 10);

Consumer<OrderByNameDeclaration> consumer = PageablesForCriteria.orderBy(
pageable,
propertyName -> Optional.empty());
OrderByNameDeclaration orderByNameDeclaration = mock(OrderByNameDeclaration.class);
consumer.accept(orderByNameDeclaration);
verifyNoMoreInteractions(orderByNameDeclaration);
}

@Test
public void testOrderByWhenNonSortAndSetDefault() {
Pageable pageable = PageRequest.of(0, 10);

PropertyMetamodel<?> idProp = mock(PropertyMetamodel.class);

Consumer<OrderByNameDeclaration> consumer = PageablesForCriteria.orderBy(
pageable,
propertyName -> Optional.empty(),
t -> t.asc(idProp));
OrderByNameDeclaration orderByNameDeclaration = mock(OrderByNameDeclaration.class);
consumer.accept(orderByNameDeclaration);
verify(orderByNameDeclaration, times(1)).asc(idProp);
}

@Test
public void testOrderBySingleEntity() {
Pageable pageable = PageRequest.of(0, 10, Sort.by("name").descending().and(Sort.by("age").ascending()));

PropertyMetamodel<?> nameProp = mock(PropertyMetamodel.class);
when(nameProp.getName()).thenReturn("name");
PropertyMetamodel<?> ageProp = mock(PropertyMetamodel.class);
when(ageProp.getName()).thenReturn("age");
EntityMetamodel<?> entity = mock(EntityMetamodel.class);
when(entity.allPropertyMetamodels()).thenReturn(List.of(nameProp, ageProp));

Consumer<OrderByNameDeclaration> consumer = PageablesForCriteria.orderBySingleEntity(pageable, entity);
OrderByNameDeclaration orderByNameDeclaration = mock(OrderByNameDeclaration.class);
consumer.accept(orderByNameDeclaration);
verify(orderByNameDeclaration, times(1)).desc(nameProp);
verify(orderByNameDeclaration, times(1)).asc(ageProp);
}
}