diff --git a/pom.xml b/pom.xml index 4cc6241b..1958decf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-keyvalue - 4.0.0-SNAPSHOT + 4.0.0-GH-363-SNAPSHOT Spring Data KeyValue diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java index e4848309..d63b1bdd 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueAdapter.java @@ -20,7 +20,10 @@ import org.jspecify.annotations.Nullable; import org.springframework.beans.factory.DisposableBean; +import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity; +import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty; import org.springframework.data.keyvalue.core.query.KeyValueQuery; +import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.util.CloseableIterator; /** @@ -32,6 +35,14 @@ */ public interface KeyValueAdapter extends DisposableBean { + /** + * Expose the {@link MappingContext} to obtain mapping metadata. + * + * @return the used mapping context. + * @since 4.0 + */ + MappingContext, ? extends KeyValuePersistentProperty> getMappingContext(); + /** * Add object with given id to keyspace. * diff --git a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java index 3b33b821..04c415a5 100644 --- a/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java +++ b/src/main/java/org/springframework/data/keyvalue/core/KeyValueTemplate.java @@ -70,38 +70,24 @@ public class KeyValueTemplate implements KeyValueOperations, ApplicationEventPub * @param adapter must not be {@literal null}. */ public KeyValueTemplate(KeyValueAdapter adapter) { - this(adapter, new KeyValueMappingContext<>()); + this(adapter, DefaultIdentifierGenerator.INSTANCE); } /** - * Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} and {@link MappingContext}. + * Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} and {@link IdentifierGenerator}. * * @param adapter must not be {@literal null}. - * @param mappingContext must not be {@literal null}. - */ - public KeyValueTemplate(KeyValueAdapter adapter, - MappingContext, ? extends KeyValuePersistentProperty> mappingContext) { - this(adapter, mappingContext, DefaultIdentifierGenerator.INSTANCE); - } - - /** - * Create new {@link KeyValueTemplate} using the given {@link KeyValueAdapter} and {@link MappingContext}. - * - * @param adapter must not be {@literal null}. - * @param mappingContext must not be {@literal null}. * @param identifierGenerator must not be {@literal null}. - * @since 2.4 + * @since 4.0 */ public KeyValueTemplate(KeyValueAdapter adapter, - MappingContext, ? extends KeyValuePersistentProperty> mappingContext, IdentifierGenerator identifierGenerator) { Assert.notNull(adapter, "Adapter must not be null"); - Assert.notNull(mappingContext, "MappingContext must not be null"); Assert.notNull(identifierGenerator, "IdentifierGenerator must not be null"); this.adapter = adapter; - this.mappingContext = mappingContext; + this.mappingContext = adapter.getMappingContext(); this.identifierGenerator = identifierGenerator; } diff --git a/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java b/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java index f276451b..71733950 100644 --- a/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java +++ b/src/main/java/org/springframework/data/keyvalue/repository/config/KeyValueRepositoryConfigurationExtension.java @@ -21,6 +21,7 @@ import java.util.Optional; import org.jspecify.annotations.Nullable; + import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -28,7 +29,6 @@ import org.springframework.core.annotation.AnnotationAttributes; import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.type.AnnotationMetadata; -import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.keyvalue.repository.KeyValueRepository; import org.springframework.data.keyvalue.repository.query.KeyValuePartTreeQuery; import org.springframework.data.keyvalue.repository.query.SpelQueryCreator; @@ -47,7 +47,6 @@ */ public abstract class KeyValueRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport { - protected static final String MAPPING_CONTEXT_BEAN_NAME = "keyValueMappingContext"; protected static final String KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE = "keyValueTemplateRef"; @Override @@ -78,7 +77,6 @@ public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfi builder.addPropertyReference("keyValueOperations", attributes.getString(KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE)); builder.addPropertyValue("queryCreator", getQueryCreatorType(config)); builder.addPropertyValue("queryType", getQueryType(config)); - builder.addPropertyReference("mappingContext", getMappingContextBeanRef()); } /** @@ -128,15 +126,6 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf super.registerBeansForRoot(registry, configurationSource); - registerIfNotAlreadyRegistered(() -> { - - RootBeanDefinition mappingContext = new RootBeanDefinition(KeyValueMappingContext.class); - mappingContext.setSource(configurationSource.getSource()); - - return mappingContext; - - }, registry, getMappingContextBeanRef(), configurationSource); - Optional keyValueTemplateName = configurationSource.getAttribute(KEY_VALUE_TEMPLATE_BEAN_REF_ATTRIBUTE); // No custom template reference configured and no matching bean definition found @@ -174,15 +163,4 @@ public void registerBeansForRoot(BeanDefinitionRegistry registry, RepositoryConf */ protected abstract String getDefaultKeyValueTemplateRef(); - /** - * Returns the {@link org.springframework.data.mapping.context.MappingContext} bean name to potentially register a - * default mapping context bean if no bean is registered with the returned name. Defaults to - * {@link MAPPING_CONTEXT_BEAN_NAME}. - * - * @return the {@link org.springframework.data.mapping.context.MappingContext} bean name. Never {@literal null}. - * @since 2.0 - */ - protected String getMappingContextBeanRef() { - return MAPPING_CONTEXT_BEAN_NAME; - } } diff --git a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java index 3018c104..e285a4c8 100644 --- a/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java +++ b/src/main/java/org/springframework/data/map/MapKeyValueAdapter.java @@ -28,6 +28,9 @@ import org.springframework.data.keyvalue.core.KeyValueAdapter; import org.springframework.data.keyvalue.core.QueryEngine; import org.springframework.data.keyvalue.core.SortAccessor; +import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentEntity; +import org.springframework.data.keyvalue.core.mapping.KeyValuePersistentProperty; +import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.util.CloseableIterator; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -38,12 +41,14 @@ * @author Christoph Strobl * @author Derek Cochran * @author Marcel Overdijk + * @author Mark Paluch */ public class MapKeyValueAdapter extends AbstractKeyValueAdapter { @SuppressWarnings("rawtypes") // private final Class keySpaceMapType; private final Map> store; + private KeyValueMappingContext, ? extends KeyValuePersistentProperty> mappingContext = new KeyValueMappingContext<>(); /** * Create new {@link MapKeyValueAdapter} using {@link ConcurrentHashMap} as backing store type. @@ -143,6 +148,16 @@ private MapKeyValueAdapter(Map> store, Class, ? extends KeyValuePersistentProperty> getMappingContext() { + return mappingContext; + } + + public void setMappingContext( + KeyValueMappingContext, ? extends KeyValuePersistentProperty> mappingContext) { + this.mappingContext = mappingContext; + } + @Override public @Nullable Object put(Object id, Object item, String keyspace) { diff --git a/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java b/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java index 482e180d..acf468f6 100644 --- a/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java +++ b/src/main/java/org/springframework/data/map/repository/config/EnableMapRepositories.java @@ -60,13 +60,25 @@ /** * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.: - * {@code @EnableJpaRepositories("org.my.pkg")} instead of {@code @EnableJpaRepositories(basePackages="org.my.pkg")}. + * {@code @EnableMapRepositories("org.my.pkg")} instead of {@code @EnableMapRepositories(basePackages="org.my.pkg")}. */ String[] value() default {}; /** - * Base packages to scan for annotated components. {@link #value()} is an alias for (and mutually exclusive with) this - * attribute. Use {@link #basePackageClasses()} for a type-safe alternative to String-based package names. + * Base packages to scan for annotated components. + *

+ * {@link #value} is an alias for (and mutually exclusive with) this attribute. + *

+ * Supports {@code ${…}} placeholders which are resolved against the {@link org.springframework.core.env.Environment + * Environment} as well as Ant-style package patterns — for example, {@code "org.example.**"}. + *

+ * Multiple packages or patterns may be specified, either separately or within a single {@code String} — for + * example, {@code {"org.example.config", "org.example.service.**"}} or + * {@code "org.example.config, org.example.service.**"}. + *

+ * Use {@link #basePackageClasses} for a type-safe alternative to String-based package names. + * + * @see org.springframework.context.ConfigurableApplicationContext#CONFIG_LOCATION_DELIMITERS */ String[] basePackages() default {}; diff --git a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java index 22774b0b..cfe4b095 100644 --- a/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java +++ b/src/test/java/org/springframework/data/keyvalue/core/KeyValueTemplateUnitTests.java @@ -47,6 +47,7 @@ import org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeGetEvent; import org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeInsertEvent; import org.springframework.data.keyvalue.core.event.KeyValueEvent.BeforeUpdateEvent; +import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.keyvalue.core.query.KeyValueQuery; /** @@ -73,6 +74,7 @@ class KeyValueTemplateUnitTests { @BeforeEach void setUp() { + when(adapterMock.getMappingContext()).thenReturn(new KeyValueMappingContext()); this.template = new KeyValueTemplate(adapterMock); this.template.setApplicationEventPublisher(publisherMock); } diff --git a/src/test/java/org/springframework/data/map/repository/config/MapRepositoriesConfigurationExtensionIntegrationTests.java b/src/test/java/org/springframework/data/map/repository/config/MapRepositoriesConfigurationExtensionIntegrationTests.java index d4b5fabd..adffa321 100644 --- a/src/test/java/org/springframework/data/map/repository/config/MapRepositoriesConfigurationExtensionIntegrationTests.java +++ b/src/test/java/org/springframework/data/map/repository/config/MapRepositoriesConfigurationExtensionIntegrationTests.java @@ -44,6 +44,7 @@ import org.springframework.data.keyvalue.core.QueryEngineFactory; import org.springframework.data.keyvalue.core.SortAccessor; import org.springframework.data.keyvalue.core.SpelQueryEngine; +import org.springframework.data.keyvalue.core.mapping.context.KeyValueMappingContext; import org.springframework.data.keyvalue.core.query.KeyValueQuery; import org.springframework.data.keyvalue.repository.KeyValueRepository; import org.springframework.data.keyvalue.repository.query.PredicateQueryCreator; @@ -220,7 +221,7 @@ public KeyValueOperations mapKeyValueTemplate() { public KeyValueAdapter keyValueAdapter() { KeyValueAdapter mock = mock(KeyValueAdapter.class); - + when(mock.getMappingContext()).thenReturn(new KeyValueMappingContext()); when(mock.get(any(), anyString(), any())).thenThrow(new IllegalStateException("Mock")); return mock;