SpringDataJpa基礎編3:ページングとソート

15565 ワード

1、SpringDataJpaにおけるページング関連インタフェース及びクラス
1.1、Pageableインタフェース
【Pageableインタフェースソース】
public interface Pageable {
	
	static Pageable unpaged() {
		return Unpaged.INSTANCE;
	}
	default boolean isPaged() {
		return true;
	}
	default boolean isUnpaged() {
		return !isPaged();
	}
	int getPageNumber();
	int getPageSize();
	long getOffset();
	Sort getSort();
	default Sort getSortOr(Sort sort) {

		Assert.notNull(sort, "Fallback Sort must not be null!");

		return getSort().isSorted() ? getSort() : sort;
	}
	
	Pageable next();
	
	Pageable previousOrFirst();
	
	Pageable first();
	
	boolean hasPrevious();
	
	default Optional toOptional() {
		return isUnpaged() ? Optional.empty() : Optional.of(this);
	}
}

1.2.PageRequestクラス
PageRequestクラスはPageableインタフェースの実装クラスです
import org.springframework.data.domain.Sort.Direction;
import org.springframework.lang.Nullable;
/**
 * Basic Java Bean implementation of {@code Pageable}.
 *       PageRequest  Pageable     
 */
public class PageRequest extends AbstractPageRequest {
private static final long serialVersionUID = -4541509938956089562L;
   //    
	private final Sort sort;
//@Deprecated           ,     
	@Deprecated
	public PageRequest(int page, int size) {
		this(page, size, Sort.unsorted());
	}
//@Deprecated           ,     
	@Deprecated
	public PageRequest(int page, int size, Direction direction, String... properties) {
		this(page, size, Sort.by(direction, properties));
	}
//@Deprecated           ,     	
	@Deprecated
	public PageRequest(int page, int size, Sort sort) {
		super(page, size);
		this.sort = sort;
	}

	/**
	 * Creates a new unsorted {@link PageRequest}.
	 *                
	 * @param page zero-based page index    ,  0  .
	 * @param size the size of the page to be returned        。	
	 */
	public static PageRequest of(int page, int size) {
		return of(page, size, Sort.unsorted());
	}

	/**
	 * Creates a new {@link PageRequest} with sort parameters applied.
	 *                  
	 * @param page zero-based page index    ,  0   .
	 * @param size the size of the page to be returned.
	 * @param sort must not be {@literal null}     .
	 * @since 2.0
	 */
	public static PageRequest of(int page, int size, Sort sort) {
		return new PageRequest(page, size, sort);
	}

	
	public static PageRequest of(int page, int size, Direction direction, String... properties) {
		return of(page, size, Sort.by(direction, properties));
	}

	
	public Sort getSort() {
		return sort;
	}
//   
	public Pageable next() {
		return new PageRequest(getPageNumber() + 1, getPageSize(), getSort());
	}
//   
	
	public PageRequest previous() {
		return getPageNumber() == 0 ? this : new PageRequest(getPageNumber() - 1, getPageSize(), getSort());
	}

//     0  
	public Pageable first() {
		return new PageRequest(0, getPageSize(), getSort());
	}

	@Override
	public boolean equals(@Nullable Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof PageRequest)) {
			return false;
		}
		PageRequest that = (PageRequest) obj;
		return super.equals(that) && this.sort.equals(that.sort);
	}

	@Override
	public int hashCode() {
		return 31 * super.hashCode() + sort.hashCode();
	}
	@Override
	public String toString() {
		return String.format("Page request [number: %d, size %d, sort: %s]", getPageNumber(), getPageSize(), sort);
	}
}

