DATAJPA-1301 - Improved null handling for TupleBackedMap.
TupleBackedMap now properly distinguishes between tuple elements with a null value and non-existing elements. Original pull request: #262.
This commit is contained in:
committed by
Oliver Gierke
parent
7272f9de91
commit
4c0a3bcd81
@@ -317,6 +317,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
||||
*/
|
||||
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) {
|
||||
@@ -333,9 +334,22 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
||||
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
|
||||
@@ -343,29 +357,45 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
||||
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
|
||||
|
||||
@@ -99,6 +99,13 @@ public class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserReposi
|
||||
@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.
|
||||
*/
|
||||
|
||||
@@ -31,6 +31,7 @@ import java.util.Collection;
|
||||
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;
|
||||
@@ -2235,6 +2236,27 @@ public class UserRepositoryTests {
|
||||
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();
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.sample;
|
||||
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;
|
||||
|
||||
@@ -489,6 +490,10 @@ public interface UserRepository
|
||||
// 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();
|
||||
|
||||
Reference in New Issue
Block a user