View Javadoc
1   /*
2    *    Copyright 2016-2025 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       https://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package examples.paging;
17  
18  import java.util.HashMap;
19  import java.util.Map;
20  import java.util.Objects;
21  import java.util.function.Function;
22  
23  import org.mybatis.dynamic.sql.render.RenderingStrategies;
24  import org.mybatis.dynamic.sql.select.SelectModel;
25  import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
26  
27  /**
28   * This adapter modifies the generated SQL by adding a LIMIT and OFFSET clause at the end
29   * of the generated SQL.  This can be used to create a paginated query.
30   *
31   * <p>LIMIT and OFFSET has limited support in relational databases, so this cannot be considered
32   * a general solution for all paginated queries (and that is why this adapter lives only in the
33   * test source tree and is not packaged with the core library code).
34   *
35   * <p>I believe it works in MySQL, HSQLDB, and Postgres.
36   *
37   * <p><b>Important Note: </b> this adapter is no longer required for limit and offset support as the
38   * library now supports limit and offset natively. However, this remains a good example of altering the generated
39   * SQL before it is executed.
40   *
41   * @author Jeff Butler
42   */
43  public class LimitAndOffsetAdapter<R> {
44      private final SelectModel selectModel;
45      private final Function<SelectStatementProvider, R> mapperMethod;
46      private final int limit;
47      private final int offset;
48  
49      private LimitAndOffsetAdapter(SelectModel selectModel, Function<SelectStatementProvider, R> mapperMethod,
50              int limit, int offset) {
51          this.selectModel = Objects.requireNonNull(selectModel);
52          this.mapperMethod = Objects.requireNonNull(mapperMethod);
53          this.limit = limit;
54          this.offset = offset;
55      }
56  
57      public R execute() {
58          return mapperMethod.apply(selectStatement());
59      }
60  
61      private SelectStatementProvider selectStatement() {
62          return new LimitAndOffsetDecorator(
63                  selectModel.render(RenderingStrategies.MYBATIS3));
64      }
65  
66      public static <R> LimitAndOffsetAdapter<R> of(SelectModel selectModel,
67              Function<SelectStatementProvider, R> mapperMethod, int limit, int offset) {
68          return new LimitAndOffsetAdapter<>(selectModel, mapperMethod, limit, offset);
69      }
70  
71      public class LimitAndOffsetDecorator implements SelectStatementProvider {
72          private final Map<String, Object> parameters = new HashMap<>();
73          private final String selectStatement;
74  
75          public LimitAndOffsetDecorator(SelectStatementProvider delegate) {
76              parameters.putAll(delegate.getParameters());
77              parameters.put("limit", limit);
78              parameters.put("offset", offset);
79  
80              selectStatement = delegate.getSelectStatement() +
81                      " LIMIT #{parameters.limit} OFFSET #{parameters.offset}";
82          }
83  
84          @Override
85          public Map<String, Object> getParameters() {
86              return parameters;
87          }
88  
89          @Override
90          public String getSelectStatement() {
91              return selectStatement;
92          }
93      }
94  }