From ea9bd6e643798759c8aad62f91e6dc559451ccbd Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Mon, 18 Mar 2019 21:12:29 -0400 Subject: [PATCH 1/5] Initial implementation of limit and offset --- .../org/mybatis/dynamic/sql/SqlColumn.java | 15 +- .../sql/select/QueryExpressionDSL.java | 44 ++- .../mybatis/dynamic/sql/select/SelectDSL.java | 38 ++- .../dynamic/sql/select/SelectModel.java | 26 +- .../DefaultSelectStatementProvider.java | 20 +- .../sql/select/render/SelectRenderer.java | 29 +- .../animal/data/LimitAndOffsetTest.java | 267 ++++++++++++++++++ .../java/examples/groupby/GroupByTest.java | 72 ++++- .../java/examples/joins/JoinMapperTest.java | 105 ++++++- 9 files changed, 603 insertions(+), 13 deletions(-) create mode 100644 src/test/java/examples/animal/data/LimitAndOffsetTest.java diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 33a869579..67e9e84de 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,7 @@ public class SqlColumn implements BindableColumn, SortSpecification { private SqlColumn(Builder builder) { name = Objects.requireNonNull(builder.name); jdbcType = builder.jdbcType; - table = Objects.requireNonNull(builder.table); + table = builder.table; typeHandler = builder.typeHandler; } @@ -89,9 +89,14 @@ public String aliasOrName() { return alias().orElse(name); } + public Optional table() { + return Optional.ofNullable(table); + } + @Override public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) { - return tableAliasCalculator.aliasForColumn(table) + return table() + .flatMap(tableAliasCalculator::aliasForColumn) .map(this::applyTableAlias) .orElseGet(this::name); } @@ -106,6 +111,10 @@ private String applyTableAlias(String tableAlias) { return tableAlias + "." + name(); //$NON-NLS-1$ } + public static SqlColumn of(String name) { + return SqlColumn.withName(name).build(); + } + public static SqlColumn of(String name, SqlTable table) { return SqlColumn.withName(name) .withTable(table) diff --git a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java index 614fcf5b4..2058ec646 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/QueryExpressionDSL.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -144,6 +144,16 @@ protected QueryExpressionModel buildModel() { .build(); } + public SelectDSL.LimitFinisher limit(long limit) { + selectDSL.addQueryExpression(buildModel()); + return selectDSL.limit(limit); + } + + public SelectDSL.OffsetFinisher offset(long offset) { + selectDSL.addQueryExpression(buildModel()); + return selectDSL.offset(offset); + } + public static class FromGatherer { private FromGathererBuilder builder; private Map tableAliasMap = new HashMap<>(); @@ -234,6 +244,18 @@ public GroupByFinisher groupBy(BasicColumn...columns) { return new GroupByFinisher(); } + public SelectDSL.LimitFinisher limit(long limit) { + whereModel = buildWhereModel(); + selectDSL.addQueryExpression(buildModel()); + return selectDSL.limit(limit); + } + + public SelectDSL.OffsetFinisher offset(long offset) { + whereModel = buildWhereModel(); + selectDSL.addQueryExpression(buildModel()); + return selectDSL.offset(offset); + } + @Override public R build() { whereModel = buildWhereModel(); @@ -384,6 +406,18 @@ public SelectDSL orderBy(SortSpecification...columns) { selectDSL.setOrderByModel(OrderByModel.of(columns)); return selectDSL; } + + public SelectDSL.LimitFinisher limit(long limit) { + joinModel = buildJoinModel(); + selectDSL.addQueryExpression(buildModel()); + return selectDSL.limit(limit); + } + + public SelectDSL.OffsetFinisher offset(long offset) { + joinModel = buildJoinModel(); + selectDSL.addQueryExpression(buildModel()); + return selectDSL.offset(offset); + } } public class GroupByFinisher implements Buildable { @@ -396,6 +430,14 @@ public SelectDSL orderBy(SortSpecification...columns) { public R build() { return selectDSL.build(); } + + public SelectDSL.LimitFinisher limit(long limit) { + return selectDSL.limit(limit); + } + + public SelectDSL.OffsetFinisher offset(long offset) { + return selectDSL.offset(offset); + } } public class UnionBuilder { diff --git a/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java b/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java index 4b0a5bb9c..b47b8e934 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/SelectDSL.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import org.mybatis.dynamic.sql.select.QueryExpressionDSL.FromGatherer; import org.mybatis.dynamic.sql.select.QueryExpressionDSL.FromGathererBuilder; import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; +import org.mybatis.dynamic.sql.util.Buildable; /** * Implements a standard SQL dialect for building model classes. @@ -32,11 +33,13 @@ * * @param the type of model produced by this builder */ -public class SelectDSL { +public class SelectDSL implements Buildable { private Function adapterFunction; private List queryExpressions = new ArrayList<>(); private OrderByModel orderByModel; + private Long limit; + private Long offset; private SelectDSL(Function adapterFunction) { this.adapterFunction = Objects.requireNonNull(adapterFunction); @@ -96,10 +99,41 @@ void setOrderByModel(OrderByModel orderByModel) { this.orderByModel = orderByModel; } + public LimitFinisher limit(long limit) { + this.limit = limit; + return new LimitFinisher(); + } + + public OffsetFinisher offset(long offset) { + this.offset = offset; + return new OffsetFinisher(); + } + + @Override public R build() { SelectModel selectModel = SelectModel.withQueryExpressions(queryExpressions) .withOrderByModel(orderByModel) + .withLimit(limit) + .withOffset(offset) .build(); return adapterFunction.apply(selectModel); } + + public class LimitFinisher implements Buildable { + public OffsetFinisher offset(int offset) { + return SelectDSL.this.offset(offset); + } + + @Override + public R build() { + return SelectDSL.this.build(); + } + } + + public class OffsetFinisher implements Buildable { + @Override + public R build() { + return SelectDSL.this.build(); + } + } } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java b/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java index 74fbbb62d..acb90dd3b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/SelectModel.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,10 +29,14 @@ public class SelectModel { private List queryExpressions; private OrderByModel orderByModel; + private Long limit; + private Long offset; private SelectModel(Builder builder) { queryExpressions = Objects.requireNonNull(builder.queryExpressions); orderByModel = builder.orderByModel; + limit = builder.limit; + offset = builder.offset; } public Stream mapQueryExpressions(Function mapper) { @@ -43,6 +47,14 @@ public Optional orderByModel() { return Optional.ofNullable(orderByModel); } + public Optional limit() { + return Optional.ofNullable(limit); + } + + public Optional offset() { + return Optional.ofNullable(offset); + } + public SelectStatementProvider render(RenderingStrategy renderingStrategy) { return SelectRenderer.withSelectModel(this) .withRenderingStrategy(renderingStrategy) @@ -57,6 +69,8 @@ public static Builder withQueryExpressions(List queryExpre public static class Builder { private List queryExpressions = new ArrayList<>(); private OrderByModel orderByModel; + private Long limit; + private Long offset; public Builder withQueryExpressions(List queryExpressions) { this.queryExpressions.addAll(queryExpressions); @@ -68,6 +82,16 @@ public Builder withOrderByModel(OrderByModel orderByModel) { return this; } + public Builder withLimit(Long limit) { + this.limit = limit; + return this; + } + + public Builder withOffset(Long offset) { + this.offset = offset; + return this; + } + public SelectModel build() { return new SelectModel(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/DefaultSelectStatementProvider.java b/src/main/java/org/mybatis/dynamic/sql/select/render/DefaultSelectStatementProvider.java index 3dd20a51c..97108557e 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/DefaultSelectStatementProvider.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/DefaultSelectStatementProvider.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,11 +27,15 @@ public class DefaultSelectStatementProvider implements SelectStatementProvider { private String queryExpression; private Map parameters; private Optional orderByClause; + private Optional limitClause; + private Optional offsetClause; private DefaultSelectStatementProvider(Builder builder) { queryExpression = Objects.requireNonNull(builder.queryExpression); orderByClause = Objects.requireNonNull(builder.orderByClause); parameters = Collections.unmodifiableMap(Objects.requireNonNull(builder.parameters)); + limitClause = Objects.requireNonNull(builder.limitClause); + offsetClause = Objects.requireNonNull(builder.offsetClause); } @Override @@ -41,7 +45,7 @@ public Map getParameters() { @Override public String getSelectStatement() { - return queryExpression + spaceBefore(orderByClause); + return queryExpression + spaceBefore(orderByClause) + spaceBefore(limitClause) + spaceBefore(offsetClause); } public static Builder withQueryExpression(String queryExpression) { @@ -52,6 +56,8 @@ public static class Builder { private String queryExpression; private Optional orderByClause = Optional.empty(); private Map parameters = new HashMap<>(); + private Optional limitClause = Optional.empty(); + private Optional offsetClause = Optional.empty(); public Builder withQueryExpression(String queryExpression) { this.queryExpression = queryExpression; @@ -68,6 +74,16 @@ public Builder withParameters(Map parameters) { return this; } + public Builder withLimitClause(Optional limitClause) { + this.limitClause = limitClause; + return this; + } + + public Builder withOffsetClause(Optional offsetClause) { + this.offsetClause = offsetClause; + return this; + } + public DefaultSelectStatementProvider build() { return new DefaultSelectStatementProvider(this); } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java index 963720894..a89f407cb 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,14 @@ */ package org.mybatis.dynamic.sql.select.render; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; +import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.SortSpecification; +import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.OrderByModel; import org.mybatis.dynamic.sql.select.QueryExpressionModel; @@ -27,6 +30,8 @@ import org.mybatis.dynamic.sql.util.CustomCollectors; public class SelectRenderer { + private static final String LIMIT_PARAMETER = "_limit"; //$NON-NLS-1$ + private static final String OFFSET_PARAMETER = "_offset"; //$NON-NLS-1$ private SelectModel selectModel; private RenderingStrategy renderingStrategy; private AtomicInteger sequence; @@ -42,9 +47,15 @@ public SelectStatementProvider render() { .mapQueryExpressions(this::renderQueryExpression) .collect(QueryExpressionCollector.collect()); + Map parameters = collector.parameters(); + Optional limitClause = selectModel.limit().map(l -> renderLimit(parameters, l)); + Optional offsetClause = selectModel.offset().map(o -> renderOffset(parameters, o)); + return DefaultSelectStatementProvider.withQueryExpression(collector.queryExpression()) - .withParameters(collector.parameters()) + .withParameters(parameters) .withOrderByClause(selectModel.orderByModel().map(this::renderOrderBy)) + .withLimitClause(limitClause) + .withOffsetClause(offsetClause) .build(); } @@ -69,6 +80,20 @@ private String orderByPhrase(SortSpecification column) { return phrase; } + private String renderLimit(Map parameters, Long limit) { + BindableColumn bc = SqlColumn.of(LIMIT_PARAMETER); + String placeholder = renderingStrategy.getFormattedJdbcPlaceholder(bc, "parameters", LIMIT_PARAMETER); //$NON-NLS-1$ + parameters.put(LIMIT_PARAMETER, limit); + return "limit " + placeholder; //$NON-NLS-1$ + } + + private String renderOffset(Map parameters, Long offset) { + BindableColumn bc = SqlColumn.of(OFFSET_PARAMETER); + String placeholder = renderingStrategy.getFormattedJdbcPlaceholder(bc, "parameters", OFFSET_PARAMETER); //$NON-NLS-1$ + parameters.put(OFFSET_PARAMETER, offset); + return "offset " + placeholder; //$NON-NLS-1$ + } + public static Builder withSelectModel(SelectModel selectModel) { return new Builder().withSelectModel(selectModel); } diff --git a/src/test/java/examples/animal/data/LimitAndOffsetTest.java b/src/test/java/examples/animal/data/LimitAndOffsetTest.java new file mode 100644 index 000000000..8d7ad6962 --- /dev/null +++ b/src/test/java/examples/animal/data/LimitAndOffsetTest.java @@ -0,0 +1,267 @@ +/** + * Copyright 2016-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package examples.animal.data; + +import static examples.animal.data.AnimalDataDynamicSqlSupport.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mybatis.dynamic.sql.SqlBuilder.*; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.DriverManager; +import java.util.List; + +import org.apache.ibatis.datasource.unpooled.UnpooledDataSource; +import org.apache.ibatis.jdbc.ScriptRunner; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSession; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mybatis.dynamic.sql.render.RenderingStrategy; +import org.mybatis.dynamic.sql.select.render.SelectStatementProvider; + +public class LimitAndOffsetTest { + + private static final String JDBC_URL = "jdbc:hsqldb:mem:aname"; + private static final String JDBC_DRIVER = "org.hsqldb.jdbcDriver"; + + private SqlSessionFactory sqlSessionFactory; + + @BeforeEach + public void setup() throws Exception { + Class.forName(JDBC_DRIVER); + InputStream is = getClass().getResourceAsStream("/examples/animal/data/CreateAnimalData.sql"); + try (Connection connection = DriverManager.getConnection(JDBC_URL, "sa", "")) { + ScriptRunner sr = new ScriptRunner(connection); + sr.setLogWriter(null); + sr.runScript(new InputStreamReader(is)); + } + + UnpooledDataSource ds = new UnpooledDataSource(JDBC_DRIVER, JDBC_URL, "sa", ""); + Environment environment = new Environment("test", new JdbcTransactionFactory(), ds); + Configuration config = new Configuration(environment); + config.addMapper(AnimalDataMapper.class); + sqlSessionFactory = new SqlSessionFactoryBuilder().build(config); + } + + @Test + public void testLimitAndOffsetAfterFrom() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .limit(3) + .offset(22) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(3), + () -> assertThat(records.get(0).getId()).isEqualTo(23), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData limit #{parameters._limit} offset #{parameters._offset}"), + () -> assertThat(selectStatement.getParameters().get("_limit")).isEqualTo(3L), + () -> assertThat(selectStatement.getParameters().get("_offset")).isEqualTo(22L) + ); + } + } + + @Test + public void testLimitOnlyAfterFrom() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .limit(3) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(3), + () -> assertThat(records.get(0).getId()).isEqualTo(1), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData limit #{parameters._limit}"), + () -> assertThat(selectStatement.getParameters().get("_limit")).isEqualTo(3L) + ); + } + } + + @Test + public void testOffsetOnlyAfterFrom() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .offset(22) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(43), + () -> assertThat(records.get(0).getId()).isEqualTo(23), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData offset #{parameters._offset}"), + () -> assertThat(selectStatement.getParameters().get("_offset")).isEqualTo(22L) + ); + } + } + + @Test + public void testLimitAndOffsetAfterWhere() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .where(id, isLessThan(50)) + .and(id, isGreaterThan(22)) + .limit(3) + .offset(22) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(3), + () -> assertThat(records.get(0).getId()).isEqualTo(45), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData where id < #{parameters.p1,jdbcType=INTEGER} and id > #{parameters.p2,jdbcType=INTEGER} limit #{parameters._limit} offset #{parameters._offset}"), + () -> assertThat(selectStatement.getParameters().get("_limit")).isEqualTo(3L), + () -> assertThat(selectStatement.getParameters().get("_offset")).isEqualTo(22L) + ); + } + } + + @Test + public void testLimitOnlyAfterWhere() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .where(id, isLessThan(50)) + .limit(3) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(3), + () -> assertThat(records.get(0).getId()).isEqualTo(1), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData where id < #{parameters.p1,jdbcType=INTEGER} limit #{parameters._limit}"), + () -> assertThat(selectStatement.getParameters().get("_limit")).isEqualTo(3L) + ); + } + } + + @Test + public void testOffsetOnlyAfterWhere() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .where(id, isLessThan(50)) + .offset(22) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(27), + () -> assertThat(records.get(0).getId()).isEqualTo(23), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData where id < #{parameters.p1,jdbcType=INTEGER} offset #{parameters._offset}"), + () -> assertThat(selectStatement.getParameters().get("_offset")).isEqualTo(22L) + ); + } + } + + @Test + public void testLimitAndOffsetAfterOrderBy() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .orderBy(id) + .limit(3) + .offset(22) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(3), + () -> assertThat(records.get(0).getId()).isEqualTo(23), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData order by id limit #{parameters._limit} offset #{parameters._offset}"), + () -> assertThat(selectStatement.getParameters().get("_limit")).isEqualTo(3L), + () -> assertThat(selectStatement.getParameters().get("_offset")).isEqualTo(22L) + ); + } + } + + @Test + public void testLimitOnlyAfterOrderBy() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .orderBy(id) + .limit(3) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(3), + () -> assertThat(records.get(0).getId()).isEqualTo(1), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData order by id limit #{parameters._limit}"), + () -> assertThat(selectStatement.getParameters().get("_limit")).isEqualTo(3L) + ); + } + } + + @Test + public void testOffsetOnlyAfterOrderBy() { + try (SqlSession sqlSession = sqlSessionFactory.openSession()) { + SelectStatementProvider selectStatement = select(animalData.allColumns()) + .from(animalData) + .orderBy(id) + .offset(22) + .build() + .render(RenderingStrategy.MYBATIS3); + + AnimalDataMapper mapper = sqlSession.getMapper(AnimalDataMapper.class); + List records = mapper.selectMany(selectStatement); + + assertAll( + () -> assertThat(records.size()).isEqualTo(43), + () -> assertThat(records.get(0).getId()).isEqualTo(23), + () -> assertThat(selectStatement.getSelectStatement()).isEqualTo("select * from AnimalData order by id offset #{parameters._offset}"), + () -> assertThat(selectStatement.getParameters().get("_offset")).isEqualTo(22L) + ); + } + } +} diff --git a/src/test/java/examples/groupby/GroupByTest.java b/src/test/java/examples/groupby/GroupByTest.java index 018e290d8..2a50a9727 100644 --- a/src/test/java/examples/groupby/GroupByTest.java +++ b/src/test/java/examples/groupby/GroupByTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -195,4 +195,74 @@ public void testGroupByAfterWhere() { assertThat(row.get("COUNT")).isEqualTo(2L); } } + + @Test + public void testLimitAndOffsetAfterGroupBy() { + try (SqlSession session = sqlSessionFactory.openSession()) { + GroupByMapper mapper = session.getMapper(GroupByMapper.class); + + SelectStatementProvider selectStatement = select(lastName, count().as("count")) + .from(person) + .groupBy(lastName) + .limit(1) + .offset(1) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expected = "select last_name, count(*) as count from Person group by last_name limit #{parameters._limit} offset #{parameters._offset}"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expected); + + List> rows = mapper.generalSelect(selectStatement); + assertThat(rows.size()).isEqualTo(1); + Map row = rows.get(0); + assertThat(row.get("LAST_NAME")).isEqualTo("Rubble"); + assertThat(row.get("COUNT")).isEqualTo(3L); + } + } + + @Test + public void testLimitOnlyAfterGroupBy() { + try (SqlSession session = sqlSessionFactory.openSession()) { + GroupByMapper mapper = session.getMapper(GroupByMapper.class); + + SelectStatementProvider selectStatement = select(lastName, count().as("count")) + .from(person) + .groupBy(lastName) + .limit(1) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expected = "select last_name, count(*) as count from Person group by last_name limit #{parameters._limit}"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expected); + + List> rows = mapper.generalSelect(selectStatement); + assertThat(rows.size()).isEqualTo(1); + Map row = rows.get(0); + assertThat(row.get("LAST_NAME")).isEqualTo("Flintstone"); + assertThat(row.get("COUNT")).isEqualTo(4L); + } + } + + @Test + public void testOffsetOnlyAfterGroupBy() { + try (SqlSession session = sqlSessionFactory.openSession()) { + GroupByMapper mapper = session.getMapper(GroupByMapper.class); + + SelectStatementProvider selectStatement = select(lastName, count().as("count")) + .from(person) + .groupBy(lastName) + .offset(1) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expected = "select last_name, count(*) as count from Person group by last_name offset #{parameters._offset}"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expected); + + List> rows = mapper.generalSelect(selectStatement); + assertThat(rows.size()).isEqualTo(1); + Map row = rows.get(0); + assertThat(row.get("LAST_NAME")).isEqualTo("Rubble"); + assertThat(row.get("COUNT")).isEqualTo(3L); + } + } } diff --git a/src/test/java/examples/joins/JoinMapperTest.java b/src/test/java/examples/joins/JoinMapperTest.java index 37ca5171b..9b61bb4b4 100644 --- a/src/test/java/examples/joins/JoinMapperTest.java +++ b/src/test/java/examples/joins/JoinMapperTest.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -511,4 +511,107 @@ public void testSelf() { assertThat(row.getParentId()).isNull(); } } + + @Test + public void testLimitAndOffsetAfterJoin() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description) + .from(itemMaster, "im") + .leftJoin(orderLine, "ol").on(orderLine.itemId, equalTo(itemMaster.itemId)) + .limit(2) + .offset(1) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expectedStatment = "select ol.order_id, ol.quantity, im.item_id, im.description" + + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id" + + " limit #{parameters._limit} offset #{parameters._offset}"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List> rows = mapper.generalSelect(selectStatement); + + assertThat(rows.size()).isEqualTo(2); + Map row = rows.get(0); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Helmet"); + assertThat(row.get("ITEM_ID")).isEqualTo(22); + + row = rows.get(1); + assertThat(row.get("ORDER_ID")).isEqualTo(1); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("First Base Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(33); + } + } + + @Test + public void testLimitOnlyAfterJoin() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description) + .from(itemMaster, "im") + .leftJoin(orderLine, "ol").on(orderLine.itemId, equalTo(itemMaster.itemId)) + .limit(2) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expectedStatment = "select ol.order_id, ol.quantity, im.item_id, im.description" + + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id" + + " limit #{parameters._limit}"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List> rows = mapper.generalSelect(selectStatement); + + assertThat(rows.size()).isEqualTo(2); + Map row = rows.get(0); + assertThat(row.get("ORDER_ID")).isEqualTo(1); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Helmet"); + assertThat(row.get("ITEM_ID")).isEqualTo(22); + + row = rows.get(1); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Helmet"); + assertThat(row.get("ITEM_ID")).isEqualTo(22); + } + } + + @Test + public void testOffsetOnlyAfterJoin() { + try (SqlSession session = sqlSessionFactory.openSession()) { + JoinMapper mapper = session.getMapper(JoinMapper.class); + + SelectStatementProvider selectStatement = select(orderLine.orderId, orderLine.quantity, itemMaster.itemId, itemMaster.description) + .from(itemMaster, "im") + .leftJoin(orderLine, "ol").on(orderLine.itemId, equalTo(itemMaster.itemId)) + .offset(2) + .build() + .render(RenderingStrategy.MYBATIS3); + + String expectedStatment = "select ol.order_id, ol.quantity, im.item_id, im.description" + + " from ItemMaster im left join OrderLine ol on ol.item_id = im.item_id" + + " offset #{parameters._offset}"; + assertThat(selectStatement.getSelectStatement()).isEqualTo(expectedStatment); + + List> rows = mapper.generalSelect(selectStatement); + + assertThat(rows.size()).isEqualTo(3); + Map row = rows.get(0); + assertThat(row.get("ORDER_ID")).isEqualTo(1); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("First Base Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(33); + + row = rows.get(1); + assertThat(row.get("ORDER_ID")).isEqualTo(2); + assertThat(row.get("QUANTITY")).isEqualTo(1); + assertThat(row.get("DESCRIPTION")).isEqualTo("Outfield Glove"); + assertThat(row.get("ITEM_ID")).isEqualTo(44); + } + } } From 7ff0d1d1045c7031fe1bc9a1473585b0326d4487 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Mon, 18 Mar 2019 21:41:21 -0400 Subject: [PATCH 2/5] Allow rendering parameters that aren't columns --- .../org/mybatis/dynamic/sql/SqlColumn.java | 13 ++----------- .../sql/render/MyBatis3RenderingStrategy.java | 18 ++++++++++++------ .../dynamic/sql/render/RenderingStrategy.java | 14 ++++++++++++-- .../SpringNamedParameterRenderingStrategy.java | 7 ++++--- .../sql/select/render/SelectRenderer.java | 8 ++------ 5 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java index 67e9e84de..ac08533df 100644 --- a/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java +++ b/src/main/java/org/mybatis/dynamic/sql/SqlColumn.java @@ -33,7 +33,7 @@ public class SqlColumn implements BindableColumn, SortSpecification { private SqlColumn(Builder builder) { name = Objects.requireNonNull(builder.name); jdbcType = builder.jdbcType; - table = builder.table; + table = Objects.requireNonNull(builder.table); typeHandler = builder.typeHandler; } @@ -89,14 +89,9 @@ public String aliasOrName() { return alias().orElse(name); } - public Optional table() { - return Optional.ofNullable(table); - } - @Override public String renderWithTableAlias(TableAliasCalculator tableAliasCalculator) { - return table() - .flatMap(tableAliasCalculator::aliasForColumn) + return tableAliasCalculator.aliasForColumn(table) .map(this::applyTableAlias) .orElseGet(this::name); } @@ -111,10 +106,6 @@ private String applyTableAlias(String tableAlias) { return tableAlias + "." + name(); //$NON-NLS-1$ } - public static SqlColumn of(String name) { - return SqlColumn.withName(name).build(); - } - public static SqlColumn of(String name, SqlTable table) { return SqlColumn.withName(name) .withTable(table) diff --git a/src/main/java/org/mybatis/dynamic/sql/render/MyBatis3RenderingStrategy.java b/src/main/java/org/mybatis/dynamic/sql/render/MyBatis3RenderingStrategy.java index ce3bf5702..b5f4b16ca 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/MyBatis3RenderingStrategy.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/MyBatis3RenderingStrategy.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,13 @@ */ package org.mybatis.dynamic.sql.render; +import java.util.Optional; + import org.mybatis.dynamic.sql.BindableColumn; public class MyBatis3RenderingStrategy extends RenderingStrategy { @Override - public String getFormattedJdbcPlaceholder(BindableColumn column, String prefix, String parameterName) { + public String getFormattedJdbcPlaceholder(Optional> column, String prefix, String parameterName) { return "#{" //$NON-NLS-1$ + prefix + "." //$NON-NLS-1$ @@ -29,13 +31,17 @@ public String getFormattedJdbcPlaceholder(BindableColumn column, String prefi + "}"; //$NON-NLS-1$ } - private String renderTypeHandler(BindableColumn column) { - return column.typeHandler().map(th -> ",typeHandler=" + th) //$NON-NLS-1$ + private String renderTypeHandler(Optional> column) { + return column + .flatMap(BindableColumn::typeHandler) + .map(th -> ",typeHandler=" + th) //$NON-NLS-1$ .orElse(""); //$NON-NLS-1$ } - private String renderJdbcType(BindableColumn column) { - return column.jdbcType().map(jt -> ",jdbcType=" + jt.getName()) //$NON-NLS-1$ + private String renderJdbcType(Optional> column) { + return column + .flatMap(BindableColumn::jdbcType) + .map(jt -> ",jdbcType=" + jt.getName()) //$NON-NLS-1$ .orElse(""); //$NON-NLS-1$ } } diff --git a/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java b/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java index 20b36053a..cfed8a660 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,11 +15,21 @@ */ package org.mybatis.dynamic.sql.render; +import java.util.Optional; + import org.mybatis.dynamic.sql.BindableColumn; public abstract class RenderingStrategy { public static final RenderingStrategy MYBATIS3 = new MyBatis3RenderingStrategy(); public static final RenderingStrategy SPRING_NAMED_PARAMETER = new SpringNamedParameterRenderingStrategy(); - public abstract String getFormattedJdbcPlaceholder(BindableColumn column, String prefix, String parameterName); + public String getFormattedJdbcPlaceholder(BindableColumn column, String prefix, String parameterName) { + return getFormattedJdbcPlaceholder(Optional.of(column), prefix, parameterName); + } + + public String getFormattedJdbcPlaceholder(String prefix, String parameterName) { + return getFormattedJdbcPlaceholder(Optional.empty(), prefix, parameterName); + } + + public abstract String getFormattedJdbcPlaceholder(Optional> column, String prefix, String parameterName); } diff --git a/src/main/java/org/mybatis/dynamic/sql/render/SpringNamedParameterRenderingStrategy.java b/src/main/java/org/mybatis/dynamic/sql/render/SpringNamedParameterRenderingStrategy.java index 55795e107..a2260e42b 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/SpringNamedParameterRenderingStrategy.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/SpringNamedParameterRenderingStrategy.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,13 +15,14 @@ */ package org.mybatis.dynamic.sql.render; +import java.util.Optional; + import org.mybatis.dynamic.sql.BindableColumn; public class SpringNamedParameterRenderingStrategy extends RenderingStrategy { @Override - public String getFormattedJdbcPlaceholder(BindableColumn column, String prefix, String parameterName) { + public String getFormattedJdbcPlaceholder(Optional> column, String prefix, String parameterName) { return ":" + parameterName; //$NON-NLS-1$ } - } diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java index a89f407cb..fc6cad5ca 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java @@ -20,9 +20,7 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; -import org.mybatis.dynamic.sql.BindableColumn; import org.mybatis.dynamic.sql.SortSpecification; -import org.mybatis.dynamic.sql.SqlColumn; import org.mybatis.dynamic.sql.render.RenderingStrategy; import org.mybatis.dynamic.sql.select.OrderByModel; import org.mybatis.dynamic.sql.select.QueryExpressionModel; @@ -81,15 +79,13 @@ private String orderByPhrase(SortSpecification column) { } private String renderLimit(Map parameters, Long limit) { - BindableColumn bc = SqlColumn.of(LIMIT_PARAMETER); - String placeholder = renderingStrategy.getFormattedJdbcPlaceholder(bc, "parameters", LIMIT_PARAMETER); //$NON-NLS-1$ + String placeholder = renderingStrategy.getFormattedJdbcPlaceholder("parameters", LIMIT_PARAMETER); //$NON-NLS-1$ parameters.put(LIMIT_PARAMETER, limit); return "limit " + placeholder; //$NON-NLS-1$ } private String renderOffset(Map parameters, Long offset) { - BindableColumn bc = SqlColumn.of(OFFSET_PARAMETER); - String placeholder = renderingStrategy.getFormattedJdbcPlaceholder(bc, "parameters", OFFSET_PARAMETER); //$NON-NLS-1$ + String placeholder = renderingStrategy.getFormattedJdbcPlaceholder("parameters", OFFSET_PARAMETER); //$NON-NLS-1$ parameters.put(OFFSET_PARAMETER, offset); return "offset " + placeholder; //$NON-NLS-1$ } From 37e20dd631e7bc53f2b0fcf4f46517cbbb7f3214 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 2 Apr 2019 09:40:16 -0400 Subject: [PATCH 3/5] Add an initial change log and wire it into the generated site --- CHANGELOG.md | 10 ++++++++++ pom.xml | 24 ++++++++++++++++++++++++ src/site/site.xml | 1 + 3 files changed, 35 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..cb2e88123 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Change Log + +## Release 1.1.1 (Unreleased) + +### Added + +- Limit and offset support in the select statement + + +## Release 1.1.0 diff --git a/pom.xml b/pom.xml index b808c6973..8d25f97e2 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,30 @@ + + + + maven-resources-plugin + + + copy-changelog + pre-site + + copy-resources + + + ${project.build.directory}/generated-site/markdown/docs + + + ${basedir} + CHANGELOG.md + + + + + + + diff --git a/src/site/site.xml b/src/site/site.xml index 5adf86091..d2bebddef 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -35,6 +35,7 @@ + From 507338dbe82ce9bd55800e29322cc9539f88a55a Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 2 Apr 2019 14:37:55 -0400 Subject: [PATCH 4/5] Use a constant for the default parameter prefix --- .../org/mybatis/dynamic/sql/render/RenderingStrategy.java | 1 + .../mybatis/dynamic/sql/select/render/SelectRenderer.java | 4 ++-- .../dynamic/sql/update/render/SetPhraseVisitor.java | 4 ++-- .../dynamic/sql/where/render/WhereConditionVisitor.java | 7 +++---- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java b/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java index cfed8a660..3939fe7e1 100644 --- a/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java +++ b/src/main/java/org/mybatis/dynamic/sql/render/RenderingStrategy.java @@ -22,6 +22,7 @@ public abstract class RenderingStrategy { public static final RenderingStrategy MYBATIS3 = new MyBatis3RenderingStrategy(); public static final RenderingStrategy SPRING_NAMED_PARAMETER = new SpringNamedParameterRenderingStrategy(); + public static final String DEFAULT_PARAMETER_PREFIX = "parameters"; //$NON-NLS-1$ public String getFormattedJdbcPlaceholder(BindableColumn column, String prefix, String parameterName) { return getFormattedJdbcPlaceholder(Optional.of(column), prefix, parameterName); diff --git a/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java b/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java index fc6cad5ca..33f4bae08 100644 --- a/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java +++ b/src/main/java/org/mybatis/dynamic/sql/select/render/SelectRenderer.java @@ -79,13 +79,13 @@ private String orderByPhrase(SortSpecification column) { } private String renderLimit(Map parameters, Long limit) { - String placeholder = renderingStrategy.getFormattedJdbcPlaceholder("parameters", LIMIT_PARAMETER); //$NON-NLS-1$ + String placeholder = renderingStrategy.getFormattedJdbcPlaceholder(RenderingStrategy.DEFAULT_PARAMETER_PREFIX, LIMIT_PARAMETER); parameters.put(LIMIT_PARAMETER, limit); return "limit " + placeholder; //$NON-NLS-1$ } private String renderOffset(Map parameters, Long offset) { - String placeholder = renderingStrategy.getFormattedJdbcPlaceholder("parameters", OFFSET_PARAMETER); //$NON-NLS-1$ + String placeholder = renderingStrategy.getFormattedJdbcPlaceholder(RenderingStrategy.DEFAULT_PARAMETER_PREFIX, OFFSET_PARAMETER); parameters.put(OFFSET_PARAMETER, offset); return "offset " + placeholder; //$NON-NLS-1$ } diff --git a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java index e2e465329..b048a83c0 100644 --- a/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/update/render/SetPhraseVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -111,6 +111,6 @@ public FragmentAndParameters visit(ColumnMapping mapping) { private Function, String> toJdbcPlaceholder(String parameterName) { return column -> renderingStrategy - .getFormattedJdbcPlaceholder(column, "parameters", parameterName); //$NON-NLS-1$ + .getFormattedJdbcPlaceholder(column, RenderingStrategy.DEFAULT_PARAMETER_PREFIX, parameterName); } } diff --git a/src/main/java/org/mybatis/dynamic/sql/where/render/WhereConditionVisitor.java b/src/main/java/org/mybatis/dynamic/sql/where/render/WhereConditionVisitor.java index 3f8f79dcb..3cb0fb0a4 100644 --- a/src/main/java/org/mybatis/dynamic/sql/where/render/WhereConditionVisitor.java +++ b/src/main/java/org/mybatis/dynamic/sql/where/render/WhereConditionVisitor.java @@ -1,5 +1,5 @@ /** - * Copyright 2016-2018 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -142,12 +142,11 @@ public static Builder withColumn(BindableColumn column) { } public static class Builder { - private static final String DEFAULT_PARAMETER_PREFIX = "parameters"; //$NON-NLS-1$ private RenderingStrategy renderingStrategy; private AtomicInteger sequence; private BindableColumn column; private TableAliasCalculator tableAliasCalculator; - private String parameterPrefix = DEFAULT_PARAMETER_PREFIX; + private String parameterPrefix = RenderingStrategy.DEFAULT_PARAMETER_PREFIX; public Builder withSequence(AtomicInteger sequence) { this.sequence = sequence; @@ -171,7 +170,7 @@ public Builder withTableAliasCalculator(TableAliasCalculator tableAliasCalcul public Builder withParameterName(String parameterName) { if (parameterName != null) { - parameterPrefix = parameterName + "." + DEFAULT_PARAMETER_PREFIX; //$NON-NLS-1$ + parameterPrefix = parameterName + "." + RenderingStrategy.DEFAULT_PARAMETER_PREFIX; //$NON-NLS-1$ } return this; } From c23d62a55ccb3cc62e70a8ca2e8afc6c9a1148a0 Mon Sep 17 00:00:00 2001 From: Jeff Butler Date: Tue, 2 Apr 2019 14:55:23 -0400 Subject: [PATCH 5/5] Documentation for limit and offset --- src/site/markdown/docs/select.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/site/markdown/docs/select.md b/src/site/markdown/docs/select.md index 0d23d7f83..2629fbaf9 100644 --- a/src/site/markdown/docs/select.md +++ b/src/site/markdown/docs/select.md @@ -195,3 +195,11 @@ When using a column function (lower, upper, etc.), then is is customary to give In this example the `substring` function is used in both the select list and the GROUP BY expression. In the ORDER BY expression, we use the `sortColumn` function to duplicate the alias given to the column in the select list. +## Limit and Offset Support +Since version 1.1.1, the select statement supports limit and offset. You can specify: + +- Limit only +- Offset only +- Both limit and offset + +It is important to note that the select renderer writes limit and offset clauses into the generated select statement as is. The library does not attempt to normalize those values for databases that don't support limit and offset directly. Therefore, it is very important for users to understand whether or not the target database supports limit and offset. If the target database does not support limit and offset, then it is likely that using this support will create SQL that has runtime errors.