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