mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
feat: Add query.setHint() to support sql hint as inline comment in select queries (#3365)
* feat: Add query.setHint() to support sql hint as inline comment in select queries
This is for ORM queries only.
An ORM query like:
new QCustomer()
.setHint("FirstRows")
.select(QCustomer.Alias.id, QCustomer.Alias.name)
.findList();
Produces SQL that includes the hint as an inline comment like:
select /*+ FirstRows */ t0.id, t0.name
from customer t0
* Fix test QOrderTest
This commit is contained in:
@@ -1601,6 +1601,14 @@ public interface Query<T> extends CancelableQuery {
|
||||
*/
|
||||
Query<T> setLabel(String label);
|
||||
|
||||
/**
|
||||
* Set a SQL query hint.
|
||||
* <p>
|
||||
* This results in an inline comment that immediately follows
|
||||
* after the select keyword in the form: {@code /*+ hint *\/ }
|
||||
*/
|
||||
Query<T> setHint(String hint);
|
||||
|
||||
/**
|
||||
* Set to true if this query should execute against the doc store.
|
||||
* <p>
|
||||
|
||||
@@ -776,4 +776,8 @@ public class DatabasePlatform {
|
||||
}
|
||||
return "/* " + label + " */ ";
|
||||
}
|
||||
|
||||
public String inlineSqlHint(String hint) {
|
||||
return "/*+ " + hint + " */ ";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,6 +226,11 @@ public interface SpiQuery<T> extends Query<T>, SpiQueryFetch, TxnProfileEventCod
|
||||
*/
|
||||
ProfileLocation profileLocation();
|
||||
|
||||
/**
|
||||
* Return the SQL hint to include in the query.
|
||||
*/
|
||||
String hint();
|
||||
|
||||
/**
|
||||
* Return the label set on the query.
|
||||
*/
|
||||
|
||||
@@ -226,7 +226,7 @@ public final class BatchControl {
|
||||
void executeNow(ArrayList<PersistRequest> list) throws BatchedSqlException {
|
||||
boolean old = transaction.isFlushOnQuery();
|
||||
transaction.setFlushOnQuery(false);
|
||||
// disable flush on query due transsaction callbacks
|
||||
// disable flush on query due transaction callbacks
|
||||
try {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i % batchSize == 0) {
|
||||
|
||||
@@ -595,7 +595,7 @@ final class CQueryBuilder {
|
||||
}
|
||||
|
||||
private void appendSelectDistinct() {
|
||||
sb.append("select ").append(inlineSqlComment());
|
||||
sb.append("select ").append(hint()).append(inlineSqlComment());
|
||||
if (distinct && !countSingleAttribute) {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append("count(");
|
||||
@@ -608,6 +608,11 @@ final class CQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private String hint() {
|
||||
String hint = query.hint();
|
||||
return hint == null ? "" : dbPlatform.inlineSqlHint(hint);
|
||||
}
|
||||
|
||||
private String inlineSqlComment() {
|
||||
if (!includeLabelInSql) {
|
||||
return "";
|
||||
|
||||
@@ -133,6 +133,11 @@ final class DefaultFetchGroupQuery<T> implements SpiFetchGroupQuery<T>, SpiQuery
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query<T> setHint(String hint) {
|
||||
throw new RuntimeException("EB102: Only select() and fetch() clause is allowed on FetchGroup");
|
||||
}
|
||||
|
||||
// Everything else deemed invalid
|
||||
|
||||
@Override
|
||||
|
||||
@@ -57,6 +57,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
private ProfilingListener profilingListener;
|
||||
private Type type;
|
||||
private String label;
|
||||
private String hint;
|
||||
private Mode mode = Mode.NORMAL;
|
||||
private boolean usingFuture;
|
||||
private Object tenantId;
|
||||
@@ -235,6 +236,11 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String hint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String planLabel() {
|
||||
if (label != null) {
|
||||
@@ -258,6 +264,12 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Query<T> setHint(String hint) {
|
||||
this.hint = hint;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAutoTunable() {
|
||||
return nativeSql == null && beanDescriptor.isAutoTunable();
|
||||
@@ -732,6 +744,7 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
copy.timeout = timeout;
|
||||
copy.mapKey = mapKey;
|
||||
copy.id = id;
|
||||
copy.hint = hint;
|
||||
copy.label = label;
|
||||
copy.nativeSql = nativeSql;
|
||||
copy.useBeanCache = useBeanCache;
|
||||
@@ -1069,6 +1082,9 @@ public class DefaultOrmQuery<T> extends AbstractQuery implements SpiQuery<T> {
|
||||
if (manualId) {
|
||||
sb.append("/md");
|
||||
}
|
||||
if (hint != null) {
|
||||
sb.append("/h:").append(hint);
|
||||
}
|
||||
if (distinct) {
|
||||
sb.append("/dt");
|
||||
}
|
||||
|
||||
@@ -29,6 +29,27 @@ public class OrmQueryPlanKeyTest extends BaseTest {
|
||||
assertSame(query().createQueryPlanKey(), query().createQueryPlanKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_hintIsDifferent_expect_different() {
|
||||
DefaultOrmQuery<Customer> q1 = query();
|
||||
q1.setHint("a");
|
||||
assertDifferent(q1, query());
|
||||
|
||||
DefaultOrmQuery<Customer> q2 = query();
|
||||
q2.setHint("b");
|
||||
assertDifferent(q1, q2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_hintIsSame() {
|
||||
DefaultOrmQuery<Customer> q1 = query();
|
||||
q1.setHint("b");
|
||||
DefaultOrmQuery<Customer> q2 = query();
|
||||
q2.setHint("b");
|
||||
|
||||
assertSame(q1, q2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equals_when_diffTableJoinNull() {
|
||||
|
||||
|
||||
@@ -912,6 +912,17 @@ public abstract class TQRootBean<T, R> {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a SQL query hint.
|
||||
* <p>
|
||||
* This results in an inline comment that immediately follows
|
||||
* after the select keyword in the form: {@code /*+ hint *\/ }
|
||||
*/
|
||||
public R setHint(String hint) {
|
||||
query.setHint(hint);
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the profile location.
|
||||
* <p>
|
||||
|
||||
@@ -79,6 +79,19 @@ class QOrderTest {
|
||||
DB.delete(customer);
|
||||
}
|
||||
|
||||
@Test
|
||||
void hint() {
|
||||
LoggedSql.start();
|
||||
new QCustomer()
|
||||
.setHint("FirstRows")
|
||||
.select(QCustomer.Alias.id, QCustomer.Alias.name)
|
||||
.findList();
|
||||
|
||||
List<String> sql = LoggedSql.stop();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("select /*+ FirstRows */ /* QOrderTest.hint */ t0.id, t0.name from be_customer t0");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fetchQueryWithBatch() {
|
||||
LoggedSql.start();
|
||||
@@ -159,8 +172,7 @@ class QOrderTest {
|
||||
|
||||
@Test
|
||||
void viaFetchGraph_withNested_fetchQuery() {
|
||||
|
||||
DB.getDefault();
|
||||
DB.cacheManager().clearAll();
|
||||
LoggedSql.start();
|
||||
|
||||
final Order found = new QOrder()
|
||||
@@ -181,8 +193,7 @@ class QOrderTest {
|
||||
|
||||
@Test
|
||||
void viaFetchGraph_withNested_fetchCache() {
|
||||
|
||||
DB.getDefault();
|
||||
DB.cacheManager().clearAll();
|
||||
|
||||
// ensure the customer is loaded in the L2 cache
|
||||
new QCustomer().id.eq(customer.getId()).findOne();
|
||||
|
||||
@@ -164,6 +164,7 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
|
||||
DtoQuery<ContactDto> query = DB.find(Contact.class)
|
||||
// we must explicitly add the id property for DTO query (if we want it)
|
||||
.setHint("SomeHint")
|
||||
.select("id, email, " + concat("lastName", ", ", "firstName") + " as fullName")
|
||||
.where()
|
||||
.isNotNull("email")
|
||||
@@ -183,7 +184,7 @@ public class DtoQueryFromOrmTest extends BaseTestCase {
|
||||
}
|
||||
|
||||
List<String> sql = LoggedSql.stop();
|
||||
assertSql(sql.get(0)).contains("select /* explicitId */ t0.id, t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
assertSql(sql.get(0)).contains("select /*+ SomeHint */ /* explicitId */ t0.id, t0.email, " + concat("t0.last_name", ", ", "t0.first_name")
|
||||
+ " fullName from contact t0 where t0.email is not null and t0.last_name is not null order by t0.last_name");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user