mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-24 15:55:55 +00:00
#278 - SqlQuery setFirstRow fail - modify SQL using LIMIT/OFFSET etc to support firstRows/maxRows
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Adds ANSI based OFFSET FETCH NEXT clauses to a SQL query.
|
||||
*/
|
||||
public class BasicSqlAnsiLimiter implements BasicSqlLimiter {
|
||||
|
||||
@Override
|
||||
public String limit(String dbSql, int firstRow, int maxRows) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
|
||||
sb.append(dbSql);
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append("offset");
|
||||
sb.append(" ").append(firstRow).append(" rows");
|
||||
}
|
||||
if (maxRows > 0) {
|
||||
sb.append(" fetch next ").append(maxRows).append(" rows only");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Adds LIMIT OFFSET clauses to a SQL query.
|
||||
*/
|
||||
public class BasicSqlLimitOffset implements BasicSqlLimiter {
|
||||
|
||||
/**
|
||||
* LIMIT keyword.
|
||||
*/
|
||||
private static final String LIMIT = "limit";
|
||||
|
||||
/**
|
||||
* OFFSET keyword.
|
||||
*/
|
||||
private static final String OFFSET = "offset";
|
||||
|
||||
@Override
|
||||
public String limit(String dbSql, int firstRow, int maxRows) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(50 + dbSql.length());
|
||||
|
||||
sb.append(dbSql);
|
||||
|
||||
if (maxRows > 0) {
|
||||
sb.append(" ").append(LIMIT);
|
||||
sb.append(" ").append(maxRows);
|
||||
}
|
||||
if (firstRow > 0) {
|
||||
sb.append(" ").append(OFFSET).append(" ");
|
||||
sb.append(firstRow);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
/**
|
||||
* Simple SQL limiter for use with SqlQuery.
|
||||
*/
|
||||
public interface BasicSqlLimiter {
|
||||
|
||||
/**
|
||||
* Add basic offset/limit clause to SqlQuery query.
|
||||
*/
|
||||
String limit(String dbSql, int firstRow, int maxRows);
|
||||
}
|
||||
@@ -71,6 +71,11 @@ public class DatabasePlatform {
|
||||
*/
|
||||
protected SqlLimiter sqlLimiter = new LimitOffsetSqlLimiter();
|
||||
|
||||
/**
|
||||
* Limit/offset support for SqlQuery only.
|
||||
*/
|
||||
protected BasicSqlLimiter basicSqlLimiter = new BasicSqlLimitOffset();
|
||||
|
||||
/**
|
||||
* Mapping of JDBC to Database types.
|
||||
*/
|
||||
@@ -454,6 +459,13 @@ public class DatabasePlatform {
|
||||
return sqlLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the BasicSqlLimiter for limit/offset of SqlQuery queries.
|
||||
*/
|
||||
public BasicSqlLimiter getBasicSqlLimiter() {
|
||||
return basicSqlLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the DB TRUE literal (from the registered boolean ScalarType)
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@ public class OraclePlatform extends DatabasePlatform {
|
||||
this.maxConstraintNameLength = 30;
|
||||
this.dbEncrypt = new OracleDbEncrypt();
|
||||
this.sqlLimiter = new RownumSqlLimiter();
|
||||
this.basicSqlLimiter = new BasicSqlAnsiLimiter();
|
||||
this.platformDdl = new Oracle10Ddl(this);
|
||||
this.historySupport = new OracleDbHistorySupport();
|
||||
|
||||
|
||||
@@ -31,8 +31,6 @@ import java.util.List;
|
||||
*/
|
||||
public final class RelationalQueryRequest {
|
||||
|
||||
private static final int GLOBAL_ROW_LIMIT = Integer.valueOf(System.getProperty("ebean.query.globallimit", "1000000"));
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(RelationalQueryRequest.class);
|
||||
|
||||
private final SpiSqlQuery query;
|
||||
@@ -212,7 +210,18 @@ public final class RelationalQueryRequest {
|
||||
// convert any named parameters if required
|
||||
sql = BindParamsParser.parse(bindParams, sql);
|
||||
}
|
||||
this.sql = sql;
|
||||
this.sql = limitOffset(sql);
|
||||
}
|
||||
|
||||
private String limitOffset(String sql) {
|
||||
|
||||
int firstRow = query.getFirstRow();
|
||||
int maxRows = query.getMaxRows();
|
||||
if (firstRow > 0 || maxRows > 0) {
|
||||
return ebeanServer.getDatabasePlatform().getBasicSqlLimiter()
|
||||
.limit(query.getQuery(), firstRow, maxRows);
|
||||
}
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,13 +259,6 @@ public final class RelationalQueryRequest {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the maxRows allowed to fetch.
|
||||
*/
|
||||
public int getMaxRows() {
|
||||
return query.getMaxRows() >= 1 ? query.getMaxRows() : GLOBAL_ROW_LIMIT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL executed for this query.
|
||||
*/
|
||||
|
||||
@@ -74,19 +74,9 @@ public class DefaultRelationalQueryEngine implements RelationalQueryEngine {
|
||||
try {
|
||||
request.executeSql(binder);
|
||||
|
||||
int maxRows = request.getMaxRows();
|
||||
|
||||
int loadRowCount = 0;
|
||||
|
||||
List<SqlRow> rows = new ArrayList<SqlRow>();
|
||||
|
||||
while (request.next()) {
|
||||
rows.add(readRow(request));
|
||||
loadRowCount++;
|
||||
if (loadRowCount == maxRows) {
|
||||
// break, as we have hit the max rows to fetch...
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logSummary(request, startTime);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BasicSqlLimitOffsetTest {
|
||||
|
||||
private BasicSqlLimitOffset limiter = new BasicSqlLimitOffset();
|
||||
|
||||
@Test
|
||||
public void limit_maxRows() throws Exception {
|
||||
|
||||
String query = "select * from mytab order by id";
|
||||
String sql = limiter.limit(query, 0, 10);
|
||||
assertThat(sql).isEqualTo(query+" limit 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void limit_firstRowMaxRows() throws Exception {
|
||||
|
||||
String query = "select * from mytab order by id";
|
||||
String sql = limiter.limit(query, 5, 10);
|
||||
assertThat(sql).isEqualTo(query+" limit 10 offset 5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void limit_zeros() throws Exception {
|
||||
|
||||
String query = "select * from mytab order by id";
|
||||
String sql = limiter.limit(query, 0, 0);
|
||||
assertThat(sql).isEqualTo(query);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.avaje.ebean.config.dbplatform;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class BasicSqlStandardLimiterTest {
|
||||
|
||||
private BasicSqlAnsiLimiter limiter = new BasicSqlAnsiLimiter();
|
||||
|
||||
@Test
|
||||
public void limit_maxRows() throws Exception {
|
||||
|
||||
String query = "select * from mytab order by id";
|
||||
String sql = limiter.limit(query, 0, 10);
|
||||
assertThat(sql).isEqualTo(query+" fetch next 10 rows only");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void limit_firstRowMaxRows() throws Exception {
|
||||
|
||||
String query = "select * from mytab order by id";
|
||||
String sql = limiter.limit(query, 5, 10);
|
||||
assertThat(sql).isEqualTo(query+" offset 5 rows fetch next 10 rows only");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void limit_zeros() throws Exception {
|
||||
|
||||
String query = "select * from mytab order by id";
|
||||
String sql = limiter.limit(query, 0, 0);
|
||||
assertThat(sql).isEqualTo(query);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package com.avaje.tests.basic;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.Query;
|
||||
import com.avaje.ebeaninternal.api.SpiEbeanServer;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
||||
public class TestLimitQuery extends BaseTestCase {
|
||||
@@ -26,9 +26,6 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
|
||||
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.setAutoTune(false)
|
||||
.fetch("details")
|
||||
@@ -40,21 +37,16 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
query.findList();
|
||||
|
||||
String sql = query.getGeneratedSql();
|
||||
boolean hasLimit = sql.contains("limit 0");
|
||||
boolean hasOffset = sql.contains("offset 3");
|
||||
|
||||
if (h2Db) {
|
||||
Assert.assertTrue(hasLimit);
|
||||
Assert.assertTrue(hasOffset);
|
||||
if (isH2()) {
|
||||
assertThat(sql).contains("offset 3");
|
||||
assertThat(sql).contains("limit 0");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxRowsWithFirstRowZero() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
|
||||
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.setAutoTune(false)
|
||||
@@ -69,7 +61,7 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
boolean hasLimit = sql.contains("limit 3");
|
||||
boolean hasOffset = sql.contains("offset");
|
||||
|
||||
if (h2Db) {
|
||||
if (isH2()) {
|
||||
Assert.assertTrue(sql, hasLimit);
|
||||
Assert.assertFalse(sql, hasOffset);
|
||||
}
|
||||
@@ -79,9 +71,6 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
public void testDefaults() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
|
||||
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.setAutoTune(false)
|
||||
.fetch("details")
|
||||
@@ -94,7 +83,7 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
boolean hasLimit = sql.contains("limit");
|
||||
boolean hasOffset = sql.contains("offset");
|
||||
|
||||
if (h2Db) {
|
||||
if (isH2()) {
|
||||
Assert.assertFalse(hasLimit);
|
||||
Assert.assertFalse(hasOffset);
|
||||
}
|
||||
@@ -103,9 +92,6 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
private void rob() {
|
||||
ResetBasicData.reset();
|
||||
|
||||
SpiEbeanServer server = (SpiEbeanServer) Ebean.getServer(null);
|
||||
boolean h2Db = "h2".equals(server.getDatabasePlatform().getName());
|
||||
|
||||
Query<Order> query = Ebean.find(Order.class)
|
||||
.setAutoTune(false)
|
||||
.fetch("details")
|
||||
@@ -126,7 +112,7 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
Assert.assertTrue(hasDetailsJoin);
|
||||
Assert.assertFalse(hasSelectedDetails);
|
||||
Assert.assertTrue(hasDistinct);
|
||||
if (h2Db) {
|
||||
if (isH2()) {
|
||||
Assert.assertTrue(hasLimit);
|
||||
}
|
||||
|
||||
@@ -146,7 +132,7 @@ public class TestLimitQuery extends BaseTestCase {
|
||||
Assert.assertFalse("no join with maxRows", hasDetailsJoin);
|
||||
Assert.assertFalse(hasSelectedDetails);
|
||||
Assert.assertFalse(hasDistinct);
|
||||
if (h2Db) {
|
||||
if (isH2()) {
|
||||
Assert.assertTrue(hasLimit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.avaje.tests.compositekeys;
|
||||
|
||||
import com.avaje.ebean.config.dbplatform.H2Platform;
|
||||
|
||||
public class ModifiedH2Platform extends H2Platform {
|
||||
|
||||
public ModifiedH2Platform() {
|
||||
super();
|
||||
this.idInExpandedForm = true;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.avaje.tests.query.sqlquery;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebean.Ebean;
|
||||
import com.avaje.ebean.QueryEachConsumer;
|
||||
import com.avaje.ebean.QueryEachWhileConsumer;
|
||||
@@ -7,13 +8,87 @@ import com.avaje.ebean.SqlQuery;
|
||||
import com.avaje.ebean.SqlRow;
|
||||
import com.avaje.tests.model.basic.Order;
|
||||
import com.avaje.tests.model.basic.ResetBasicData;
|
||||
import org.avaje.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SqlQueryTests {
|
||||
public class SqlQueryTests extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void firstRowMaxRows() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
SqlQuery sqlQuery = Ebean.createSqlQuery("Select * from o_order");
|
||||
sqlQuery.setFirstRow(3);
|
||||
sqlQuery.setMaxRows(10);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
List<SqlRow> list = sqlQuery.findList();
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql.get(0)).contains("Select * from o_order limit 10 offset 3; --bind()");
|
||||
assertThat(list).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstRow() {
|
||||
|
||||
if (isPostgres()) {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
SqlQuery sqlQuery = Ebean.createSqlQuery("Select * from o_order order by id");
|
||||
sqlQuery.setFirstRow(3);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
sqlQuery.findList();
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql.get(0)).contains("Select * from o_order order by id offset 3");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maxRows() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
SqlQuery sqlQuery = Ebean.createSqlQuery("Select * from o_order order by id");
|
||||
sqlQuery.setMaxRows(10);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
sqlQuery.findList();
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql.get(0)).contains("Select * from o_order order by id limit 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findEachMaxRows() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
SqlQuery sqlQuery = Ebean.createSqlQuery("Select * from o_order");
|
||||
sqlQuery.setMaxRows(10);
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
sqlQuery.findEach(new QueryEachConsumer<SqlRow>() {
|
||||
@Override
|
||||
public void accept(SqlRow bean) {
|
||||
bean.get("id");
|
||||
}
|
||||
});
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
|
||||
assertThat(sql.get(0)).contains("limit 10");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void findEach() {
|
||||
|
||||
Reference in New Issue
Block a user