#361 - ENH: Add ServerConfig expressionEqualsWithNullAsNoop - Can eq ne gt ... methods in ExpressionList treat null value as 1=1 ?

This commit is contained in:
Robin Bygrave
2015-07-30 06:41:51 +12:00
parent 27847424b3
commit 54f3dbf35a
7 changed files with 180 additions and 6 deletions
@@ -324,6 +324,11 @@ public class ServerConfig {
private Object objectMapper;
private boolean diffFlatMode;
/**
* Set to true if you want eq("someProperty", null) to generate 1=1 rather than "is null" sql expression.
*/
private boolean expressionEqualsWithNullAsNoop;
/**
* Construct a Server Configuration for programmatically creating an EbeanServer.
*/
@@ -1902,6 +1907,7 @@ public class ServerConfig {
persistenceContextScope = PersistenceContextScope.valueOf(p.get("persistenceContextScope", "TRANSACTION"));
expressionEqualsWithNullAsNoop = p.getBoolean("expressionEqualsWithNullAsNoop", expressionEqualsWithNullAsNoop);
diffFlatMode = p.getBoolean("diffFlatMode", diffFlatMode);
asOfViewSuffix = p.get("asOfViewSuffix", asOfViewSuffix);
asOfSysPeriod = p.get("asOfSysPeriod", asOfSysPeriod);
@@ -2029,4 +2035,23 @@ public class ServerConfig {
public void setDiffFlatMode(boolean diffFlatMode) {
this.diffFlatMode = diffFlatMode;
}
/**
* Return true if eq("someProperty", null) should to generate "1=1" rather than "is null" sql expression.
*/
public boolean isExpressionEqualsWithNullAsNoop() {
return expressionEqualsWithNullAsNoop;
}
/**
* Set to true if you want eq("someProperty", null) to generate "1=1" rather than "is null" sql expression.
* <p>
* Setting this to true has the effect that eq(propertyName, value), ieq(propertyName, value) and
* ne(propertyName, value) have no effect when the value is null. The expression factory adds a NoopExpression
* which will add "1=1" into the SQL rather than "is null".
* </p>
*/
public void setExpressionEqualsWithNullAsNoop(boolean expressionEqualsWithNullAsNoop) {
this.expressionEqualsWithNullAsNoop = expressionEqualsWithNullAsNoop;
}
}
@@ -106,7 +106,7 @@ public class InternalConfiguration {
this.cacheManager = cacheManager;
this.serverConfig = serverConfig;
this.bootupClasses = bootupClasses;
this.expressionFactory = new DefaultExpressionFactory();
this.expressionFactory = new DefaultExpressionFactory(serverConfig.isExpressionEqualsWithNullAsNoop());
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
this.binder = new Binder(typeManager);
@@ -22,8 +22,10 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
private static final Object[] EMPTY_ARRAY = new Object[] {};
private final boolean equalsWithNullAsNoop;
public DefaultExpressionFactory() {
public DefaultExpressionFactory(boolean equalsWithNullAsNoop) {
this.equalsWithNullAsNoop = equalsWithNullAsNoop;
}
public ExpressionFactory createExpressionFactory(){
@@ -39,7 +41,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
public Expression eq(String propertyName, Object value) {
if (value == null) {
return isNull(propertyName);
return equalsWithNullAsNoop ? NoopExpression.INSTANCE : isNull(propertyName);
}
return new SimpleExpression(propertyName, SimpleExpression.Op.EQ, value);
}
@@ -49,7 +51,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
public Expression ne(String propertyName, Object value) {
if (value == null) {
return isNotNull(propertyName);
return equalsWithNullAsNoop ? NoopExpression.INSTANCE : isNotNull(propertyName);
}
return new SimpleExpression(propertyName, SimpleExpression.Op.NOT_EQ, value);
}
@@ -60,7 +62,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
*/
public Expression ieq(String propertyName, String value) {
if (value == null) {
return isNull(propertyName);
return equalsWithNullAsNoop ? NoopExpression.INSTANCE : isNull(propertyName);
}
return new CaseInsensitiveEqualExpression(propertyName, value);
}
@@ -0,0 +1,47 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.event.BeanQueryRequest;
import com.avaje.ebeaninternal.api.HashQueryPlanBuilder;
import com.avaje.ebeaninternal.api.ManyWhereJoins;
import com.avaje.ebeaninternal.api.SpiExpression;
import com.avaje.ebeaninternal.api.SpiExpressionRequest;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
/**
* Effectively an expression that has no effect.
*/
class NoopExpression implements SpiExpression {
protected static final NoopExpression INSTANCE = new NoopExpression();
@Override
public void containsMany(BeanDescriptor<?> desc, ManyWhereJoins whereManyJoins) {
// nothing to do
}
@Override
public void queryAutoFetchHash(HashQueryPlanBuilder builder) {
builder.add(NoopExpression.class);
}
@Override
public void queryPlanHash(BeanQueryRequest<?> request, HashQueryPlanBuilder builder) {
queryAutoFetchHash(builder);
}
@Override
public int queryBindHash() {
// no bind values
return 0;
}
@Override
public void addSql(SpiExpressionRequest request) {
request.append("1=1");
}
@Override
public void addBindValues(SpiExpressionRequest request) {
// nothing to do
}
}
@@ -0,0 +1,58 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.Expression;
import org.junit.Test;
import static org.assertj.core.api.StrictAssertions.assertThat;
public class DefaultExpressionFactoryTest {
@Test
public void testEq() throws Exception {
DefaultExpressionFactory factory = new DefaultExpressionFactory(false);
Expression expression = factory.eq("name", null);
assertThat(expression).isInstanceOf(NullExpression.class);
}
@Test
public void testNe() throws Exception {
DefaultExpressionFactory factory = new DefaultExpressionFactory(false);
Expression expression = factory.ne("name", null);
assertThat(expression).isInstanceOf(NullExpression.class);
}
@Test
public void testIeq() throws Exception {
DefaultExpressionFactory factory = new DefaultExpressionFactory(false);
Expression expression = factory.ieq("name", null);
assertThat(expression).isInstanceOf(NullExpression.class);
}
@Test
public void testEq_with_equalsWithNullAsNoop() throws Exception {
DefaultExpressionFactory factory = new DefaultExpressionFactory(true);
Expression expression = factory.eq("name", null);
assertThat(expression).isInstanceOf(NoopExpression.class);
}
@Test
public void testNe_with_equalsWithNullAsNoop() throws Exception {
DefaultExpressionFactory factory = new DefaultExpressionFactory(true);
Expression expression = factory.ne("name", null);
assertThat(expression).isInstanceOf(NoopExpression.class);
}
@Test
public void testIeq_with_equalsWithNullAsNoop() throws Exception {
DefaultExpressionFactory factory = new DefaultExpressionFactory(true);
Expression expression = factory.ieq("name", null);
assertThat(expression).isInstanceOf(NoopExpression.class);
}
}
@@ -0,0 +1,42 @@
package com.avaje.ebeaninternal.server.expression;
import com.avaje.ebean.BaseTestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.tests.model.basic.Customer;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
public class NoopExpressionQueryTest extends BaseTestCase {
@Test
public void test() {
Query<Customer> query = Ebean.find(Customer.class)
.select("id")
.where().add(NoopExpression.INSTANCE)
.query();
query.findList();
String generatedSql = query.getGeneratedSql();
assertThat(generatedSql).contains("select t0.id c0 from o_customer t0 where 1=1");
}
@Test
public void test_withPreAndPost() {
Query<Customer> query = Ebean.find(Customer.class)
.select("id")
.where().eq("name", null)
.add(NoopExpression.INSTANCE)
.ne("status", null)
.query();
query.findList();
String generatedSql = query.getGeneratedSql();
assertThat(generatedSql).contains("select t0.id c0 from o_customer t0 where t0.name is null and 1=1 and t0.status is not null");
}
}
@@ -87,7 +87,7 @@ public class TestQueryLanguage extends BaseTestCase {
OrmQueryDetailParser p = new OrmQueryDetailParser(q);
p.parse();
DefaultOrmQuery<Order> qry = new DefaultOrmQuery<Order>(Order.class, server,
new DefaultExpressionFactory(), (String) null);
new DefaultExpressionFactory(false), (String) null);
p.assign(qry);
return qry;