Merge pull request #2996 from ebean-orm/buchtajz-query_select_bugs

Add support for specifying select clause with RawSql
This commit is contained in:
Rob Bygrave
2023-03-14 11:48:40 +13:00
committed by GitHub
8 changed files with 152 additions and 19 deletions
@@ -8,6 +8,7 @@ import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.core.OrmQueryRequest;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.querydefn.OrmQueryLimitRequest;
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.util.BindParamsParser;
@@ -56,7 +57,19 @@ final class CQueryBuilderRawSql {
private String buildMainQuery(String orderBy, OrmQueryRequest<?> request, CQueryPredicates predicates, SpiRawSql.Sql sql) {
StringBuilder sb = new StringBuilder();
sb.append(sql.getPreFrom());
OrmQueryProperties ormQueryProperties = request.query().getDetail().getChunk(null, false);
if (ormQueryProperties.hasSelectClause()) {
boolean first = true;
for (String selectProperty : ormQueryProperties.getIncluded()) {
if (!first) {
sb.append(", ");
}
sb.append(selectProperty);
first = false;
}
} else {
sb.append(sql.getPreFrom());
}
sb.append(" ");
String s = sql.getPreWhere();
@@ -591,10 +591,17 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
*/
@Override
public final void setSelectId() {
// clear select and fetch joins
detail.clear();
select(beanDescriptor.idSelect());
singleAttribute = true;
if (rawSql != null) {
String column = rawSql.mapToColumn(beanDescriptor.idSelect());
if (column != null) {
select(column);
}
} else {
// clear select and fetch joins
detail.clear();
select(beanDescriptor.idSelect());
singleAttribute = true;
}
}
@Override
@@ -60,4 +60,8 @@ public final class DRawSql implements SpiRawSql {
return columnMapping;
}
@Override
public String mapToColumn(String property) {
return columnMapping.mapToColumn(property);
}
}
@@ -5,13 +5,7 @@ import io.ebean.util.CamelCaseHelper;
import java.io.Serializable;
import java.sql.ResultSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.*;
/**
* Internal service API for Raw Sql.
@@ -31,6 +25,7 @@ public interface SpiRawSql extends RawSql {
SpiRawSql.ColumnMapping getColumnMapping();
String mapToColumn(String property);
/**
* Represents the sql part of the query. For parsed RawSql the sql is broken
@@ -363,7 +358,6 @@ public interface SpiRawSql extends RawSql {
* </p>
*/
public void tableAliasMapping(String tableAlias, String path) {
String startMatch = tableAlias + ".";
for (Map.Entry<String, Column> entry : dbColumnMap.entrySet()) {
if (entry.getKey().startsWith(startMatch)) {
@@ -372,6 +366,11 @@ public interface SpiRawSql extends RawSql {
}
}
public String mapToColumn(String property) {
final var column = propertyColumnMap.get(property);
return column == null ? null : column.getDbColumn();
}
/**
* A Column of the RawSql that is mapped to a bean property (or ignored).
*/
@@ -1,10 +1,10 @@
package org.tests.rawsql;
import io.ebean.*;
import io.ebean.xtest.BaseTestCase;
import io.ebean.xtest.IgnorePlatform;
import io.ebean.annotation.Platform;
import io.ebean.test.LoggedSql;
import io.ebean.xtest.BaseTestCase;
import io.ebean.xtest.IgnorePlatform;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.Customer;
import org.tests.model.basic.Order;
@@ -21,7 +21,6 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
@Test
public void test() {
ResetBasicData.reset();
RawSql rawSql = RawSqlBuilder.parse("select r.id, r.name from o_customer r ")
@@ -42,10 +41,37 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
LoggedSql.start();
assertThat(query.findCount()).isEqualTo(list.size());
List<String>sql = LoggedSql.stop();
assertThat(sql.get(0)).startsWith("select count(*) from ( select r.id, r.name from o_customer r");
assertThat(sql.get(0)).startsWith("select count(*) from ( select r.id from o_customer r");
assertThat(sql.get(0)).doesNotContain("order by");
}
@Test
public void testFindCount_when_idPropertyNotMapped() {
ResetBasicData.reset();
// mapping does not include the id column & property
RawSql rawSql = RawSqlBuilder.parse("select r.name, r.status from o_customer r ")
.columnMapping("r.name", "name")
.columnMapping("r.status", "status")
.create();
Query<Customer> query = DB.find(Customer.class);
query.setRawSql(rawSql);
query.where().ilike("name", "r%").orderBy("name");
LoggedSql.start();
List<Customer> list = query.findList();
assertNotNull(list);
// check also select count(*)
assertThat(query.findCount()).isEqualTo(list.size());
List<String>sql = LoggedSql.stop();
assertThat(sql.get(0)).startsWith("select r.name, r.status from o_customer r where lower(r.name) like ?");
assertThat(sql.get(0)).contains(" order by r.name");
assertThat(sql.get(1)).startsWith("select count(*) from ( select r.name, r.status from o_customer r where lower(r.name) like ?");
assertThat(sql.get(1)).doesNotContain("order by");
}
@Test
@IgnorePlatform({Platform.MYSQL, Platform.MARIADB, Platform.SQLSERVER})
public void test_upperCaseSql() {
@@ -158,7 +184,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
LoggedSql.start();
query.findCount();
List<String>sql = LoggedSql.stop();
assertThat(sql.get(0)).startsWith("select count(*) from ( select o.id, o.order_date, o.ship_date from o_order o");
assertThat(sql.get(0)).startsWith("select count(*) from ( select o.id from o_order o");
assertThat(sql.get(0)).doesNotContain("order by");
}
@@ -193,7 +219,7 @@ public class TestRawSqlOrmQuery extends BaseTestCase {
LoggedSql.start();
query.findCount();
List<String>sql = LoggedSql.stop();
assertThat(sql.get(0)).startsWith("select count(*) from ( select o.id, o.order_date, o.ship_date from o_order o");
assertThat(sql.get(0)).startsWith("select count(*) from ( select o.id from o_order o");
assertThat(sql.get(0)).doesNotContain("order by");
}
@@ -0,0 +1,42 @@
package org.tests.rawsql;
import io.ebean.DB;
import io.ebean.FetchGroup;
import io.ebean.RawSql;
import io.ebean.RawSqlBuilder;
import io.ebean.xtest.BaseTestCase;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.ResetBasicData;
import org.tests.rawsql.transport.QuerySumResponse;
import org.tests.rawsql.transport.SampleReport;
import java.util.List;
public class TestRawSqlQuerySelect extends BaseTestCase {
@Test
public void testRawSQL() {
ResetBasicData.reset();
RawSql rawSql = RawSqlBuilder.parse("select id, name, anniversary, city from (select c.id, c.name, c.anniversary, a.city from o_customer c left join o_address a ON a.id = c.billing_address_id order by c.id) w").create();
String sumSql = "COUNT(1)::BIGINT AS count, SUM(id)::DOUBLE PRECISION AS sum";
var query = DB.find(SampleReport.class);
query.setRawSql(rawSql);
query.setMaxRows(10);
List<SampleReport> list = query.findList();
Assertions.assertNotNull(list);
Assertions.assertTrue(!list.isEmpty() && list.size() < 11);
var countSum = DB.find(SampleReport.class)
.setRawSql(rawSql)
//.select(sumSql)
.select(FetchGroup.of(SampleReport.class, sumSql))
.setMaxRows(10)
.asDto(QuerySumResponse.class)
.findOne();
Assertions.assertInstanceOf(QuerySumResponse.class, countSum);
}
}
@@ -0,0 +1,30 @@
package org.tests.rawsql.transport;
public class QuerySumResponse {
private long count;
private Double sum;
public QuerySumResponse() {
}
public QuerySumResponse(long count, Double sum) {
this.count = count;
this.sum = sum;
}
public long getCount() {
return count;
}
public void setCount(long count) {
this.count = count;
}
public Double getSum() {
return sum;
}
public void setSum(Double sum) {
this.sum = sum;
}
}
@@ -0,0 +1,12 @@
package org.tests.rawsql.transport;
import javax.persistence.Entity;
import java.sql.Date;
@Entity
public class SampleReport {
Long id;
String name;
Date anniversary;
String city;
}