Skip to content

Commit

Permalink
Merge pull request #903 from FredDeschenes/bindbean-use-public-fields
Browse files Browse the repository at this point in the history
Use public fields as well as getters for 'bindBean'
  • Loading branch information
qualidafial committed Oct 4, 2017
2 parents 706213a + 50b03e5 commit 661bcd2
Show file tree
Hide file tree
Showing 4 changed files with 52 additions and 11 deletions.
Expand Up @@ -17,6 +17,7 @@
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
Expand Down Expand Up @@ -101,6 +102,36 @@ public Optional<Argument> find(String name, StatementContext ctx)
}
}
}

try
{
for (Field field : bean.getClass().getFields())
{
if (field.getName().equals(propertyName))
{
Object fieldValue = field.get(bean);
Type fieldType = field.getGenericType();
Optional<Argument> argument = ctx.findArgumentFor(fieldType, fieldValue);

if (!argument.isPresent())
{
throw new UnableToCreateStatementException(
String.format("No argument factory registered for type [%s] for field [%s] on [%s]",
fieldType,
propertyName,
bean), ctx);
}

return argument;
}
}
}
catch (IllegalAccessException e)
{
throw new UnableToCreateStatementException(String.format("Access exception getting field for " +
"bean property [%s] on [%s]",
propertyName, bean), e, ctx);
}
}
return Optional.empty();
}
Expand Down
3 changes: 2 additions & 1 deletion docs/src/adoc/index.adoc
Expand Up @@ -1464,7 +1464,8 @@ In SQL Object (but not in Core), you can qualify a bound map with a prefix:
void insert(@BindMap("user") Map<String, ?> map);
----
You can bind from the properties of a Java Bean:
You can bind from the properties or public fields of a Java Bean
(properties are prioritized if both are present):
[source,java]
----
Expand Down
Expand Up @@ -21,15 +21,15 @@
import org.jdbi.v3.sqlobject.customizer.internal.BindBeanFactory;

/**
* Binds the properties of a JavaBean to a SQL statement.
* Binds the properties and public fields of a JavaBean to a SQL statement.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER})
@SqlStatementCustomizingAnnotation(BindBeanFactory.class)
public @interface BindBean
{
/**
* Prefix to apply to each bean property name. If specified, properties will be bound as
* Prefix to apply to each bean property/field name. If specified, properties will be bound as
* {@code prefix.propertyName}.
*
* @return the prefix
Expand Down
25 changes: 17 additions & 8 deletions sqlobject/src/test/java/org/jdbi/v3/sqlobject/TestBindBean.java
Expand Up @@ -80,31 +80,36 @@ public interface Dao {

@Test
public void testNoArgumentFactoryRegisteredForProperty() {
handle.execute("create table beans (id integer, value_type varchar)");
handle.execute("create table beans (id integer, value_type varchar, fromField varchar, fromGetter varchar)");

assertThatThrownBy(() -> handle.attach(BeanDao.class).insert(new Bean(1, ValueType.valueOf("foo"))))
assertThatThrownBy(() -> handle.attach(BeanDao.class).insert(new Bean(1, ValueType.valueOf("foo"), "fooField", "fooGetter")))
.hasMessageContaining("No argument factory registered");
}

@Test
public void testArgumentFactoryRegisteredForProperty() {
handle.execute("create table beans (id integer, value_type varchar)");
handle.execute("create table beans (id integer, value_type varchar, fromField varchar, fromGetter varchar)");
handle.registerArgument(new ValueTypeArgumentFactory());

BeanDao dao = handle.attach(BeanDao.class);

dao.insert(new Bean(1, ValueType.valueOf("foo")));
assertThat(dao.getById(1)).extracting(Bean::getId, Bean::getValueType)
.containsExactly(1, ValueType.valueOf("foo"));
dao.insert(new Bean(1, ValueType.valueOf("foo"), "fooField", "fooGetter"));
assertThat(dao.getById(1)).extracting(Bean::getId, Bean::getValueType, bean -> bean.fromField, Bean::getFromGetter)
.containsExactly(1, ValueType.valueOf("foo"), "fooField", "fooGetter");
}

public static class Bean {
private int id;
private ValueType valueType;
public String fromField;
public final String fromGetter = "ACCESSED FROM FIELD";
private String actuallyFromGetter;

public Bean(int id, ValueType valueType) {
public Bean(int id, ValueType valueType, String fromField, String fromGetter) {
this.id = id;
this.valueType = valueType;
this.fromField = fromField;
this.actuallyFromGetter = fromGetter;
}

public int getId() {
Expand All @@ -114,6 +119,10 @@ public int getId() {
public ValueType getValueType() {
return valueType;
}

public String getFromGetter() {
return actuallyFromGetter;
}
}

public static class ValueTypeArgumentFactory extends AbstractArgumentFactory<ValueType> {
Expand All @@ -128,7 +137,7 @@ protected Argument build(ValueType value, ConfigRegistry config) {
}

public interface BeanDao {
@SqlUpdate("insert into beans (id, value_type) values (:id, :valueType)")
@SqlUpdate("insert into beans (id, value_type, fromField, fromGetter) values (:id, :valueType, :fromField, :fromGetter)")
void insert(@BindBean Bean bean);

@SqlQuery("select * from beans where id = :id")
Expand Down

0 comments on commit 661bcd2

Please sign in to comment.