Skip to content

Commit

Permalink
DATAJPA-1301 - Improved null handling for TupleBackedMap.
Browse files Browse the repository at this point in the history
TupleBackedMap now properly distinguishes between tuple elements with a null value and non-existing elements.

Original pull request: #262.
  • Loading branch information
schauder authored and odrotbohm committed Mar 27, 2018
1 parent 7272f9d commit 4c0a3bc
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 6 deletions.
Expand Up @@ -317,6 +317,7 @@ public Object convert(Object source) {
*/
private static class TupleBackedMap implements Map<String, Object> {

public static final String UNMODIFIABLE_MESSAGE = "A TupleBackedMap cannot be modified.";
private final Tuple tuple;

TupleBackedMap(Tuple tuple) {
Expand All @@ -333,39 +334,68 @@ public boolean isEmpty() {
return tuple.getElements().isEmpty();
}

/**
* If the key is not a {@code String} or not a key of the backing {@link Tuple} this returns {@code false}.
* Otherwise this returns {@code true} even when the value from the backing {@code Tuple} is {@code null}.
*
* @param key the key for which to get the value from the map.
* @return wether the key is an element of the backing tuple.
*/
@Override
public boolean containsKey(Object key) {
return key instanceof String && tuple.get((String) key) != null;

try {
tuple.get((String) key);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}

@Override
public boolean containsValue(Object value) {
return Arrays.asList(tuple.toArray()).contains(value);
}

/**
* If the key is not a {@code String} or not a key of the backing {@link Tuple} this returns {@code null}.
* Otherwise the value from the backing {@code Tuple} is returned, which also might be {@code null}.
*
* @param key the key for which to get the value from the map.
* @return the value of the backing {@link Tuple} for that key or {@code null}.
*/
@Override
public Object get(Object key) {
return key instanceof String ? tuple.get((String) key) : null;

if (!(key instanceof String)) {
return null;
}

try {
return tuple.get((String) key);
} catch (IllegalArgumentException e) {
return null;
}
}

@Override
public Object put(String key, Object value) {
throw new UnsupportedOperationException("A TupleBackedMap cannot be modified");
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}

@Override
public Object remove(Object key) {
throw new UnsupportedOperationException("A TupleBackedMap cannot be modified");
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}

@Override
public void putAll(Map<? extends String, ?> m) {
throw new UnsupportedOperationException("A TupleBackedMap cannot be modified");
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}

@Override
public void clear() {
throw new UnsupportedOperationException("A TupleBackedMap cannot be modified");
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
}

@Override
Expand Down
Expand Up @@ -99,6 +99,13 @@ public void queryProvidesCorrectNumberOfParametersForNativeQuery() {
@Override
public void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() {}

/**
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=525319 is fixed.
*/
@Override
@Test // DATAJPA-1301
public void returnsNullValueInMap() {}

/**
* TODO: Remove, once https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 is fixed.
*/
Expand Down
Expand Up @@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -2235,6 +2236,27 @@ public void bindsNativeQueryResultsToProjectionByName() {
assertThat(element.getLastname(), is("Arrasz"));
}

@Test // DATAJPA-1301
public void returnsNullValueInMap() {

Assume
.assumeTrue(getHibernateVersion().isGreaterThanOrEqualTo(HIBERNATE_VERSION_SUPPORTING_TUPLE_ON_NATIVE_QUERIES));

firstUser.setLastname(null);
flushTestUsers();

Map<String, Object> map = repository.findMapWithNullValues();

assertThat(map.keySet(), contains("firstname", "lastname"));
assertThat(map.containsKey("firstname"), is(true));
assertThat(map.containsKey("lastname"), is(true));

assertThat(map.get("firstname"), is((Object) "Oliver"));
assertThat(map.get("lastname"), is(nullValue()));
assertThat(map.get("non-existent"), is(nullValue()));
assertThat(map.get(new Object()), is(nullValue()));
}

private Page<User> executeSpecWithSort(Sort sort) {

flushTestUsers();
Expand Down
Expand Up @@ -18,6 +18,7 @@
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

Expand Down Expand Up @@ -489,6 +490,10 @@ List<User> findUsersByFirstnameForSpELExpressionWithParameterIndexOnlyWithEntity
// DATAJPA-1273
List<NameOnly> findByNamedQueryWithAliasInInvertedOrder();

// DATAJPA-1301
@Query("select firstname as firstname, lastname as lastname from User u where u.firstname = 'Oliver'")
Map<String, Object> findMapWithNullValues();

interface RolesAndFirstname {

String getFirstname();
Expand Down

0 comments on commit 4c0a3bc

Please sign in to comment.