1.3.Pageインタフェース
Pageインタフェースは、SpringDataJpaのページング結果の戻り値タイプです.
import java.util.Collections;
import java.util.function.Function;
public interface Page extends Slice {
//      Page  	
	static  Page empty() {
		return empty(Pageable.unpaged());
	}
//      Page  ,   pageable    
	static  Page empty(Pageable pageable) {
		return new PageImpl<>(Collections.emptyList(), pageable, 0);
	}
//     
	int getTotalPages();

//       
	long getTotalElements();
	//
	 Page map(Function super T, ? extends U> converter);
}

 
2、SpringDataJpaで関連インタフェースとクラスを並べ替える
2.1.Sortクラスの内部クラスDirection
実はSortクラスにはクラス部クラスDirectionがあり、Directionは列挙タイプです.
	/**
	 * Enumeration for sort directions.
	 * Sort       	 
	 */
	public static enum Direction {
//ASC    ,DESC    
		ASC, DESC;
		/**
		 * Returns whether the direction is ascending.
		 *       
		 * @return
		 * @since 1.13
		 */
		public boolean isAscending() {
			return this.equals(ASC);
		}

		/**
		 * Returns whether the direction is descending.
		 *       
		 * @return
		 * @since 1.13
		 */
		public boolean isDescending() {
			return this.equals(DESC);
		}
		
		public static Direction fromString(String value) {

			try {
				return Direction.valueOf(value.toUpperCase(Locale.US));
			} catch (Exception e) {
				throw new IllegalArgumentException(String.format(
						"Invalid value '%s' for orders given! Has to be either 'desc' or 'asc' (case insensitive).", value), e);
			}
		}
		
		public static Optional fromOptionalString(String value) {

			try {
				return Optional.of(fromString(value));
			} catch (IllegalArgumentException e) {
				return Optional.empty();
			}
		}
	}

2.2、Orderクラス
OrderクラスはSortクラスの内部クラスです
//sort       
	public static class Order implements Serializable {
		private static final long serialVersionUID = 1522511010900108987L;
		private static final boolean DEFAULT_IGNORE_CASE = false;
		private static final NullHandling DEFAULT_NULL_HANDLING = NullHandling.NATIVE;
		private final Direction direction;
		private final String property;
		private final boolean ignoreCase;
		private final NullHandling nullHandling;
		public Order(@Nullable Direction direction, String property) {
			this(direction, property, DEFAULT_IGNORE_CASE, DEFAULT_NULL_HANDLING);
		}

		public Order(@Nullable Direction direction, String property, NullHandling nullHandlingHint) {
			this(direction, property, DEFAULT_IGNORE_CASE, nullHandlingHint);
		}
		
		@Deprecated
		public Order(String property) {
			this(DEFAULT_DIRECTION, property);
		}

		
		public static Order by(String property) {
			return new Order(DEFAULT_DIRECTION, property);
		}

		public static Order asc(String property) {
			return new Order(Direction.ASC, property, DEFAULT_NULL_HANDLING);
		}

		
		public static Order desc(String property) {
			return new Order(Direction.DESC, property, DEFAULT_NULL_HANDLING);
		}

		private Order(@Nullable Direction direction, String property, boolean ignoreCase, NullHandling nullHandling) {

			if (!StringUtils.hasText(property)) {
				throw new IllegalArgumentException("Property must not null or empty!");
			}

			this.direction = direction == null ? DEFAULT_DIRECTION : direction;
			this.property = property;
			this.ignoreCase = ignoreCase;
			this.nullHandling = nullHandling;
		}

		public Direction getDirection() {
			return direction;
		}

		public String getProperty() {
			return property;
		}

	
		public boolean isAscending() {
			return this.direction.isAscending();
		}

		public boolean isDescending() {
			return this.direction.isDescending();
		}

	
		public boolean isIgnoreCase() {
			return ignoreCase;
		}

		
		public Order with(Direction direction) {
			return new Order(direction, this.property, this.ignoreCase, this.nullHandling);
		}

	
		public Order withProperty(String property) {
			return new Order(this.direction, property, this.ignoreCase, this.nullHandling);
		}

		public Sort withProperties(String... properties) {
			return Sort.by(this.direction, properties);
		}

		public Order ignoreCase() {
			return new Order(direction, property, true, nullHandling);
		}

		public Order with(NullHandling nullHandling) {
			return new Order(direction, this.property, ignoreCase, nullHandling);
		}

		public Order nullsFirst() {
			return with(NullHandling.NULLS_FIRST);
		}

		public Order nullsLast() {
			return with(NullHandling.NULLS_LAST);
		}

	
		public Order nullsNative() {
			return with(NullHandling.NATIVE);
		}

		public NullHandling getNullHandling() {
			return nullHandling;
		}

		@Override
		public int hashCode() {

			int result = 17;

			result = 31 * result + direction.hashCode();
			result = 31 * result + property.hashCode();
			result = 31 * result + (ignoreCase ? 1 : 0);
			result = 31 * result + nullHandling.hashCode();

			return result;
		}

		@Override
		public boolean equals(@Nullable Object obj) {

			if (this == obj) {
				return true;
			}

			if (!(obj instanceof Order)) {
				return false;
			}

			Order that = (Order) obj;

			return this.direction.equals(that.direction) && this.property.equals(that.property)
					&& this.ignoreCase == that.ignoreCase && this.nullHandling.equals(that.nullHandling);
		}

		
		@Override
		public String toString() {

			String result = String.format("%s: %s", property, direction);

			if (!NullHandling.NATIVE.equals(nullHandling)) {
				result += ", " + nullHandling;
			}

			if (ignoreCase) {
				result += ", ignoring case";
			}

			return result;
		}
	}

