Add support for RepositoryFragmentsContributor.
This change adopts the newly added AOT changes from https://github.com/spring-projects/spring-data-commons/pull/3282 to Cassandra repositories. - Introduces repository fragments contributor and updates the repository factories and factory beans w/ to use the default implementations. - Also makes the `CassandraRepositoryFactoryBean` consistent with its reactive counterpart by only setting the mapping context from the `CassandraOperations` in `afterPropertiesSet` iff the mapping context was not specified by the user. Closes #1574
This commit is contained in:
@@ -25,6 +25,7 @@ import org.springframework.data.cassandra.config.DefaultBeanNames;
|
||||
import org.springframework.data.cassandra.core.mapping.Table;
|
||||
import org.springframework.data.cassandra.repository.CassandraRepository;
|
||||
import org.springframework.data.cassandra.repository.support.CassandraRepositoryFactoryBean;
|
||||
import org.springframework.data.cassandra.repository.support.SimpleCassandraRepository;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport;
|
||||
@@ -40,6 +41,7 @@ import org.w3c.dom.Element;
|
||||
* @author Mark Paluch
|
||||
* @author Christoph Strobl
|
||||
* @author Mateusz Szymczak
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class CassandraRepositoryConfigurationExtension extends RepositoryConfigurationExtensionSupport {
|
||||
|
||||
@@ -55,6 +57,11 @@ public class CassandraRepositoryConfigurationExtension extends RepositoryConfigu
|
||||
return "cassandra";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryBaseClassName() {
|
||||
return SimpleCassandraRepository.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
return CassandraRepositoryFactoryBean.class.getName();
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.util.Collections;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.data.cassandra.repository.ReactiveCassandraRepository;
|
||||
import org.springframework.data.cassandra.repository.support.ReactiveCassandraRepositoryFactoryBean;
|
||||
import org.springframework.data.cassandra.repository.support.SimpleReactiveCassandraRepository;
|
||||
import org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource;
|
||||
import org.springframework.data.repository.config.RepositoryConfigurationExtension;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
@@ -30,6 +31,7 @@ import org.springframework.util.StringUtils;
|
||||
* {@link RepositoryConfigurationExtension} for Cassandra.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Chris Bono
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ReactiveCassandraRepositoryConfigurationExtension extends CassandraRepositoryConfigurationExtension {
|
||||
@@ -39,6 +41,11 @@ public class ReactiveCassandraRepositoryConfigurationExtension extends Cassandra
|
||||
return "Reactive Cassandra";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryBaseClassName() {
|
||||
return SimpleReactiveCassandraRepository.class.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryFactoryBeanClassName() {
|
||||
return ReactiveCassandraRepositoryFactoryBean.class.getName();
|
||||
|
||||
@@ -34,6 +34,7 @@ import org.springframework.data.projection.ProjectionFactory;
|
||||
import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.query.CachingValueExpressionDelegate;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
@@ -50,6 +51,7 @@ import org.springframework.util.Assert;
|
||||
* @author Thomas Darimont
|
||||
* @author Mark Paluch
|
||||
* @author John Blum
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
@@ -57,6 +59,8 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private final CassandraOperations operations;
|
||||
|
||||
private CassandraRepositoryFragmentsContributor fragmentsContributor = CassandraRepositoryFragmentsContributor.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new {@link CassandraRepositoryFactory} with the given {@link CassandraOperations}.
|
||||
*
|
||||
@@ -70,6 +74,17 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
this.mappingContext = operations.getConverter().getMappingContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link CassandraRepositoryFragmentsContributor} to be used. Defaults to
|
||||
* {@link CassandraRepositoryFragmentsContributor#DEFAULT}.
|
||||
*
|
||||
* @param fragmentsContributor
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setFragmentsContributor(CassandraRepositoryFragmentsContributor fragmentsContributor) {
|
||||
this.fragmentsContributor = fragmentsContributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader,
|
||||
@Nullable BeanFactory beanFactory) {
|
||||
@@ -103,6 +118,23 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
new CachingValueExpressionDelegate(valueExpressionDelegate), mappingContext));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
|
||||
return getRepositoryFragments(metadata, operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add Cassandra-specific extensions.
|
||||
* Built-in fragment contribution can be customized by configuring {@link CassandraRepositoryFragmentsContributor}.
|
||||
*
|
||||
* @param metadata repository metadata.
|
||||
* @param operations the Cassandra operations manager.
|
||||
* @return {@link RepositoryFragments} to be added to the repository.
|
||||
* @since 5.0
|
||||
*/
|
||||
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, CassandraOperations operations) {
|
||||
return fragmentsContributor.contribute(metadata, getEntityInformation(metadata.getDomainType()), operations);
|
||||
}
|
||||
|
||||
private record CassandraQueryLookupStrategy(CassandraOperations operations,
|
||||
ValueExpressionDelegate valueExpressionDelegate,
|
||||
@@ -130,4 +162,3 @@ public class CassandraRepositoryFactory extends RepositoryFactorySupport {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.jspecify.annotations.Nullable;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.CassandraTemplate;
|
||||
import org.springframework.data.cassandra.repository.CassandraRepository;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
|
||||
@@ -31,12 +32,17 @@ import org.springframework.util.Assert;
|
||||
* @author John Blum
|
||||
* @author Oliver Gierke
|
||||
* @author Mark Paluch
|
||||
* @author Chris Bono
|
||||
*/
|
||||
public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
|
||||
extends RepositoryFactoryBeanSupport<T, S, ID> {
|
||||
|
||||
private boolean mappingContextConfigured = false;
|
||||
|
||||
private @Nullable CassandraOperations cassandraOperations;
|
||||
|
||||
private CassandraRepositoryFragmentsContributor repositoryFragmentsContributor = CassandraRepositoryFragmentsContributor.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new {@link CassandraRepositoryFactoryBean} for the given repository interface.
|
||||
*
|
||||
@@ -51,7 +57,35 @@ public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
|
||||
|
||||
Assert.state(cassandraOperations != null, "CassandraOperations must not be null");
|
||||
|
||||
return new CassandraRepositoryFactory(cassandraOperations);
|
||||
CassandraRepositoryFactory factory = getFactoryInstance(cassandraOperations);
|
||||
factory.setFragmentsContributor(repositoryFragmentsContributor);
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and initializes a {@link CassandraRepositoryFactory} instance.
|
||||
*
|
||||
* @param operations the Cassandra operations
|
||||
* @return new {@link CassandraRepositoryFactory} instance
|
||||
*/
|
||||
protected CassandraRepositoryFactory getFactoryInstance(CassandraOperations operations) {
|
||||
return new CassandraRepositoryFactory(operations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CassandraRepositoryFragmentsContributor getRepositoryFragmentsContributor() {
|
||||
return this.repositoryFragmentsContributor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link CassandraRepositoryFragmentsContributor} to contribute built-in fragment functionality to the
|
||||
* repository.
|
||||
*
|
||||
* @param repositoryFragmentsContributor must not be {@literal null}.
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setRepositoryFragmentsContributor(CassandraRepositoryFragmentsContributor repositoryFragmentsContributor) {
|
||||
this.repositoryFragmentsContributor = repositoryFragmentsContributor;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,9 +95,15 @@ public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
|
||||
* on Apache Cassandra.
|
||||
*/
|
||||
public void setCassandraTemplate(CassandraTemplate cassandraTemplate) {
|
||||
|
||||
this.cassandraOperations = cassandraTemplate;
|
||||
setMappingContext(cassandraTemplate.getConverter().getMappingContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setMappingContext(MappingContext<?, ?> mappingContext) {
|
||||
|
||||
super.setMappingContext(mappingContext);
|
||||
|
||||
this.mappingContextConfigured = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,5 +112,9 @@ public class CassandraRepositoryFactoryBean<T extends Repository<S, ID>, S, ID>
|
||||
super.afterPropertiesSet();
|
||||
|
||||
Assert.notNull(cassandraOperations, "CassandraOperations must not be null");
|
||||
|
||||
if (!mappingContextConfigured) {
|
||||
setMappingContext(cassandraOperations.getConverter().getMappingContext());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.repository.support;
|
||||
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragmentsContributor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Cassandra-specific {@link RepositoryFragmentsContributor} contributing fragments based on the repository.
|
||||
* <p>
|
||||
* Implementations must define a no-args constructor.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface CassandraRepositoryFragmentsContributor extends RepositoryFragmentsContributor {
|
||||
|
||||
CassandraRepositoryFragmentsContributor DEFAULT = EmptyFragmentsContributor.INSTANCE;
|
||||
|
||||
/**
|
||||
* Returns a composed {@code CassandraRepositoryFragmentsContributor} that first applies this contributor to its inputs,
|
||||
* and then applies the {@code after} contributor concatenating effectively both results. If evaluation of either
|
||||
* contributors throws an exception, it is relayed to the caller of the composed contributor.
|
||||
*
|
||||
* @param after the contributor to apply after this contributor is applied.
|
||||
* @return a composed contributor that first applies this contributor and then applies the {@code after} contributor.
|
||||
*/
|
||||
default CassandraRepositoryFragmentsContributor andThen(CassandraRepositoryFragmentsContributor after) {
|
||||
|
||||
Assert.notNull(after, "CassandraRepositoryFragmentsContributor must not be null");
|
||||
|
||||
return new CassandraRepositoryFragmentsContributor() {
|
||||
|
||||
@Override
|
||||
public RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, CassandraOperations operations) {
|
||||
return CassandraRepositoryFragmentsContributor.this.contribute(metadata, entityInformation, operations)
|
||||
.append(after.contribute(metadata, entityInformation, operations));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return CassandraRepositoryFragmentsContributor.this.describe(metadata).append(after.describe(metadata));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add
|
||||
* Cassandra-specific extensions.
|
||||
*
|
||||
* @param metadata repository metadata.
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
* @return {@link RepositoryFragments} to be added to the repository.
|
||||
*/
|
||||
RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, CassandraOperations operations);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.repository.support;
|
||||
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition;
|
||||
|
||||
/**
|
||||
* Implementation of {@link CassandraRepositoryFragmentsContributor} that contributes empty fragments by default.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @since 5.0
|
||||
*/
|
||||
enum EmptyFragmentsContributor implements CassandraRepositoryFragmentsContributor {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, CassandraOperations operations) {
|
||||
return RepositoryComposition.RepositoryFragments.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return RepositoryComposition.RepositoryFragments.empty();
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import org.springframework.data.repository.core.NamedQueries;
|
||||
import org.springframework.data.repository.core.RepositoryInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.ReactiveRepositoryFactorySupport;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.query.CachingValueExpressionDelegate;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy;
|
||||
import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
@@ -46,6 +47,7 @@ import org.springframework.util.Assert;
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Chris Bono
|
||||
* @since 2.0
|
||||
*/
|
||||
public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactorySupport {
|
||||
@@ -54,6 +56,8 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
|
||||
|
||||
private final MappingContext<? extends CassandraPersistentEntity<?>, ? extends CassandraPersistentProperty> mappingContext;
|
||||
|
||||
private ReactiveCassandraRepositoryFragmentsContributor fragmentsContributor = ReactiveCassandraRepositoryFragmentsContributor.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveCassandraRepositoryFactory} with the given {@link ReactiveCassandraOperations}.
|
||||
*
|
||||
@@ -67,6 +71,17 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
|
||||
this.mappingContext = cassandraOperations.getConverter().getMappingContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link ReactiveCassandraRepositoryFragmentsContributor} to be used. Defaults to
|
||||
* {@link ReactiveCassandraRepositoryFragmentsContributor#DEFAULT}.
|
||||
*
|
||||
* @param fragmentsContributor
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setFragmentsContributor(ReactiveCassandraRepositoryFragmentsContributor fragmentsContributor) {
|
||||
this.fragmentsContributor = fragmentsContributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProjectionFactory getProjectionFactory(@Nullable ClassLoader classLoader,
|
||||
@Nullable BeanFactory beanFactory) {
|
||||
@@ -100,6 +115,24 @@ public class ReactiveCassandraRepositoryFactory extends ReactiveRepositoryFactor
|
||||
return new MappingCassandraEntityInformation<>(entity, operations.getConverter());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata) {
|
||||
return getRepositoryFragments(metadata, operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add Cassandra-specific extensions.
|
||||
* Built-in fragment contribution can be customized by configuring {@link CassandraRepositoryFragmentsContributor}.
|
||||
*
|
||||
* @param metadata repository metadata.
|
||||
* @param operations the Cassandra operations manager.
|
||||
* @return {@link RepositoryFragments} to be added to the repository.
|
||||
* @since 5.0
|
||||
*/
|
||||
protected RepositoryFragments getRepositoryFragments(RepositoryMetadata metadata, ReactiveCassandraOperations operations) {
|
||||
return fragmentsContributor.contribute(metadata, getEntityInformation(metadata.getDomainType()), operations);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link QueryLookupStrategy} to create
|
||||
* {@link org.springframework.data.cassandra.repository.query.PartTreeCassandraQuery} instances.
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.util.Assert;
|
||||
* {@link org.springframework.data.cassandra.repository.ReactiveCassandraRepository} instances.
|
||||
*
|
||||
* @author Mark Paluch
|
||||
* @author Chris Bono
|
||||
* @since 2.0
|
||||
* @see org.springframework.data.repository.reactive.ReactiveSortingRepository
|
||||
*/
|
||||
@@ -39,6 +40,8 @@ public class ReactiveCassandraRepositoryFactoryBean<T extends Repository<S, ID>,
|
||||
|
||||
private @Nullable ReactiveCassandraOperations operations;
|
||||
|
||||
private ReactiveCassandraRepositoryFragmentsContributor repositoryFragmentsContributor = ReactiveCassandraRepositoryFragmentsContributor.DEFAULT;
|
||||
|
||||
/**
|
||||
* Create a new {@link ReactiveCassandraRepositoryFactoryBean} for the given repository interface.
|
||||
*
|
||||
@@ -71,7 +74,9 @@ public class ReactiveCassandraRepositoryFactoryBean<T extends Repository<S, ID>,
|
||||
|
||||
Assert.state(operations != null, "ReactiveCassandraOperations must not be null");
|
||||
|
||||
return getFactoryInstance(operations);
|
||||
ReactiveCassandraRepositoryFactory factory = getFactoryInstance(operations);
|
||||
factory.setFragmentsContributor(repositoryFragmentsContributor);
|
||||
return factory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,10 +85,26 @@ public class ReactiveCassandraRepositoryFactoryBean<T extends Repository<S, ID>,
|
||||
* @param operations
|
||||
* @return
|
||||
*/
|
||||
protected RepositoryFactorySupport getFactoryInstance(ReactiveCassandraOperations operations) {
|
||||
protected ReactiveCassandraRepositoryFactory getFactoryInstance(ReactiveCassandraOperations operations) {
|
||||
return new ReactiveCassandraRepositoryFactory(operations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveCassandraRepositoryFragmentsContributor getRepositoryFragmentsContributor() {
|
||||
return this.repositoryFragmentsContributor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures the {@link ReactiveCassandraRepositoryFragmentsContributor} to contribute built-in fragment
|
||||
* functionality to the repository.
|
||||
*
|
||||
* @param repositoryFragmentsContributor must not be {@literal null}.
|
||||
* @since 5.0
|
||||
*/
|
||||
public void setRepositoryFragmentsContributor(ReactiveCassandraRepositoryFragmentsContributor repositoryFragmentsContributor) {
|
||||
this.repositoryFragmentsContributor = repositoryFragmentsContributor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() {
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.repository.support;
|
||||
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragmentsContributor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Reactive Cassandra-specific {@link RepositoryFragmentsContributor} contributing fragments based on the repository.
|
||||
* <p>
|
||||
* Implementations must define a no-args constructor.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @since 5.0
|
||||
*/
|
||||
public interface ReactiveCassandraRepositoryFragmentsContributor extends RepositoryFragmentsContributor {
|
||||
|
||||
ReactiveCassandraRepositoryFragmentsContributor DEFAULT = ReactiveEmptyFragmentsContributor.INSTANCE;
|
||||
|
||||
/**
|
||||
* Returns a composed {@code ReactiveCassandraRepositoryFragmentsContributor} that first applies this contributor to
|
||||
* its inputs, and then applies the {@code after} contributor concatenating effectively both results. If evaluation
|
||||
* of either contributors throws an exception, it is relayed to the caller of the composed contributor.
|
||||
*
|
||||
* @param after the contributor to apply after this contributor is applied.
|
||||
* @return a composed contributor that first applies this contributor and then applies the {@code after} contributor.
|
||||
*/
|
||||
default ReactiveCassandraRepositoryFragmentsContributor andThen(ReactiveCassandraRepositoryFragmentsContributor after) {
|
||||
|
||||
Assert.notNull(after, "ReactiveCassandraRepositoryFragmentsContributor must not be null");
|
||||
|
||||
return new ReactiveCassandraRepositoryFragmentsContributor() {
|
||||
|
||||
@Override
|
||||
public RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, ReactiveCassandraOperations operations) {
|
||||
return ReactiveCassandraRepositoryFragmentsContributor.this.contribute(metadata, entityInformation, operations)
|
||||
.append(after.contribute(metadata, entityInformation, operations));
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return ReactiveCassandraRepositoryFragmentsContributor.this.describe(metadata).append(after.describe(metadata));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link RepositoryFragments} based on {@link RepositoryMetadata} to add
|
||||
* Cassandra-specific extensions.
|
||||
*
|
||||
* @param metadata repository metadata.
|
||||
* @param entityInformation must not be {@literal null}.
|
||||
* @param operations must not be {@literal null}.
|
||||
* @return {@link RepositoryFragments} to be added to the repository.
|
||||
*/
|
||||
RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, ReactiveCassandraOperations operations);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.repository.support;
|
||||
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ReactiveCassandraRepositoryFragmentsContributor} that contributes empty fragments by
|
||||
* default.
|
||||
*
|
||||
* @author Chris Bono
|
||||
* @since 5.0
|
||||
*/
|
||||
enum ReactiveEmptyFragmentsContributor implements ReactiveCassandraRepositoryFragmentsContributor {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, ReactiveCassandraOperations operations) {
|
||||
return RepositoryComposition.RepositoryFragments.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return RepositoryComposition.RepositoryFragments.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.support;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.cassandra.core.CassandraOperations;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.domain.User;
|
||||
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
|
||||
import org.springframework.data.cassandra.repository.support.CassandraRepositoryFragmentsContributor;
|
||||
import org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link CassandraRepositoryFragmentsContributor}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class CassandraRepositoryFragmentsContributorUnitTests {
|
||||
|
||||
@Test // GH-3279
|
||||
void composedContributorShouldCreateFragments() {
|
||||
|
||||
var mappingContext = new CassandraMappingContext();
|
||||
var converter = new MappingCassandraConverter(mappingContext);
|
||||
CassandraOperations operations = mock();
|
||||
when(operations.getConverter()).thenReturn(converter);
|
||||
|
||||
var contributor = CassandraRepositoryFragmentsContributor.DEFAULT
|
||||
.andThen(MyCassandraRepositoryFragmentsContributor.INSTANCE)
|
||||
.andThen(MyOtherCassandraRepositoryFragmentsContributor.INSTANCE);
|
||||
|
||||
var fragments = contributor.contribute(
|
||||
AbstractRepositoryMetadata.getMetadata(MyUserRepo.class),
|
||||
new MappingCassandraEntityInformation<>(mappingContext.getPersistentEntity(User.class), converter),
|
||||
operations);
|
||||
|
||||
assertThat(fragments.stream())
|
||||
.extracting(RepositoryFragment::getImplementationClass)
|
||||
.containsExactly(Optional.of(MyFragment.class), Optional.of(MyOtherFragment.class));
|
||||
}
|
||||
|
||||
enum MyCassandraRepositoryFragmentsContributor implements CassandraRepositoryFragmentsContributor {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, CassandraOperations operations) {
|
||||
return RepositoryFragments.just(new MyFragment());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return RepositoryFragments.just(new MyFragment());
|
||||
}
|
||||
}
|
||||
|
||||
enum MyOtherCassandraRepositoryFragmentsContributor implements CassandraRepositoryFragmentsContributor {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, CassandraOperations operations) {
|
||||
return RepositoryFragments.just(new MyOtherFragment());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return RepositoryFragments.just(new MyOtherFragment());
|
||||
}
|
||||
}
|
||||
|
||||
static class MyFragment {
|
||||
|
||||
}
|
||||
|
||||
static class MyOtherFragment {
|
||||
|
||||
}
|
||||
|
||||
interface MyUserRepo extends Repository<User, Long> {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.cassandra.support;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.cassandra.core.ReactiveCassandraOperations;
|
||||
import org.springframework.data.cassandra.core.convert.MappingCassandraConverter;
|
||||
import org.springframework.data.cassandra.core.mapping.CassandraMappingContext;
|
||||
import org.springframework.data.cassandra.domain.User;
|
||||
import org.springframework.data.cassandra.repository.query.CassandraEntityInformation;
|
||||
import org.springframework.data.cassandra.repository.support.MappingCassandraEntityInformation;
|
||||
import org.springframework.data.cassandra.repository.support.ReactiveCassandraRepositoryFragmentsContributor;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.core.RepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.AbstractRepositoryMetadata;
|
||||
import org.springframework.data.repository.core.support.RepositoryComposition.RepositoryFragments;
|
||||
import org.springframework.data.repository.core.support.RepositoryFragment;
|
||||
|
||||
import static org.assertj.core.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ReactiveCassandraRepositoryFragmentsContributor}.
|
||||
*
|
||||
* @author Chris Bono
|
||||
*/
|
||||
class ReactiveCassandraRepositoryFragmentsContributorUnitTests {
|
||||
|
||||
@Test // GH-3279
|
||||
void composedContributorShouldCreateFragments() {
|
||||
|
||||
var mappingContext = new CassandraMappingContext();
|
||||
var converter = new MappingCassandraConverter(mappingContext);
|
||||
ReactiveCassandraOperations operations = mock();
|
||||
when(operations.getConverter()).thenReturn(converter);
|
||||
|
||||
var contributor = ReactiveCassandraRepositoryFragmentsContributor.DEFAULT
|
||||
.andThen(MyReactiveCassandraRepositoryFragmentsContributor.INSTANCE)
|
||||
.andThen(MyOtherReactiveCassandraRepositoryFragmentsContributor.INSTANCE);
|
||||
|
||||
var fragments = contributor.contribute(
|
||||
AbstractRepositoryMetadata.getMetadata(MyUserRepo.class),
|
||||
new MappingCassandraEntityInformation<>(mappingContext.getPersistentEntity(User.class), converter),
|
||||
operations);
|
||||
|
||||
assertThat(fragments.stream())
|
||||
.extracting(RepositoryFragment::getImplementationClass)
|
||||
.containsExactly(Optional.of(MyReactiveFragment.class), Optional.of(MyOtherReactiveFragment.class));
|
||||
}
|
||||
|
||||
enum MyReactiveCassandraRepositoryFragmentsContributor implements ReactiveCassandraRepositoryFragmentsContributor {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, ReactiveCassandraOperations operations) {
|
||||
return RepositoryFragments.just(new MyReactiveFragment());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return RepositoryFragments.just(new MyReactiveFragment());
|
||||
}
|
||||
}
|
||||
|
||||
enum MyOtherReactiveCassandraRepositoryFragmentsContributor implements ReactiveCassandraRepositoryFragmentsContributor {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public RepositoryFragments contribute(RepositoryMetadata metadata,
|
||||
CassandraEntityInformation<?, ?> entityInformation, ReactiveCassandraOperations operations) {
|
||||
return RepositoryFragments.just(new MyOtherReactiveFragment());
|
||||
}
|
||||
|
||||
@Override
|
||||
public RepositoryFragments describe(RepositoryMetadata metadata) {
|
||||
return RepositoryFragments.just(new MyOtherReactiveFragment());
|
||||
}
|
||||
}
|
||||
|
||||
static class MyReactiveFragment {
|
||||
|
||||
}
|
||||
|
||||
static class MyOtherReactiveFragment {
|
||||
|
||||
}
|
||||
|
||||
interface MyUserRepo extends Repository<User, Long> {}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user