View Javadoc
1   /*
2    *    Copyright 2016-2026 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.animal.data;
17  
18  import static examples.animal.data.AnimalDataDynamicSqlSupport.*;
19  import static org.assertj.core.api.Assertions.assertThat;
20  import static org.junit.jupiter.api.Assertions.assertAll;
21  import static org.mybatis.dynamic.sql.SqlBuilder.*;
22  
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.sql.Connection;
26  import java.sql.DriverManager;
27  import java.util.List;
28  
29  import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
30  import org.apache.ibatis.jdbc.ScriptRunner;
31  import org.apache.ibatis.mapping.Environment;
32  import org.apache.ibatis.session.Configuration;
33  import org.apache.ibatis.session.SqlSession;
34  import org.apache.ibatis.session.SqlSessionFactory;
35  import org.apache.ibatis.session.SqlSessionFactoryBuilder;
36  import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;
37  import org.junit.jupiter.api.BeforeEach;
38  import org.junit.jupiter.api.Test;
39  import org.mybatis.dynamic.sql.render.RenderingStrategies;
40  import org.mybatis.dynamic.sql.select.render.SelectStatementProvider;
41  
42  class LimitAndOffsetTest {
43  
44      private static final String JDBC_URL = "jdbc:hsqldb:mem:aname";
45      private static final String JDBC_DRIVER = "org.hsqldb.jdbcDriver";
46  
47      private SqlSessionFactory sqlSessionFactory;
48  
49      @BeforeEach
50      void setup() throws Exception {
51          Class.forName(JDBC_DRIVER);
52          try (InputStream is = getClass().getResourceAsStream("/examples/animal/data/CreateAnimalData.sql")) {
53              assert is != null;
54              try (Connection connection = DriverManager.getConnection(JDBC_URL, "sa", "");
55                  InputStreamReader isr = new InputStreamReader(is)) {
56                  ScriptRunner sr = new ScriptRunner(connection);
57                  sr.setLogWriter(null);
58                  sr.runScript(isr);
59              }
60          }
61  
62          UnpooledDataSource ds = new UnpooledDataSource(JDBC_DRIVER, JDBC_URL, "sa", "");
63          Environment environment = new Environment("test", new JdbcTransactionFactory(), ds);
64          Configuration config = new Configuration(environment);
65          config.addMapper(AnimalDataMapper.class);
66          sqlSessionFactory = new SqlSessionFactoryBuilder().build(config);
67      }
68  
69      @Test
70      void testLimitAndOffsetAfterFrom() {
71          try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
72              SelectStatementProvider selectStatement = select(animalData.allColumns())
73                      .from(animalData)
74                      .limit(3)
75                      .offset(22)
76                      .build()
77                      .render(RenderingStrategies.MYBATIS3);
78  
79              AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
80              List<AnimalData> records = mapper.selectMany(selectStatement);
81  
82              assertAll(
83                      () -> assertThat(records).hasSize(3),
84                      () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(23),
85                      () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData limit #{parameters.p1} offset #{parameters.p2}"),
86                      () -> assertThat(selectStatement.getParameters()).containsEntry("p1", 3L),
87                      () -> assertThat(selectStatement.getParameters()).containsEntry("p2", 22L)
88              );
89          }
90      }
91  
92      @Test
93      void testLimitOnlyAfterFrom() {
94          try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
95              SelectStatementProvider selectStatement = select(animalData.allColumns())
96                      .from(animalData)
97                      .limit(3)
98                      .build()
99                      .render(RenderingStrategies.MYBATIS3);
100 
101             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
102             List<AnimalData> records = mapper.selectMany(selectStatement);
103 
104             assertAll(
105                     () -> assertThat(records).hasSize(3),
106                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(1),
107                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData limit #{parameters.p1}"),
108                     () -> assertThat(selectStatement.getParameters()).containsEntry("p1", 3L)
109             );
110         }
111     }
112 
113     @Test
114     void testOffsetOnlyAfterFrom() {
115         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
116             SelectStatementProvider selectStatement = select(animalData.allColumns())
117                     .from(animalData)
118                     .offset(22)
119                     .build()
120                     .render(RenderingStrategies.MYBATIS3);
121 
122             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
123             List<AnimalData> records = mapper.selectMany(selectStatement);
124 
125             assertAll(
126                     () -> assertThat(records).hasSize(43),
127                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(23),
128                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData offset #{parameters.p1} rows"),
129                     () -> assertThat(selectStatement.getParameters()).containsEntry("p1", 22L)
130             );
131         }
132     }
133 
134     @Test
135     void testLimitAndOffsetAfterWhere() {
136         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
137             SelectStatementProvider selectStatement = select(animalData.allColumns())
138                     .from(animalData)
139                     .where(id, isLessThan(50))
140                     .and(id, isGreaterThan(22))
141                     .limit(3)
142                     .offset(22)
143                     .build()
144                     .render(RenderingStrategies.MYBATIS3);
145 
146             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
147             List<AnimalData> records = mapper.selectMany(selectStatement);
148 
149             assertAll(
150                     () -> assertThat(records).hasSize(3),
151                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(45),
152                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData where id < #{parameters.p1,jdbcType=INTEGER} and id > #{parameters.p2,jdbcType=INTEGER} limit #{parameters.p3} offset #{parameters.p4}"),
153                     () -> assertThat(selectStatement.getParameters()).containsEntry("p3", 3L),
154                     () -> assertThat(selectStatement.getParameters()).containsEntry("p4", 22L)
155             );
156         }
157     }
158 
159     @Test
160     void testLimitOnlyAfterWhere() {
161         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
162             SelectStatementProvider selectStatement = select(animalData.allColumns())
163                     .from(animalData)
164                     .where(id, isLessThan(50))
165                     .limit(3)
166                     .build()
167                     .render(RenderingStrategies.MYBATIS3);
168 
169             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
170             List<AnimalData> records = mapper.selectMany(selectStatement);
171 
172             assertAll(
173                     () -> assertThat(records).hasSize(3),
174                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(1),
175                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData where id < #{parameters.p1,jdbcType=INTEGER} limit #{parameters.p2}"),
176                     () -> assertThat(selectStatement.getParameters()).containsEntry("p2", 3L)
177             );
178         }
179     }
180 
181     @Test
182     void testOffsetOnlyAfterWhere() {
183         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
184             SelectStatementProvider selectStatement = select(animalData.allColumns())
185                     .from(animalData)
186                     .where(id, isLessThan(50))
187                     .offset(22)
188                     .build()
189                     .render(RenderingStrategies.MYBATIS3);
190 
191             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
192             List<AnimalData> records = mapper.selectMany(selectStatement);
193 
194             assertAll(
195                     () -> assertThat(records).hasSize(27),
196                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(23),
197                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData where id < #{parameters.p1,jdbcType=INTEGER} offset #{parameters.p2} rows"),
198                     () -> assertThat(selectStatement.getParameters()).containsEntry("p2", 22L)
199             );
200         }
201     }
202 
203     @Test
204     void testLimitAndOffsetAfterOrderBy() {
205         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
206             SelectStatementProvider selectStatement = select(animalData.allColumns())
207                     .from(animalData)
208                     .orderBy(id)
209                     .limit(3)
210                     .offset(22)
211                     .build()
212                     .render(RenderingStrategies.MYBATIS3);
213 
214             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
215             List<AnimalData> records = mapper.selectMany(selectStatement);
216 
217             assertAll(
218                     () -> assertThat(records).hasSize(3),
219                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(23),
220                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData order by id limit #{parameters.p1} offset #{parameters.p2}"),
221                     () -> assertThat(selectStatement.getParameters()).containsEntry("p1", 3L),
222                     () -> assertThat(selectStatement.getParameters()).containsEntry("p2", 22L)
223             );
224         }
225     }
226 
227     @Test
228     void testLimitOnlyAfterOrderBy() {
229         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
230             SelectStatementProvider selectStatement = select(animalData.allColumns())
231                     .from(animalData)
232                     .orderBy(id)
233                     .limit(3)
234                     .build()
235                     .render(RenderingStrategies.MYBATIS3);
236 
237             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
238             List<AnimalData> records = mapper.selectMany(selectStatement);
239 
240             assertAll(
241                     () -> assertThat(records).hasSize(3),
242                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(1),
243                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData order by id limit #{parameters.p1}"),
244                     () -> assertThat(selectStatement.getParameters()).containsEntry("p1", 3L)
245             );
246         }
247     }
248 
249     @Test
250     void testOffsetOnlyAfterOrderBy() {
251         try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
252             SelectStatementProvider selectStatement = select(animalData.allColumns())
253                     .from(animalData)
254                     .orderBy(id)
255                     .offset(22)
256                     .build()
257                     .render(RenderingStrategies.MYBATIS3);
258 
259             AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class);
260             List<AnimalData> records = mapper.selectMany(selectStatement);
261 
262             assertAll(
263                     () -> assertThat(records).hasSize(43),
264                     () -> assertThat(records).first().isNotNull().extracting(AnimalData::id).isEqualTo(23),
265                     () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData order by id offset #{parameters.p1} rows"),
266                     () -> assertThat(selectStatement.getParameters()).containsEntry("p1", 22L)
267             );
268         }
269     }
270 }