2.3.Sortソートクラス
【Sortクラスの一部ソース】
public class Sort implements Streamable, Serializable {

	private static final long serialVersionUID = 5737186511678863905L;
//
	private static final Sort UNSORTED = Sort.by(new Order[0]);

	public static final Direction DEFAULT_DIRECTION = Direction.ASC;

	private final List orders;
//@Deprecated           ,       
	@Deprecated
	public Sort(Order... orders) {
		this(Arrays.asList(orders));
	}
//@Deprecated           ,       
	@Deprecated
	public Sort(List orders) {

		Assert.notNull(orders, "Orders must not be null!");

		this.orders = Collections.unmodifiableList(orders);
	}
//@Deprecated           ,       
	@Deprecated
	public Sort(String... properties) {
		this(DEFAULT_DIRECTION, properties);
	}

	
	public Sort(Direction direction, String... properties) {
		this(direction, properties == null ? new ArrayList<>() : Arrays.asList(properties));
	}

	
	public Sort(Direction direction, List properties) {

		if (properties == null || properties.isEmpty()) {
			throw new IllegalArgumentException("You have to provide at least one property to sort by!");
		}

		this.orders = new ArrayList<>(properties.size());

		for (String property : properties) {
			this.orders.add(new Order(direction, property));
		}
	}

	
	public static Sort by(String... properties) {

		Assert.notNull(properties, "Properties must not be null!");

		return properties.length == 0 ? Sort.unsorted() : new Sort(properties);
	}

	public static Sort by(List orders) {

		Assert.notNull(orders, "Orders must not be null!");

		return orders.isEmpty() ? Sort.unsorted() : new Sort(orders);
	}

	
	public static Sort by(Order... orders) {

		Assert.notNull(orders, "Orders must not be null!");

		return new Sort(orders);
	}

	
	public static Sort by(Direction direction, String... properties) {

		Assert.notNull(direction, "Direction must not be null!");
		Assert.notNull(properties, "Properties must not be null!");
		Assert.isTrue(properties.length > 0, "At least one property must be given!");

		return Sort.by(Arrays.stream(properties)//
				.map(it -> new Order(direction, it))//
				.collect(Collectors.toList()));
	}

	public static Sort unsorted() {
		return UNSORTED;
	}


	public Sort descending() {
		return withDirection(Direction.DESC);
	}

	
	public Sort ascending() {
		return withDirection(Direction.ASC);
	}

	public boolean isSorted() {
		return !orders.isEmpty();
	}

	public boolean isUnsorted() {
		return !isSorted();
	}
//.............................            ...................................
}

3、応用例
3.1、実体類
@Entity
@Table(name="tb_label")
public class Label implements Serializable {
    @Id
    private String id;//
    private String labelname;//    
    private String state;//  
    private Long count;//    
    private Long fans;//   
    private String recommend;//    
    public Label() {
    }
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getLabelname() {
        return labelname;
    }
    public void setLabelname(String labelname) {
        this.labelname = labelname;
    }
    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public Long getCount() {
        return count;
    }

    public void setCount(Long count) {
        this.count = count;
    }
    public Long getFans() {
        return fans;
    }
    public void setFans(Long fans) {
        this.fans = fans;
    }
    public String getRecommend() {
        return recommend;
    }
    public void setRecommend(String recommend) {
        this.recommend = recommend;
    }
    @Override
    public String toString() {
        return "Label{" +
                "id='" + id + '\'' +
                ", labelname='" + labelname + '\'' +
                ", state='" + state + '\'' +
                ", count=" + count +
                ", fans=" + fans +
                ", recommend='" + recommend + '\'' +
                '}';
    }
}

3.2、DAOインタフェース
/**
 * JpaRepository        
 * JpaSpecificationExecutor      
 */
public interface LabelDao extends JpaRepository

3.3、テストコード
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BaseApplication.class)
public class JpaTest {
    @Autowired
    private LabelDao labelDao;
    @Test
    public void testPage(){
        int pageNo=2;//   ,   springdatajpa      0   
        int size=5;//  5 
        //PageRequest  Pageable      
        PageRequest pageRequest =  PageRequest.of(pageNo-1, size);
        Page