# 1. Introduction MyPages is a java based, open source pagination plugin for [MyBatis](https://github.com/mybatis/mybatis-3) that simplifies database paging queries. # 2. Getting Started Please make sure the Java version is 1.8 or above. # 3. Maven Dependency ```xml io.leego mypages ${mypages.version} ``` # 4. Quick Start Assume there are schema and table based on `MySQL`: ```sql create database mypages; use mypages; create table user ( id bigint primary key auto_increment, username varchar(20), password varchar(40), nickname varchar(20), status tinyint default 1 ); ``` Assume there is a mapper defined like the following: ```java public interface UserMapper { @Select("select * from user") List query(@Param("page") Integer page, @Param("size") Integer size); } ``` ## 4.1. Java ```java public void query() { DataSource dataSource = dataSource(); SqlSessionFactory sqlSessionFactory = sqlSessionFactory(dataSource); try (SqlSession session = sqlSessionFactory.openSession(false)) { UserMapper userMapper = session.getMapper(UserMapper.class); List list = userMapper.query(1, 10); Page page = Page.of(list); } } public DataSource dataSource() { HikariConfig config = new HikariConfig(); config.setDriverClassName("com.mysql.cj.jdbc.Driver"); config.setJdbcUrl("jdbc:mysql://localhost:3306/mypages?serverTimezone=GMT%2B8"); config.setUsername("root"); config.setPassword("123456"); return new HikariDataSource(config); } public SqlSessionFactory sqlSessionFactory(DataSource dataSource) { TransactionFactory transactionFactory = new JdbcTransactionFactory(); Environment environment = new Environment(SqlSessionFactory.class.getSimpleName(), transactionFactory, dataSource); Configuration configuration = new Configuration(environment); configuration.setMapUnderscoreToCamelCase(true); configuration.addMapper(UserMapper.class); configuration.addInterceptor(paginationInterceptor(dataSource)); return new SqlSessionFactoryBuilder().build(configuration); } public PaginationInterceptor paginationInterceptor(DataSource dataSource) { PaginationSettings settings = PaginationSettings.builder() .database(Database.fromDataSource(dataSource)) .pageField("page") .sizeField("size") .build(); return new PaginationInterceptor(settings); } ``` ## 4.2. Spring ```xml ``` ```java @Service public class UserService { @Autowired private UserMapper userMapper; public Page query(int page, int size) { List list = userMapper.query(page, size); return Page.of(list); } } ``` ## 4.3. Spring Boot ```java @Configuration public class MybatisConfiguration { @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { Interceptor[] plugins = new Interceptor[]{paginationInterceptor(dataSource)}; SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSource); sqlSessionFactoryBean.setPlugins(plugins); return sqlSessionFactoryBean.getObject(); } @Bean public PaginationInterceptor paginationInterceptor(DataSource dataSource) { PaginationSettings settings = PaginationSettings.builder() .database(Database.fromDataSource(dataSource)) .pageField("page") .sizeField("size") .build(); return new PaginationInterceptor(settings); } } ``` ```java @Service public class UserService { @Autowired private UserMapper userMapper; public Page query(int page, int size) { List list = userMapper.query(page, size); return Page.of(list); } } ``` ## 4.4. Spring Boot Starter > * Please see: [mypages-spring-boot-starter](../mypages-spring-boot-starter/README.md) # 5. Enable Pagination ## 5.1. Annotations (Recommended) Using `@Pagination`, `@Page`, `@Size`, `@Offset`, `@Rows` ```java @Pagination public class QueryParam { @Page private Integer page; @Size private Integer size; @Offset private Integer offset; @Rows private Integer rows; /* getter setter */ } ``` More annotations: `@CountExpr`, `@CountMethodName`, `@DisableCount`, `@DisablePagination` ## 5.2. PaginationSettings (Recommended) Obtaining paging parameters by reflection. ```java PaginationSettings settings = PaginationSettings.builder() .database(Database.MYSQL) .pageField("page") .sizeField("size") .build(); PaginationInterceptor interceptor = new PaginationInterceptor(settings); ``` ### 5.2.1. Custom class Assume there are classes defined like the following: ```java public class QueryParam { private Integer page; private Integer size; public QueryParam(Integer page, Integer size) { this.page = page; this.size = size; } /* getter setter */ } ``` ```java public interface UserMapper { @Select("SELECT * FROM user") List query(QueryParam param); } ``` ### 5.2.2. MyBatis `@Param` Assume there is a mapper defined like the following: ```java public interface UserMapper { @Select("SELECT * FROM user") List query(@Param("page") Integer page, @Param("size") Integer size); } ``` ### 5.2.3. `Map` Assume there is a mapper defined like the following: ```java public interface UserMapper { @Select("SELECT * FROM user") List query(Map map); } ``` Call the query method: ```java @Service public class UserService { @Autowired private UserMapper userMapper; public Page query() { Map map = new HashMap<>(); map.put("size", 1); map.put("page", 10); return Page.of(userMapper.query(map)); } } ``` ### Configurations ```java PaginationSettings settings = PaginationSettings.builder() .database(Database.MYSQL) // The database type such as `MySQL`, `PostgreSQL`, `Oracle` .countExpr("*") // The column name or expression .pageField("page") // The `page` field name of the parameter .sizeField("size") // The `size` field name of the parameter .offsetField("offset") // The `offset` field name of the parameter .rowsField("rows") // The `rows` field name of the parameter .countExprField("countExpr") // The count expression field name of the parameter .countMethodNameField("countMethodName") // The `count-method-name` field name of the parameter .enableCountField("enableCount") // The `enable-count` field name of the parameter .skipQueryIfCountEqualsZero(true) // Whether to skip query if total value equals zero .useGeneratedIfCountMethodIsMissing(true) // Whether to use generated if the specified count method is missing .useGracefulCountSql(true) // Whether to use graceful count-sql .keepSorting(false) // Whether to keep sorting .defaultPage(1) // Replaces the `page` with `default-page` if the `page` is `null` or less than `1` .defaultSize(10) // Replaces the `size` with `default-size` if the `size` is `null` or less than `1` .maxPage(10000) // Replaces the `page` with `max-page` if the `page` is greater than `max-page` .maxSize(10000) // Replaces the `size` with `max-size` if the `size` is greater than `max-size` .build(); ``` ## 5.3. Implements `io.leego.mypages.util.Pageable` Define a class implements `io.leego.mypages.util.Pageable`. ```java public class QueryParam implements io.leego.mypages.util.Pageable { private Integer page; private Integer size; private Integer offset; private Integer rows; @Override public Integer getPage() { return page; } public void setPage(Integer page) { this.page = page; } @Override public Integer getSize() { return size; } public void setSize(Integer size) { this.size = size; } @Override public Integer getOffset() { return offset; } public void setOffset(Integer offset) { this.offset = offset; } @Override public Integer getRows() { return rows; } public void setRows(Integer rows) { this.rows = rows; } } ``` # 6. Using Graceful Count SQL Please see:`io.leego.mypages.util.SqlUtils#toCountSql` ## 6.1. Simple Input: ```sql select * from user where status = ? ``` Output: ```sql select count(*) from user where status = ? ``` ## 6.2. Sorting Input: ```sql select * from user where status = ? order by username desc ``` Output: ```sql select count(*) from user where status = ? ``` ## 6.3. Keep Soring Input: ```sql select * from user where status = ? order by username desc ``` Output: ```sql select count(*) from user where status = ? order by username desc ``` ## 6.4. Aggregation Input: ```sql select nickname from user where status = ? group by nickname ``` Output: ```sql select count(*) from (select nickname from user where status = ? group by nickname) mp_cta ``` ## 6.5. With-As Input: ```sql with temp as (select * from user where status = ?) select * from temp ``` Output: ```sql with temp as (select * from user where status = ?) select count(*) from temp ``` ## 6.6. Sub-Query Input: ```sql select * from (select * from user) u where u.status = ? ``` Output: ```sql select count(*) from (select * from user) u where u.status = ? ``` # 7. Using custom count methods ## 7.1. Annotations ```java @Pagination public class QueryParam { @Page private Integer page; @Size private Integer size; @CountMethodName private String countMethodName; public QueryParam(Integer page, Integer size, String countMethodName) { this.page = page; this.size = size; this.countMethodName = countMethodName; } /* getter setter */ } ``` ## 7.2. Configurations ```java public class QueryParam { private Integer page; private Integer size; private String countMethodName; public QueryParam(Integer page, Integer size, String countMethodName) { this.page = page; this.size = size; this.countMethodName = countMethodName; } /* getter setter */ } ``` ```java PaginationSettings settings = PaginationSettings.builder() .database(Database.MYSQL) .pageField("page") .sizeField("size") .countMethodNameField("countMethodName") .build(); PaginationInterceptor interceptor = new PaginationInterceptor(settings); ``` ## 7.3. Call `Query` and `Count` Methods Assume there is a mapper defined like the following: ```java public interface UserMapper { @Select("SELECT * FROM user") List query(QueryParam param); @Select("SELECT COUNT(*) FROM user") long count(QueryParam param); } ``` Specify the count method name: ```java import io.leego.mypages.util.Page; @Service public class UserService { @Autowired private UserMapper userMapper; public Page query() { // Specifies the count method name. return Page.of(userMapper.query(new QueryParam(1, 10, "count"))); } } ``` # 8. Parameters rationalization Rationalize the parameters if the values are invalid, the following parameters can be set: **defaultPage**: Replaces the page with default-page if the page is null or less than 1. **defaultSize**: Replaces the size with default-size if the size is null or less than 1. **maxPage**: Replaces the page with max-page if the page is greater than max-page. **maxSize**: Replaces the size with max-size if the size is greater than max-size. ## 8.1. Annotations ```java @Pagination(defaultPage = 1, defaultSize = 10, maxPage = 10000, maxSize = 10000) public class QueryParam { } ``` ## 8.2. Configurations ```java PaginationSettings settings = PaginationSettings.builder() .database(Database.MYSQL) .defaultPage(1) .defaultSize(10) .maxPage(10000) .maxSize(10000) .build(); PaginationInterceptor interceptor = new PaginationInterceptor(settings); ``` # 9. Query Results ## 9.1. Using `io.leego.mypages.util.Page` #### 9.1.1. Wrap ```java Page page = Page.of(mapper.query(param)); ``` #### 9.1.2. Convert ```java Page page = Page.of(mapper.query(param), foo -> new Bar(foo)); ``` #### 9.1.3. Map ```java Page page = Page.of(mapper.query(param)).map(foo -> new Bar(foo)); ``` ## 9.2. Using `io.leego.mypages.util.PaginationCollection` ```java List list = mapper.query(param); PaginationCollection pc = (PaginationCollection) list; long total = pc.getTotal(); int page = pc.getPage(); int size = pc.getSize(); ``` # 10. Coding Specification ## 10.1. Using `org.apache.ibatis.annotations.Param` Assume there are configurations defined like the following: ```java PaginationInterceptor interceptor = new PaginationInterceptor(PaginationSettings.builder() .database(Database.MYSQL) .pageField("page") .sizeField("size") .sizeField("size") .offsetField("offset") .build()); ``` ```java public interface UserMapper { @Select("select * from user where status = #{status}") List findByStatusWithPageSizeParam(@Param("page") Integer page, @Param("size") Integer size, @Param("status") Integer status); @Select("select * from user where status = #{status}") List findByStatusOffsetRowsWithParam(@Param("offset") Long offset, @Param("rows") Integer rows, @Param("status") Integer status); } ``` ## 10.2. Using `@Pagination` ### 10.2.1. Field declaration ```java @Pagination public class PaginationFieldParam { @Page private Integer page; @Size private Integer size; @Offset private Long offset; @Rows private Integer rows; private Integer status; } public interface UserMapper { @Select("select * from user where status = #{status}") List findByPaginationField(PaginationFieldParam param); @Select("select * from user where status = #{param.status}") List findByPaginationFieldWithParam0(@Param("param") PaginationFieldParam param); @Select("select * from user where status = #{param1.status}") List findByPaginationFieldWithParam1(@Param("param1") PaginationFieldParam param); } ``` ### 10.2.2. Method declaration ```java @Pagination public class PaginationMethodParam { private Integer page; private Integer size; private Long offset; private Integer rows; private Integer status; @Page public Integer getPage() { return page; } public void setPage(Integer page) { this.page = page; } @Size public Integer getSize() { return size; } public void setSize(Integer size) { this.size = size; } @Offset public Long getOffset() { return offset; } public void setOffset(Long offset) { this.offset = offset; } @Rows public Integer getRows() { return rows; } public void setRows(Integer rows) { this.rows = rows; } public Integer getStatus() { return status; } public void setStatus(Integer status) { this.status = status; } } public interface UserMapper { @Select("select * from user where status = #{status}") List findByPaginationMethod(PaginationMethodParam param); @Select("select * from user where status = #{param.status}") List findByPaginationMethodWithParam0(@Param("param") PaginationMethodParam param); @Select("select * from user where status = #{param1.status}") List findByPaginationMethodWithParam1(@Param("param1") PaginationMethodParam param); } ``` ## 10.3. Using `io.leego.mypages.util.Pageable` ```java public class PageableParam implements Pageable { private Integer page; private Integer size; private Long offset; private Integer rows; private Integer status; } public interface UserMapper { @Select("select * from user where status = #{status}") List findByPageable(PageableParam param); @Select("select * from user where status = #{param.status}") List findByPageableWithParam0(@Param("param") PageableParam param); @Select("select * from user where status = #{param1.status}") List findByPageableWithParam1(@Param("param1") PageableParam param); } ``` ## 10.4. Using Custom Class Assume there are configurations defined like the following: ```java PaginationInterceptor interceptor = new PaginationInterceptor(PaginationSettings.builder() .database(Database.MYSQL) .pageField("page") .sizeField("size") .sizeField("size") .offsetField("offset") .build()); ``` ```java public class CustomClassParam { private Integer page; private Integer size; private Long offset; private Integer rows; private Integer status; } public interface UserMapper { @Select("select * from user where status = #{status}") List findByCustomClass(CustomClassParam param); @Select("select * from user where status = #{param.status}") List findByCustomClassWithParam0(@Param("param") CustomClassParam param); @Select("select * from user where status = #{param1.status}") List findByCustomClassWithParam1(@Param("param1") CustomClassParam param); } ``` ## 10.5. Using `Map` Assume there are configurations defined like the following: ```java PaginationInterceptor interceptor = new PaginationInterceptor(PaginationSettings.builder() .database(Database.MYSQL) .pageField("page") .sizeField("size") .sizeField("size") .offsetField("offset") .build()); ``` ## 10.5.1. Using `java.util.Map` ```java public interface UserMapper { @Select("select * from user where status = #{status}") List findByCollectionMap(Map param); @Select("select * from user where status = #{map.status}") List findByCollectionMapWithParam0(@Param("map") Map map); @Select("select * from user where status = #{param1.status}") List findByCollectionMapWithParam1(@Param("param1") Map map); } ``` ## 10.5.2. Using Custom `Map` ```java public class CustomMapParam extends HashMap { } public interface UserMapper { @Select("select * from user where status = #{status}") List findByCustomMap(CustomMapParam param); @Select("select * from user where status = #{param.status}") List findByCustomMapWithParam0(@Param("param") CustomMapParam param); @Select("select * from user where status = #{param1.status}") List findByCustomMapWithParam1(@Param("param1") CustomMapParam param); } ```