We now skip PersistentPropertyPath instances pointing to auditing properties for which the path contains a collection or map path segment as the PersistentPropertyAccessor currently cannot handle those. A more extensive fix for that will be put in place for Moore but requires more extensive API changes which we don't want to ship in a Lovelace maintenance release.
Related tickets: DATACMNS-1461.
PropertyAccessorClassGenerator now tries to look up the class to be generated first to potentially reuse an already existing one and avoid the recreation and registration of a same class which would trigger a Linkage error as a classloader cannot hold two classes with the same name.
The root of the problem is in the fact that the accessor instances are held in per instance caches in EntityInstantiators, so that multiple of those might try to create the same accessor class for a given domain type.
We now remove partially populated PersistentEntity instances from the cache held in AbstractMappingContext as the creation could fail for other RuntimeExceptions other than a MappingException and we wouldn't want to keep the instances around in any case.
Slight refactorings in the unit tests so that we can easily create MappingContext instances that reject certain types with certain exceptions.
PersistentPropertyAccessor now has an overloaded method to pass in a TraversalContext that can be prepared to property handlers for PersistentProperties. During the traversal, the handler is called with the value for the property so that it can e.g. extract list elements, map values etc.
Add dedicated interfaces for sync and reactive usage.
Hide default implementation and use reflective callback method lookup.
Update documentation and add initial reference documentation snippet for store specific modules.
Original Pull Request: #332
This is a draft for a possible Entity Callback API heavily inspired by Spring Framework's Application Event listeners. Entity callbacks are callbacks for entities that allow for entity modification at certain check points such as before saving an entity or after loading it.
Entity callbacks consist of a marker-interface for all entity callback types and the EntityCallbacks API to dispatch callbacks.
Entity callbacks are picked up from the ApplicationContext and invoked sequentially according to their ordering.
Usage example:
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
SimpleEntityCallbacks callbacks = new SimpleEntityCallbacks(ctx);
PersonDocument personDocument = new PersonDocument(null, "Walter", null);
PersonDocument afterCallback = callbacks.callback(personDocument, BeforeSaveCallback.class,
BeforeSaveCallback::onBeforeSave);
Mono<PersonDocument> afterCallback = callbacks.callbackLater(personDocument, ReactiveBeforeSaveCallback.class,
ReactiveBeforeSaveCallback::onBeforeSave);
@Configuration
static class MyConfig {
@Bean
BeforeSaveCallback<Person> personCallback() {
return object -> {
object.setSsn(object.getFirstName().length());
return object;
};
}
@Bean
MyReactiveBeforeSaveCallback callback() {
return new MyReactiveBeforeSaveCallback();
}
}
class MyReactiveBeforeSaveCallback implements ReactiveBeforeSaveCallback<Person> {
@Override
public Mono<Person> onBeforeSave(Person object) {
PersonDocument result = new PersonDocument(object.getFirstName().length(), object.getFirstName(),
object.getLastName());
return Mono.just(result);
}
}
Original Pull Request: #332
We now consider more than 16 immutable and nullable Kotlin properties per bucket in generated PropertyAccessors.
Previously only the first 16 properties were considered due to truncation of the defaulting bitmap. We used SIPUSH to render the defaulting mask in bytecode which is intended for 16 bit integers (short). Migrating to LDC (runtime constants) preserves the actual constant value of 32 bits and so we're considering now full buckets.
We now fall back to reflection-based PropertyAccessor/EntityInstantiator strategies when framework types are not visible by the entity's ClassLoader.
Typically, we use class generation to create and load PropertyAccessor and EntityInstantiator classes to bypass reflection. Generated types are injected into the ClassLoader that has loaded the actual entity. Generated classes implement framework types such as ObjectInstantiator and these interfaces must be visible to the ClassLoader that hosts the generated class. Some arrangements, such as OSGi isolate class repositories so the OSGi class loader cannot load our own types which prevents loading the generated class.
Original pull request: #324.
We now inspect all methods that match the wither method pattern (name, accepting a single argument) to find the most specific method returning the actual entity type.
Previously, we attempted to find a method only considering name and argument properties and not the return type. This lookup strategy could find a method returning the entity super type that isn't assignable to the entity type.
Original pull request: #323.
We now provide the context class for private MethodHandle lookup in the context of the actual entity class to properly use MethodHandles.defineClass(…) and to avoid illegal access warnings caused by reflective access to the defineClass(…) method on class loaders.
Also, we now use the internal type name without adding the reference type decorator when casting the result of a wither invocation. CHECKCAST allowed on earlier Java runtimes (version 8 and earlier) to use the reference decorator (L…;) around the type name. Java 9 and newer reject this format with a ClassFormatError.
Original pull request: #318.
We now correctly calculate the number of defaulting masks used to represent constructor arguments. Previously, we've been one off which caused that Kotlin classes with 32/33 parameters weren't able to be instantiated.
We also now reuse KotlinDefaultMask to apply defaulting calculation and removed code duplicates.
We now no longer consider bridge modifiers when looking up Kotlin default methods. We previously included checks whether a synthetic default method is also a bridge method to take all specifics of synthetic methods into account. With Kotlin 1.3, the compiler no longer sets the bridge flag. This behavior change would previously prevent usage of the copy method with classes compiled with Kotlin 1.3.
Default method discovery is still guesswork and Kotlin compiler reverse engineering as there is no documentation on how to look up this kind of methods.
Further references:
* https://youtrack.jetbrains.net/issue/KT-24415 - Remove bridge flag from default methods.
* https://youtrack.jetbrains.net/issue/KT-27317 - No documented rules for discoverability of generated methods.
We now use the primary constructor of Kotlin classes to discover the copy method. We use the primary constructor args to compare signatures and only use the copy method that takes all parameters in the constructor args order. This allows to find the appropriate copy method in case the class declares multiple copy methods.
We now also cache the copy method (KCallable) to reduce lookups in BeanWrapper.
Original pull request: #312.
Previously, ConvertingPropertyAccessor did not override PersistentPropertyAccessor.setProperty(PersistentPropertyPath, Object), so that the target value had to be of the leaf property's type. We now implement that method and convert it into that type before invoking the super method.
We now use a presized HashMap and Weak references in BasicPersistentEntity to improve memory and CPU profile and and avoid unmodifiable collection creation in BasicPersistentEntity.iterator(). Refactored ClassTypeInformation.from(…) lambda to method reference and predefined collection size for the cache. Reduced object instantiations during TypeDiscoverer.equals(…) by checking for type variable map emptiness to avoid Map iterator creation in Map.equals(…).
Original pull request: #305.
Use weak references in annotation and property annotation cache to retain references until the last GC root is cleared. Remove trailing whitespaces. Reformat.
Original pull request: #304.
We now use HashMap to store persistent properties of a PersistentEntity. An entity is built in a single thread so no concurrent modification happens. Concurrent reads may happen during entity usage which is fine as the PersistentEntity is not changed anymore. Previously, we used ConcurrentReferenceHashMap defaulting to soft references. Soft references can be cleared at the discretion of the GC in response to memory demand. So a default ConcurrentReferenceHashMap is memory-sensitive and acts like a cache with memory-based eviction rules.
Persistent properties are not subject to be cached but elements of a PersistentEntity and cannot be recovered once cleared.
Original pull request: #304.
The newly introduced method indicates whether any properties have to be populated to create instances of the entity. This is useful for objects that are completely initialized through their constructors as converters then can avoid iterating over all properties just to find out none of them have to be populated.
The exception messages used in the PersistentProperty.getRequired(Getter|Setter|Wither|Field)(…) now mention the name of the property that's offending.
This is needed for downstream projects that attempt to merge persistent entity instances and previously didn't have a chance to detect that an object had to be set as-is instead of being merged recursively.
PersistentPropertyAccessor is now generic to be able to retain the type information about the object it was created for and the return type of ….getBean(). Adapted client APIs.
As setting a PersistentProperty can actually change the object that the property is changed on, we now recursively traverse property paths up as longs as the setting of the property results in the bean not being replaced.
Add missing since tags. Extract methods to maintain single abstraction level per method. Move visitDefaultValue from ClassGeneratingPropertyAccessorFactory to BytecodeUtil. Extend Javadoc.
We now support updating of immutable Kotlin data objects by creating new copies through Kotlin's copy method that is generated along with data classes and immutable properties.
data class DataClassKt(val id: String) {
}
data class ExtendedDataClassKt(val id: String, val name: String) {
}
We now detect withher methods (withId(…)) that create a new object instance that contains the new property value. We support those wither methods to create new object instances from property updates. Classes following a wither pattern declare a withXXX(…) method that accepts the property value and return a new, instance of its own type associated with the property value. Along with this change we removed the ability to update final fields that worked by accident using reflection.
class ValueClass {
@Wither String id;
}
class ValueClass {
final String id;
ValueClass withId(String id) {
return new ValueClass(id);
}
}
Moved test cases into PropertyPathUnit test so that they're closer to the implementation. Switched to Introspector.decapitalize(…) to follow the Java Beans Specification regarding the handling of all-uppercase properties.
Original pull request: #289.
PersistentEntity now exposes an ….isNew(…) method that exposes the same detection algorithm previously exposed through MappingContextIsNewStrategyFactory (Persistable in favor of the version property in favor of an identifier lookup). MappingContextIsNewStrategyFactory has been refactored to return an ad-hoc strategy to delegate to the newly introduced method.
The core message to implementing modules is that they should now prefer PersistentEntityInformation within their RepositoryFactorySupport implementation and move all customizations made in the store-specific EntityInformation implementation in PersistentEntity.
We now expose what type or PersistentEntity an association points to by trying to match the association's type to identifier types to entities. In case multiple matches are found, we require the user to explicitly declare the target type via @Reference.
Introduced PersistentEntities.of(…) for convenience.
We now have a refined replica of the EvaluationContextProvider API and SPIs in the org.springframework.data.spel package. It has seen a bit of a Java 8 overhaul by removing the SPI support class in favor of turning most methods in EvaluationContextExtension into default ones.
The already existing API has been renamed to QueryMethodEvaluationContextProvider to indicate it's working with additional semantics specific to query methods (i.e. the Parameters metadata). The internals have been refactored to use the new API but still detect implementations of the old EvaluationContextExtension interface. The implementations get wrapped into an adapting proxy to satisfy the new API so that the actual inspection and usage of the extension is now already done using the new APIs.
The repository configuration has slightly change so that the creation of the EvaluationContextProvider is now taking place within RepositoryFactoryBeanSupport's implementation of BeanFactoryAware.
AbstractMappingContext is now ApplicationContextAware and holds an ExtensionAwareEvaluationContextProvider using the configured ApplicationContext. That EvaluationContextProvider is forwarded to all MutablePersistentEntity instances. BasicPersistentEntity now exposes getEvaluationContext(…) to subclasses to easily create an EvaluationContext using the extension aware infrastructure.
Removed DefaultEvaluationContextProvider in favor of a simple constant in QueryMethodEvaluationContextProvider.
Related tickets: DATACMNS-1258, DATACMNS-1108.
We now consider all types in the java.time package as simple types to prevent deep reflective access. We are already excluding java.lang types for the same reason.
Original pull request: #286.
MappingContext now exposes a method to detect all property paths pointing to properties matching a given predicate.
Extracted PersistentPropertyPath creation into a dedicated factory class so that it can be tested individually. DefaultPersistentPropertyPath now exposes a ….containsPropertyOfType(…) to detect whether we've already processed a particular type in the path. Also applied a bit of Java 8 and Lombok polish.
InvalidPersistentPropertyPath now collects suggested alternatives to create a better exception message. PersistentEntities now allows to map over a MappingContext and PersistentEntity that a given type is corresponding to. Streamable now exposes an ….isEmpty(). Removed references to equivalent methods implemented in subtypes.
We now explicitly check for an enum type in SimpleTypeHolder.isSimpleType(…) and resort to true immediately. Before that an enum implementing an interface could have seen a false for the actual enum type in case the interface type had been checked first and (correctly) produced a false. In the check for the actual enum type, depending on the iteration order through the cached values we could've hit the cached false for the interface or the cached true value for Enum.
Use ConcurrentHashMap in AnnotationBasedPersistentProperty for thread-safe annotation caching. Reintroduce eager cache check to prevent lambda instances on positive cache hits.