/*
* 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 org.jdbi.v3.sqlobject;
import static org.assertj.core.api.Assertions.assertThat;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.lang.reflect.Type;
import java.util.Optional;
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.jdbi.v3.core.Something;
import org.jdbi.v3.core.mapper.SomethingMapper;
import org.jdbi.v3.core.rule.H2DatabaseRule;
import org.jdbi.v3.sqlobject.config.RegisterRowMapper;
import org.jdbi.v3.sqlobject.customizer.BindBean;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizerFactory;
import org.jdbi.v3.sqlobject.customizer.SqlStatementCustomizingAnnotation;
import org.jdbi.v3.sqlobject.customizer.SqlStatementParameterCustomizer;
import org.jdbi.v3.sqlobject.statement.SqlBatch;
import org.jdbi.v3.sqlobject.statement.SqlQuery;
import org.junit.Rule;
import org.junit.Test;
import com.google.common.collect.ImmutableMap;
public class TestBindExpression
{
@Rule
public H2DatabaseRule dbRule = new H2DatabaseRule().withPlugin(new SqlObjectPlugin());
@RegisterRowMapper(SomethingMapper.class)
public interface DB
{
@SqlBatch("insert into something (id, name) values(:id, :name)")
void insert(@BindBean Something... things);
@SqlQuery("select id, name from something where name = :breakfast.waffle.topping limit 1")
Something findByBreakfast(@BindRoot("breakfast") Breakfast b);
}
@Test
public void testExpression() throws Exception
{
DB db = dbRule.getSharedHandle().attach(DB.class);
db.insert(new Something(1, "syrup"), new Something(2, "whipped cream"));
Something with_syrup = db.findByBreakfast(new Breakfast());
assertThat(with_syrup).isEqualTo(new Something(1, "syrup"));
}
@Retention(RetentionPolicy.RUNTIME)
@SqlStatementCustomizingAnnotation(BindRoot.BindExpressionCustomizerFactory.class)
public @interface BindRoot
{
String value();
class BindExpressionCustomizerFactory implements SqlStatementCustomizerFactory
{
@Override
public SqlStatementParameterCustomizer createForParameter(Annotation annotation,
Class<?> sqlObjectType,
Method method,
Parameter param,
int index,
Type type)
{
final String rootName = ((BindRoot) annotation).value();
final JexlEngine engine = new JexlEngine();
return (q, root) -> q.bindNamedArgumentFinder(name -> {
Expression e = engine.createExpression(name);
final Object it = e.evaluate(new MapContext(ImmutableMap.of(rootName, root)));
return it == null
? Optional.empty()
: Optional.of((position, statement, ctx) -> statement.setObject(position, it));
});
}
}
}
public static class Breakfast
{
private final Waffle waffle = new Waffle();
public Waffle getWaffle()
{
return waffle;
}
}
public static class Waffle
{
private final String topping = "syrup";
public String getTopping()
{
return topping;
}
}
@Test
public void testJexl() throws Exception
{
JexlEngine engine = new JexlEngine();
Object topping = engine.createExpression("breakfast.waffle.topping")
.evaluate(new MapContext(ImmutableMap.<String, Object>of("breakfast", new Breakfast())));
assertThat(topping).isEqualTo("syrup");
}
}