We now avoid the allocation of an Optional instance in the lookup of a dynamic projection in ParameterAccessor. Also, Lazy now exposes a ….getNullable() to be favored over ….getOptional() for hot code paths.
Replaced Stream and Optional usage with for-loops and nullable return values. Reuse parameter names to avoid repeated annotation lookups. Reuse result from Parameters.getBindable(). Introduce ParametersParameterAccessor.getValues() to consistently reuse a single accessor instance allowing access to the unwrapped values. Introduce type cache to QueryExecutionConverters to quickly reject types that do not require wrapping. Avoid recalculation of QueryMethod.isCollectionQuery().
Replace AtTest(expected = …) and ExpectedException with the corresponding AssertJ assertThatExceptionOfType(…) and assertThatIllegalArgumentException().isThrownBy(…).
Before this commit we haven't properly resolved methods on a root object provided by an EvaluationContextExtension that was using varargs. With a vararg method, the number of parameters handed into the method is not necessary equal to the number of parameters. We previously simply skipped methods with a different number of arguments. We now try direct matches first but calculate valid varargs alternatives in case that initial lookup fails and try to match those alternatives.
This lookup is implemented in ….util.ParameterTypes now and used by ….spel.spi.Function. The latter now also handles the actual invocation of those methods properly by collecting the trailing arguments into an array.
Introduced MethodInvocationRecorder to record method invocations on types to obtain the property traversal those invocations represent.
Recorded<ZipCode> recorded = MethodInvocationRecorder.forProxyOf(Person.class)
.record(Person::getAddress)
.record(Address::getZipCode);
assertThat(recorded.getPropertyPath)).hasValue("address.zipCode");
Added Streamable.toStreamable() and ….toStreamable(Collector) to allow the creation of a Streamable from streams wither using a default List-based intermediate collector (former) or providing an explicit one (latter).
Introduced ProxyUtils.getUserClass(…) that by default is basically a facade for Spring's ClassUtils.getUserClass(…) but allows the registration of ProxyDetector implementations via Spring's SpringFactoriesLoader mechanism.
Moved all existing usages of ClassUtils.getUserClass(…) to ProxyUtils.
Expanded generics to make sure we can take Functions and Suppliers of sub- and supertypes properly. Added factory method to create a Lazy from a fixed value. Added method to access Optional result. Added methods to allow chaining Lazy instances via ….or(…). Reduced visibility of ….orElseGet(…) as it basically equates to ….or(…).get(Optional)().
More unit tests.
Introduced BeanLookup to easily create a Lazy<T>-based lookup of a unique bean by type on a ListableBeanFactory. This is in support of making it easy for downstream Spring Data modules to consume the EntityPathResolver declared in an ApplicationContext using XML configuration.
QuerydslWebConfiguration now uses a plain ObjectProvider to access the bean defined falling back to our default.
Original pull request: #265.
We now eagerly resolve a generics declaration chain, which we previously - erroneously - expected GenericTypeResolver to do for us. Simplified TypeVariableTypeInformation implementation. Renamed ParameterizedTypeUnitTests to ParameterizedTypeInformationUnitTests.
We now only inspect regular Kotlin classes with inspection to adapt Kotlin-specific behavior. Multipart-, synthetic and unknown classes are not supported. In such cases we fall back to the JVM reflection mechanism.
Non-regular classes are typically synthetic stubs, lambdas and SAM conversion which do not represent typical domain objects but rather technical bridge code.
Original pull request: #245.
We now validate query method invocations to check method parameters whether they accept null values and reject execution if null values are not supported. We derive nullability support from repository interfaces declared using Kotlin and Spring's NonNullApi/Nullable annotations.
We also check whether a query method can return null. If a query method returns null which isn't supposed to do so, then we throw EmptyResultDataAccessException to prevent null return values.
interface UserRepository extends Repository<User, String> {
List<User> findByLastname(@Nullable String firstname);
@Nullable
User findByFirstnameAndLastname(String firstname, String lastname);
}
interface UserRepository : Repository<User, String> {
fun findByLastname(username: String?): List<User>
fun findByFirstnameAndLastname(firstname: String, lastname: String): User?
}
Original pull request: #241.
Type specialization - i.e. enrichment of a raw type with a current generic context - is now only done if the current type is not yet resolved completely. This allows wildcarded target references to just fall back to the type to specialize, which will then by definition carry more generics information than the one to be specialized.
Revert the merging of a parent type's type variable map and only apply the locally available declared generics in case of parameterized types. Moved that augmentation into ParameterizedTypeInformation.
Marked all packages with Spring Frameworks @NonNullApi. Added Spring's @Nullable to methods, parameters and fields that take or produce null values. Adapted using code to make sure the IDE can evaluate the null flow properly. Fixed Javadoc in places where an invalid null handling policy was advertised. Strengthened null requirements for types that expose null-instances.
Removed null handling from converters for JodaTime and ThreeTenBP. Introduced factory methods Page.empty() and Page.empty(Pageable). Introduced default methods getRequiredGetter(), …Setter() and …Field() on PersistentProperty to allow non-nullable lookups of members. The same for TypeInformation.getrequiredActualType(), …SuperTypeInformation().
Tweaked PersistentPropertyCreator.addPropertiesForRemainingDescriptors() to filter unsuitable PropertyDescriptors before actually trying to create a Property instance from them as the new stronger nullability requirements would cause exceptions downstream.
Lazy.get() now expects a non-null return value. Clients being able to cope with null need to call ….orElse(…).
Original pull request: #232.
RevisionRepository now returns Optional for methods that could previously return null. Revision exposes additional methods to lookup required revision numbers and dates. Revisions now implements Streamable and exposes a ….none() factory method to create an empty instance.
AnnotationBasedRevisionMetadata now uses Lazy to lookup the fields with revision annotations. AnnotationDetectionFieldCallback now also uses Optional in places it previously returned null. StreamUtils now exposes factory methods for Collector instances producing unmodifiable List and Set instances.
Related ticket: DATACMNS-867.
Make use of lambdas and method references though out the codebase. Remove no longer required generic type parameters.
Additionally remove unused imports and replace single element list initialization with dedicated singletonList.
Use Assertion overloads taking Supplier for dynamic assertion error messages.
Javaslang's collection and map types can now be used on repository query methods and get adapted similarly to nullable wrapper types (like JDK's Optional). Also made TypeInformation infrastructure aware of the map type so that a value type lookup gets handle correctly.
Previously, calls to ParameterizedTypeInformation.getMapValueType() caused an infinite recursion when invoked on a type information that was not a map type. This lead to a StackOverflowError.
We now call the super method doGetMapValueType() instead of calling the entry super method getMapValueType() and return by that null in case the map value type is not resolvable.
Original pull request: #176.
If the same generic type was used on recursively nested generics declarations, like this:
class GenericType<T> {}
class Nested extends GenericType<String> {}
class Concrete extends GenericType<Nested> {}
our traversal of the generics discovered T bound to String in Nested and as that is extending GenericType as well, the T detected here is the same instance as the T within the map discovered for Concrete. As we previously added the nested types after we added the original entry, the nested discovery overwrote the more local one.
We now make sure the detected nested type variable mappings don't overwrite already existing ones.
The translation of the list of actual type parameters into an array now uses the loop index correctly. Previously, it always used the first type parameter.
Added proper equals(…) and hashCode() methods to SyntheticParameterizedType to make sure instances created for the same type information and type parameters are considered equal.
If the type lookup from the store source returns a raw generic type (e.g. resolving the value of a generic property against a value of that generic type - i.e. not a more concrete type binding the generic information) in the context of a generic property, we previously did not apply the generics information of the contextual instance to that very raw type.
We now expose a TypeInformation.specialize(ClassTypeInformation) which applies the current generics context to the given raw type and basically creates a synthetic parameterized TypeInformation instance.
DefaultTypeMapper applies this specialization by default now with ClassTypeInformation simply returning the given type as is so that we don't create any resolution overhead in case no generics are involved in the first place.
Extended Version value object to correctly dump a non-numeric last segment of a version string to parse. Incorrect source version strings now raise an IllegalArgumentException better explaining the cause.
Related tickets: DATACMNS-496.
In case we create a ParameterizedTypeInformation for a property we now also analyze the field-local generics information and add the discovered type mappings to the type variable map.