Skip to content

Commit

Permalink
Generalized findMap for multiple columns.
Browse files Browse the repository at this point in the history
Previously only 2-column result-sets were supported, but now there can
be more: the first column is used for instantiating the key and the rest
are used to instantiate the value.

(Closes #2).
  • Loading branch information
komu committed May 4, 2015
1 parent afbe685 commit 413a769
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## x.y.z (yyyy-mm-dd)

- Generalized `findMap` to allow more than one column for instantiating the value. (Closes [#2](https://github.com/EvidentSolutions/dalesbred/issues/2))

## 1.0.0-alpha.2 (2015-05-04)

- Ignore underscores on Java fields and setters when resolving instantiators. (Fixes [#3](https://github.com/EvidentSolutions/dalesbred/issues/3))
Expand Down
8 changes: 4 additions & 4 deletions dalesbred/src/main/java/org/dalesbred/Database.java
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,8 @@ public long findUniqueLong(@NotNull @SQL String sql, Object... args) {
}

/**
* Executes a query that returns two values and creates a map from the results,
* using the first value as the key and second value as the value for that key.
* Executes a query that returns at least two values and creates a map from the results,
* using the first value as the key and rest of the values for instantiating {@code V}.
*/
@NotNull
public <K,V> Map<K, V> findMap(@NotNull Class<K> keyType,
Expand All @@ -492,8 +492,8 @@ public <K,V> Map<K, V> findMap(@NotNull Class<K> keyType,
}

/**
* Executes a query that returns two values and creates a map from the results,
* using the first value as the key and second value as the value for that key.
* Executes a query that returns at least two values and creates a map from the results,
* using the first value as the key and rest of the values for instantiating {@code V}
*/
@NotNull
public <K,V> Map<K, V> findMap(@NotNull Class<K> keyType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@
package org.dalesbred.result;

import org.dalesbred.UnexpectedResultException;
import org.dalesbred.instantiation.DefaultInstantiatorRegistry;
import org.dalesbred.instantiation.NamedTypeList;
import org.dalesbred.instantiation.TypeConversion;
import org.dalesbred.instantiation.*;
import org.dalesbred.internal.jdbc.ResultSetUtils;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -64,15 +62,25 @@ public Map<K, V> process(@NotNull ResultSet resultSet) throws SQLException {
Map<K,V> result = new LinkedHashMap<>();

NamedTypeList types = ResultSetUtils.getTypes(resultSet.getMetaData());
if (types.size() != 2)
throw new UnexpectedResultException("Expected ResultSet with 2 columns, but got " + types.size() + " columns.");
if (types.size() < 2)
throw new UnexpectedResultException("Expected ResultSet with at least 2 columns, but got " + types.size() + " columns.");

NamedTypeList valueTypes = types.subList(1, types.size());
TypeConversion<Object, K> keyConversion = getConversion(types.getType(0), keyType);
TypeConversion<Object, V> valueConversion = getConversion(types.getType(1), valueType);
Instantiator<V> valueInstantiator = instantiatorRegistry.findInstantiator(valueType, valueTypes);

// For performance reasons we reuse the same arguments-array and InstantiatorArguments-object for all rows.
// This should be fine as long as the instantiators don't hang on to their arguments for too long.
Object[] valueArguments = new Object[valueTypes.size()];
InstantiatorArguments instantiatorArguments = new InstantiatorArguments(valueTypes, valueArguments);

while (resultSet.next()) {
K key = keyConversion.convert(resultSet.getObject(1));
V value = valueConversion.convert(resultSet.getObject(2));

for (int i = 0; i < valueArguments.length; i++)
valueArguments[i] = resultSet.getObject(i+2);

V value = valueInstantiator.instantiate(instantiatorArguments);

result.put(key, value);
}
Expand Down
12 changes: 12 additions & 0 deletions dalesbred/src/test/java/org/dalesbred/DatabaseTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,18 @@ public void mapWithNullCoercion() {
assertThat(map.get(null), is("bar"));
}

@Test
public void mapWithMultipleArguments() {
Map<Integer, Department> map = db.findMap(Integer.class, Department.class,
"select * from (values (1, 10, 'foo'), (2, 20, 'bar')) d");

assertThat(map.size(), is(2));
assertThat(map.get(1).id, is(10));
assertThat(map.get(1).name, is("foo"));
assertThat(map.get(2).id, is(20));
assertThat(map.get(2).name, is("bar"));
}

@Test
public void findUnique_singleResult() {
assertThat(db.findUnique(Integer.class, "values (42)"), is(42));
Expand Down
5 changes: 5 additions & 0 deletions src/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ You can also convert the results directly to a map:
----
Map<Integer,String> namesByIds = db.findMap(
Integer.class, String.class, "select id, name from department");
// first column is used for key, rest for instantiating the value
Map<Integer,Department> departmentsByIds = db.findMap(
Integer.class, Department.class, "select id, id, name from department");
----

If for some reason you don't want to map the results into your own class, you can ask for
Expand Down

0 comments on commit 413a769

Please sign in to comment.