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
f27f9d37c7
commit
c8339a2292
@@ -325,6 +325,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
|||||||
*/
|
*/
|
||||||
private static class TupleBackedMap implements Map<String, Object> {
|
private static class TupleBackedMap implements Map<String, Object> {
|
||||||
|
|
||||||
|
public static final String UNMODIFIABLE_MESSAGE = "A TupleBackedMap cannot be modified.";
|
||||||
private final Tuple tuple;
|
private final Tuple tuple;
|
||||||
|
|
||||||
TupleBackedMap(Tuple tuple) {
|
TupleBackedMap(Tuple tuple) {
|
||||||
@@ -341,9 +342,22 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
|||||||
return tuple.getElements().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
|
@Override
|
||||||
public boolean containsKey(Object key) {
|
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
|
@Override
|
||||||
@@ -351,29 +365,46 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
|||||||
return Arrays.stream(tuple.toArray()).anyMatch(v -> v.equals(value));
|
return Arrays.stream(tuple.toArray()).anyMatch(v -> v.equals(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
|
@Override
|
||||||
|
@Nullable
|
||||||
public Object get(Object key) {
|
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
|
@Override
|
||||||
public Object put(String key, Object value) {
|
public Object put(String key, Object value) {
|
||||||
throw new UnsupportedOperationException("A TupleBakcedMap cannot be modified");
|
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object remove(Object key) {
|
public Object remove(Object key) {
|
||||||
throw new UnsupportedOperationException("A TupleBakcedMap cannot be modified");
|
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void putAll(Map<? extends String, ?> m) {
|
public void putAll(Map<? extends String, ?> m) {
|
||||||
throw new UnsupportedOperationException("A TupleBakcedMap cannot be modified");
|
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
throw new UnsupportedOperationException("A TupleBakcedMap cannot be modified");
|
throw new UnsupportedOperationException(UNMODIFIABLE_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -103,6 +103,14 @@ public class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserReposi
|
|||||||
@Test // DATAJPA-1248
|
@Test // DATAJPA-1248
|
||||||
public void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() {}
|
public void supportsProjectionsWithNativeQueriesAndCamelCaseProperty() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=525319 is fixed.
|
||||||
|
*/
|
||||||
|
@Ignore
|
||||||
|
@Override
|
||||||
|
@Test // DATAJPA-1301
|
||||||
|
public void returnsNullValueInMap() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: Remove, once https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 is fixed.
|
* TODO: Remove, once https://bugs.eclipse.org/bugs/show_bug.cgi?id=289141 is fixed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -51,8 +52,6 @@ import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
|||||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||||
import org.springframework.data.domain.Example;
|
import org.springframework.data.domain.Example;
|
||||||
import org.springframework.data.domain.ExampleMatcher;
|
import org.springframework.data.domain.ExampleMatcher;
|
||||||
import org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher;
|
|
||||||
import org.springframework.data.domain.ExampleMatcher.StringMatcher;
|
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
import org.springframework.data.domain.PageImpl;
|
import org.springframework.data.domain.PageImpl;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
@@ -61,6 +60,7 @@ import org.springframework.data.domain.Slice;
|
|||||||
import org.springframework.data.domain.Sort;
|
import org.springframework.data.domain.Sort;
|
||||||
import org.springframework.data.domain.Sort.Direction;
|
import org.springframework.data.domain.Sort.Direction;
|
||||||
import org.springframework.data.domain.Sort.Order;
|
import org.springframework.data.domain.Sort.Order;
|
||||||
|
import org.springframework.data.domain.ExampleMatcher.*;
|
||||||
import org.springframework.data.jpa.domain.Specification;
|
import org.springframework.data.jpa.domain.Specification;
|
||||||
import org.springframework.data.jpa.domain.sample.Address;
|
import org.springframework.data.jpa.domain.sample.Address;
|
||||||
import org.springframework.data.jpa.domain.sample.Role;
|
import org.springframework.data.jpa.domain.sample.Role;
|
||||||
@@ -2173,6 +2173,31 @@ public class UserRepositoryTests {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // DATAJPA-1301
|
||||||
|
public void returnsNullValueInMap() {
|
||||||
|
|
||||||
|
firstUser.setLastname(null);
|
||||||
|
flushTestUsers();
|
||||||
|
|
||||||
|
Map<String, Object> map = repository.findMapWithNullValues();
|
||||||
|
|
||||||
|
SoftAssertions softly = new SoftAssertions();
|
||||||
|
|
||||||
|
softly.assertThat(map.keySet()).containsExactlyInAnyOrder("firstname", "lastname");
|
||||||
|
|
||||||
|
softly.assertThat(map.containsKey("firstname")).isTrue();
|
||||||
|
softly.assertThat(map.containsKey("lastname")).isTrue();
|
||||||
|
|
||||||
|
softly.assertThat(map.get("firstname")).isEqualTo("Oliver");
|
||||||
|
softly.assertThat(map.get("lastname")).isNull();
|
||||||
|
|
||||||
|
softly.assertThat(map.get("non-existent")).isNull();
|
||||||
|
|
||||||
|
softly.assertThat(map.get(new Object())).isNull();
|
||||||
|
|
||||||
|
softly.assertAll();
|
||||||
|
}
|
||||||
|
|
||||||
private Page<User> executeSpecWithSort(Sort sort) {
|
private Page<User> executeSpecWithSort(Sort sort) {
|
||||||
|
|
||||||
flushTestUsers();
|
flushTestUsers();
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.sample;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@@ -539,6 +540,10 @@ public interface UserRepository
|
|||||||
// DATAJPA-1273
|
// DATAJPA-1273
|
||||||
List<NameOnly> findByNamedQueryWithAliasInInvertedOrder();
|
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 {
|
interface RolesAndFirstname {
|
||||||
|
|
||||||
String getFirstname();
|
String getFirstname();
|
||||||
|
|||||||
Reference in New Issue
Block a user