Skip to content

Commit

Permalink
Consider Tuples in Keyset extraction.
Browse files Browse the repository at this point in the history
We now consider Tuple values when the query uses tuples for e.g. interface projections when extracting keyset values.

Closes #3077
  • Loading branch information
mp911de committed Jul 20, 2023
1 parent d6bcdf3 commit 5f84e2a
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import jakarta.persistence.IdClass;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.Tuple;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.IdentifiableType;
Expand All @@ -34,6 +35,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;

import org.springframework.beans.BeanWrapper;
import org.springframework.core.annotation.AnnotationUtils;
Expand Down Expand Up @@ -154,6 +156,11 @@ public ID getId(T entity) {

// If it's a simple type, then immediately delegate to the provider
if (idMetadata.hasSimpleId()) {

if (entity instanceof Tuple t) {
return (ID) t.get(idMetadata.getSimpleIdAttribute().getName());
}

return (ID) persistenceUnitUtil.getIdentifier(entity);
}

Expand Down Expand Up @@ -225,27 +232,38 @@ public boolean isNew(T entity) {
@Override
public Map<String, Object> getKeyset(Iterable<String> propertyPaths, T entity) {

// TODO: Proxy handling requires more elaborate refactoring, see
// https://github.com/spring-projects/spring-data-jpa/issues/2784
BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
Function<String, Object> getter = getPropertyValueFunction(entity);

Map<String, Object> keyset = new LinkedHashMap<>();

if (hasCompositeId()) {
for (String idAttributeName : getIdAttributeNames()) {
keyset.put(idAttributeName, entityWrapper.getPropertyValue(idAttributeName));
keyset.put(idAttributeName, getter.apply(idAttributeName));
}
} else {
keyset.put(getIdAttribute().getName(), getId(entity));
}

for (String propertyPath : propertyPaths) {
keyset.put(propertyPath, entityWrapper.getPropertyValue(propertyPath));
keyset.put(propertyPath, getter.apply(propertyPath));
}

return keyset;
}

private Function<String, Object> getPropertyValueFunction(Object entity) {

if (entity instanceof Tuple t) {
return t::get;
}

// TODO: Proxy handling requires more elaborate refactoring, see
// https://github.com/spring-projects/spring-data-jpa/issues/2784
BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity);

return entityWrapper::getPropertyValue;
}

/**
* Simple value object to encapsulate id specific metadata.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,17 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.ScrollPosition;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Window;
import org.springframework.data.jpa.domain.sample.Role;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.repository.sample.RoleRepository;
import org.springframework.data.jpa.repository.sample.UserRepository;
import org.springframework.data.jpa.repository.sample.UserRepository.IdOnly;
import org.springframework.data.jpa.repository.sample.UserRepository.NameOnly;
import org.springframework.data.jpa.repository.sample.UserRepository.RolesAndFirstname;
import org.springframework.data.repository.query.QueryLookupStrategy;
import org.springframework.test.context.ContextConfiguration;
Expand Down Expand Up @@ -221,6 +224,19 @@ void executesQueryToSliceWithUnpaged() {
assertThat(slice.hasNext()).isFalse();
}

@Test // GH-3077
void shouldProjectWithKeysetScrolling() {

Window<NameOnly> first = userRepository.findTop1ByLastnameOrderByFirstname(ScrollPosition.keyset(), //
"Matthews");

Window<NameOnly> next = userRepository.findTop1ByLastnameOrderByFirstname(first.positionAt(0), //
"Matthews");

assertThat(first.getContent()).extracting(NameOnly::getFirstname).containsOnly(dave.getFirstname());
assertThat(next.getContent()).extracting(NameOnly::getFirstname).containsOnly(oliver.getFirstname());
}

@Test // DATAJPA-830
void executesMethodWithNotContainingOnStringCorrectly() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,8 @@ Window<User> findTop3ByFirstnameStartingWithOrderByFirstnameAscEmailAddressAsc(S

Page<User> findByLastnameIgnoringCase(Pageable pageable, String lastname);

Window<NameOnly> findTop1ByLastnameOrderByFirstname(ScrollPosition scrollPosition, String lastname);

List<User> findByLastnameIgnoringCaseLike(String lastname);

List<User> findByLastnameAndFirstnameAllIgnoringCase(String lastname, String firstname);
Expand Down

0 comments on commit 5f84e2a

Please sign in to comment.