18 changed files with 1026 additions and 59 deletions
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.support; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.MongoOperations; |
||||||
|
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; |
||||||
|
import org.springframework.data.repository.core.RepositoryMetadata; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryComposition; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragmentsContributor; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* MongoDB-specific {@link RepositoryFragmentsContributor} contributing fragments based on the repository. |
||||||
|
* <p> |
||||||
|
* Implementations must define a no-args constructor. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
* @since 5.0 |
||||||
|
* @see QuerydslMongoPredicateExecutor |
||||||
|
*/ |
||||||
|
public interface MongoRepositoryFragmentsContributor extends RepositoryFragmentsContributor { |
||||||
|
|
||||||
|
MongoRepositoryFragmentsContributor DEFAULT = QuerydslContributor.INSTANCE; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a composed {@code MongoRepositoryFragmentsContributor} 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 MongoRepositoryFragmentsContributor andThen(MongoRepositoryFragmentsContributor after) { |
||||||
|
|
||||||
|
Assert.notNull(after, "MongoRepositoryFragmentsContributor must not be null"); |
||||||
|
|
||||||
|
return new MongoRepositoryFragmentsContributor() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, MongoOperations operations) { |
||||||
|
return MongoRepositoryFragmentsContributor.this.contribute(metadata, entityInformation, operations) |
||||||
|
.append(after.contribute(metadata, entityInformation, operations)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) { |
||||||
|
return MongoRepositoryFragmentsContributor.this.describe(metadata).append(after.describe(metadata)); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates {@link RepositoryComposition.RepositoryFragments} based on {@link RepositoryMetadata} to add |
||||||
|
* MongoDB-specific extensions. |
||||||
|
* |
||||||
|
* @param metadata repository metadata. |
||||||
|
* @param entityInformation must not be {@literal null}. |
||||||
|
* @param operations must not be {@literal null}. |
||||||
|
* @return {@link RepositoryComposition.RepositoryFragments} to be added to the repository. |
||||||
|
*/ |
||||||
|
RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, MongoOperations operations); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,70 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.support; |
||||||
|
|
||||||
|
import static org.springframework.data.querydsl.QuerydslUtils.*; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.MongoOperations; |
||||||
|
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; |
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor; |
||||||
|
import org.springframework.data.repository.core.RepositoryMetadata; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryComposition; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragment; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragmentsContributor; |
||||||
|
|
||||||
|
/** |
||||||
|
* MongoDB-specific {@link RepositoryFragmentsContributor} contributing Querydsl fragments if a repository implements |
||||||
|
* {@link QuerydslPredicateExecutor}. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
* @since 5.0 |
||||||
|
* @see QuerydslMongoPredicateExecutor |
||||||
|
*/ |
||||||
|
enum QuerydslContributor implements MongoRepositoryFragmentsContributor { |
||||||
|
|
||||||
|
INSTANCE; |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, MongoOperations operations) { |
||||||
|
|
||||||
|
if (isQuerydslRepository(metadata)) { |
||||||
|
|
||||||
|
QuerydslMongoPredicateExecutor<?> executor = new QuerydslMongoPredicateExecutor<>(entityInformation, operations); |
||||||
|
|
||||||
|
return RepositoryComposition.RepositoryFragments |
||||||
|
.of(RepositoryFragment.implemented(QuerydslPredicateExecutor.class, executor)); |
||||||
|
} |
||||||
|
|
||||||
|
return RepositoryComposition.RepositoryFragments.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) { |
||||||
|
|
||||||
|
if (isQuerydslRepository(metadata)) { |
||||||
|
return RepositoryComposition.RepositoryFragments |
||||||
|
.of(RepositoryFragment.structural(QuerydslPredicateExecutor.class, QuerydslMongoPredicateExecutor.class)); |
||||||
|
} |
||||||
|
|
||||||
|
return RepositoryComposition.RepositoryFragments.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isQuerydslRepository(RepositoryMetadata metadata) { |
||||||
|
return QUERY_DSL_PRESENT && QuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,78 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.support; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations; |
||||||
|
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; |
||||||
|
import org.springframework.data.repository.core.RepositoryMetadata; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryComposition; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragmentsContributor; |
||||||
|
import org.springframework.util.Assert; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reactive MongoDB-specific {@link RepositoryFragmentsContributor} contributing fragments based on the repository. |
||||||
|
* <p> |
||||||
|
* Implementations must define a no-args constructor. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
* @since 5.0 |
||||||
|
* @see ReactiveQuerydslMongoPredicateExecutor |
||||||
|
*/ |
||||||
|
public interface ReactiveMongoRepositoryFragmentsContributor extends RepositoryFragmentsContributor { |
||||||
|
|
||||||
|
ReactiveMongoRepositoryFragmentsContributor DEFAULT = ReactiveQuerydslContributor.INSTANCE; |
||||||
|
|
||||||
|
/** |
||||||
|
* Returns a composed {@code ReactiveMongoRepositoryFragmentsContributor} 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 ReactiveMongoRepositoryFragmentsContributor andThen(ReactiveMongoRepositoryFragmentsContributor after) { |
||||||
|
|
||||||
|
Assert.notNull(after, "ReactiveMongoRepositoryFragmentsContributor must not be null"); |
||||||
|
|
||||||
|
return new ReactiveMongoRepositoryFragmentsContributor() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, ReactiveMongoOperations operations) { |
||||||
|
return ReactiveMongoRepositoryFragmentsContributor.this.contribute(metadata, entityInformation, operations) |
||||||
|
.append(after.contribute(metadata, entityInformation, operations)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) { |
||||||
|
return ReactiveMongoRepositoryFragmentsContributor.this.describe(metadata).append(after.describe(metadata)); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates {@link RepositoryComposition.RepositoryFragments} based on {@link RepositoryMetadata} to add |
||||||
|
* MongoDB-specific extensions. |
||||||
|
* |
||||||
|
* @param metadata repository metadata. |
||||||
|
* @param entityInformation must not be {@literal null}. |
||||||
|
* @param operations must not be {@literal null}. |
||||||
|
* @return {@link RepositoryComposition.RepositoryFragments} to be added to the repository. |
||||||
|
*/ |
||||||
|
RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, ReactiveMongoOperations operations); |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,73 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.support; |
||||||
|
|
||||||
|
import static org.springframework.data.querydsl.QuerydslUtils.*; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations; |
||||||
|
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; |
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor; |
||||||
|
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; |
||||||
|
import org.springframework.data.repository.core.RepositoryMetadata; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryComposition; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragment; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragmentsContributor; |
||||||
|
|
||||||
|
/** |
||||||
|
* Reactive MongoDB-specific {@link RepositoryFragmentsContributor} contributing Querydsl fragments if a repository |
||||||
|
* implements {@link QuerydslPredicateExecutor}. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
* @since 5.0 |
||||||
|
* @see ReactiveQuerydslMongoPredicateExecutor |
||||||
|
*/ |
||||||
|
enum ReactiveQuerydslContributor implements ReactiveMongoRepositoryFragmentsContributor { |
||||||
|
|
||||||
|
INSTANCE; |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, ReactiveMongoOperations operations) { |
||||||
|
|
||||||
|
if (isQuerydslRepository(metadata)) { |
||||||
|
|
||||||
|
ReactiveQuerydslPredicateExecutor<?> executor = new ReactiveQuerydslMongoPredicateExecutor<>(entityInformation, |
||||||
|
operations); |
||||||
|
|
||||||
|
return RepositoryComposition.RepositoryFragments |
||||||
|
.of(RepositoryFragment.implemented(ReactiveQuerydslPredicateExecutor.class, executor)); |
||||||
|
} |
||||||
|
|
||||||
|
return RepositoryComposition.RepositoryFragments.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) { |
||||||
|
|
||||||
|
if (isQuerydslRepository(metadata)) { |
||||||
|
return RepositoryComposition.RepositoryFragments.of(RepositoryFragment |
||||||
|
.structural(ReactiveQuerydslPredicateExecutor.class, ReactiveQuerydslMongoPredicateExecutor.class)); |
||||||
|
} |
||||||
|
|
||||||
|
return RepositoryComposition.RepositoryFragments.empty(); |
||||||
|
} |
||||||
|
|
||||||
|
private static boolean isQuerydslRepository(RepositoryMetadata metadata) { |
||||||
|
return QUERY_DSL_PRESENT |
||||||
|
&& ReactiveQuerydslPredicateExecutor.class.isAssignableFrom(metadata.getRepositoryInterface()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,109 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.aot; |
||||||
|
|
||||||
|
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.*; |
||||||
|
import static org.mockito.Mockito.*; |
||||||
|
|
||||||
|
import example.aot.User; |
||||||
|
import example.aot.UserRepository; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.aot.generate.GeneratedFiles; |
||||||
|
import org.springframework.aot.test.generate.TestGenerationContext; |
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||||
|
import org.springframework.context.annotation.ComponentScan; |
||||||
|
import org.springframework.context.annotation.FilterType; |
||||||
|
import org.springframework.context.aot.ApplicationContextAotGenerator; |
||||||
|
import org.springframework.core.io.InputStreamResource; |
||||||
|
import org.springframework.core.io.InputStreamSource; |
||||||
|
import org.springframework.data.aot.AotContext; |
||||||
|
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; |
||||||
|
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; |
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor; |
||||||
|
import org.springframework.mock.env.MockPropertySource; |
||||||
|
|
||||||
|
import com.mongodb.client.MongoClient; |
||||||
|
|
||||||
|
/** |
||||||
|
* Integration tests for AOT processing of imperative repositories. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
*/ |
||||||
|
class AotContributionIntegrationTests { |
||||||
|
|
||||||
|
@EnableMongoRepositories(considerNestedRepositories = true, includeFilters = { |
||||||
|
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = QuerydslUserRepository.class) }) |
||||||
|
static class AotConfiguration extends AbstractMongoClientConfiguration { |
||||||
|
|
||||||
|
@Override |
||||||
|
public MongoClient mongoClient() { |
||||||
|
return mock(MongoClient.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String getDatabaseName() { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
interface QuerydslUserRepository extends UserRepository, QuerydslPredicateExecutor<User> { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldGenerateMetadataForBaseRepositoryAndQuerydslFragment() throws IOException { |
||||||
|
|
||||||
|
TestGenerationContext generationContext = generate(AotConfiguration.class); |
||||||
|
|
||||||
|
InputStreamSource metadata = generationContext.getGeneratedFiles().getGeneratedFile(GeneratedFiles.Kind.RESOURCE, |
||||||
|
QuerydslUserRepository.class.getName().replace('.', '/') + ".json"); |
||||||
|
|
||||||
|
InputStreamResource isr = new InputStreamResource(metadata); |
||||||
|
String json = isr.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).isObject() //
|
||||||
|
.containsEntry("name", QuerydslUserRepository.class.getName()) //
|
||||||
|
.containsEntry("module", "MongoDB") //
|
||||||
|
.containsEntry("type", "IMPERATIVE"); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findBy')].fragment").isArray().first().isObject() |
||||||
|
.containsEntry("interface", "org.springframework.data.querydsl.QuerydslPredicateExecutor").containsEntry( |
||||||
|
"fragment", "org.springframework.data.mongodb.repository.support.QuerydslMongoPredicateExecutor"); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'existsById')].fragment").isArray().first().isObject() |
||||||
|
.containsEntry("fragment", "org.springframework.data.mongodb.repository.support.SimpleMongoRepository"); |
||||||
|
} |
||||||
|
|
||||||
|
private static TestGenerationContext generate(Class<?>... configurationClasses) { |
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); |
||||||
|
context.getEnvironment().getPropertySources() |
||||||
|
.addFirst(new MockPropertySource().withProperty(AotContext.GENERATED_REPOSITORIES_ENABLED, "true")); |
||||||
|
context.register(configurationClasses); |
||||||
|
|
||||||
|
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); |
||||||
|
|
||||||
|
TestGenerationContext generationContext = new TestGenerationContext(); |
||||||
|
generator.processAheadOfTime(context, generationContext); |
||||||
|
return generationContext; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,192 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.aot; |
||||||
|
|
||||||
|
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.*; |
||||||
|
import static org.assertj.core.api.Assertions.*; |
||||||
|
import static org.mockito.Mockito.*; |
||||||
|
|
||||||
|
import example.aot.UserRepository; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled; |
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
import org.junit.jupiter.api.extension.ExtendWith; |
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||||
|
import org.springframework.context.annotation.Bean; |
||||||
|
import org.springframework.context.annotation.Configuration; |
||||||
|
import org.springframework.context.support.AbstractApplicationContext; |
||||||
|
import org.springframework.core.io.Resource; |
||||||
|
import org.springframework.core.io.UrlResource; |
||||||
|
import org.springframework.data.mongodb.core.MongoOperations; |
||||||
|
import org.springframework.data.mongodb.test.util.MongoClientExtension; |
||||||
|
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; |
||||||
|
|
||||||
|
/** |
||||||
|
* Integration tests for the {@link UserRepository} JSON metadata via {@link MongoRepositoryContributor}. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
*/ |
||||||
|
@ExtendWith(MongoClientExtension.class) |
||||||
|
@SpringJUnitConfig(classes = MongoRepositoryMetadataTests.MongoRepositoryContributorConfiguration.class) |
||||||
|
class MongoRepositoryMetadataTests { |
||||||
|
|
||||||
|
@Configuration |
||||||
|
static class MongoRepositoryContributorConfiguration extends AotFragmentTestConfigurationSupport { |
||||||
|
|
||||||
|
public MongoRepositoryContributorConfiguration() { |
||||||
|
super(UserRepository.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Bean |
||||||
|
MongoOperations mongoOperations() { |
||||||
|
return mock(MongoOperations.class); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Autowired AbstractApplicationContext context; |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentBase() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).isObject() //
|
||||||
|
.containsEntry("name", UserRepository.class.getName()) //
|
||||||
|
.containsEntry("module", "MongoDB") //
|
||||||
|
.containsEntry("type", "IMPERATIVE"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentDerivedQuery() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'countUsersByLastname')].query").isArray().element(0).isObject() |
||||||
|
.containsEntry("filter", "{'lastname':?0}"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentSortedQuery() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findByLastnameStartingWithOrderByUsername')].query") //
|
||||||
|
.isArray().element(0).isObject() //
|
||||||
|
.containsEntry("filter", "{'lastname':{'$regex':/^\\Q?0\\E/}}") |
||||||
|
.containsEntry("sort", "{'username':{'$numberInt':'1'}}"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentPagedQuery() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findPageOfUsersByLastnameStartingWith')].query").isArray() |
||||||
|
.element(0).isObject().containsEntry("filter", "{'lastname':{'$regex':/^\\Q?0\\E/}}"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
@Disabled("No support for expressions yet") |
||||||
|
void shouldDocumentQueryWithExpression() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findValueExpressionNamedByEmailAddress')].query").isArray() |
||||||
|
.first().isObject().containsEntry("query", "select u from User u where u.emailAddress = :__$synthetic$__1"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentAggregation() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findAllLastnames')].query").isArray().element(0).isObject() |
||||||
|
.containsEntry("pipeline", |
||||||
|
"[{ '$match' : { 'last_name' : { '$ne' : null } } }, { '$project': { '_id' : '$last_name' } }]"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentPipelineUpdate() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findAndIncrementVisitsViaPipelineByLastname')].query").isArray() |
||||||
|
.element(0).isObject().containsEntry("filter", "{'lastname':?0}").containsEntry("update-pipeline", |
||||||
|
"[{ '$set' : { 'visits' : { '$ifNull' : [ {'$add' : [ '$visits', ?1 ] }, ?1 ] } } }]"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldDocumentBaseFragment() throws IOException { |
||||||
|
|
||||||
|
Resource resource = getResource(); |
||||||
|
|
||||||
|
assertThat(resource).isNotNull(); |
||||||
|
assertThat(resource.exists()).isTrue(); |
||||||
|
|
||||||
|
String json = resource.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
System.out.println(json); |
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'existsById')].fragment").isArray().first().isObject() |
||||||
|
.containsEntry("fragment", "org.springframework.data.mongodb.repository.support.SimpleMongoRepository"); |
||||||
|
} |
||||||
|
|
||||||
|
private Resource getResource() { |
||||||
|
|
||||||
|
String location = UserRepository.class.getPackageName().replace('.', '/') + "/" |
||||||
|
+ UserRepository.class.getSimpleName() + ".json"; |
||||||
|
return new UrlResource(context.getBeanFactory().getBeanClassLoader().getResource(location)); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,117 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.aot; |
||||||
|
|
||||||
|
import static net.javacrumbs.jsonunit.assertj.JsonAssertions.*; |
||||||
|
import static org.mockito.Mockito.*; |
||||||
|
|
||||||
|
import example.aot.User; |
||||||
|
import reactor.core.publisher.Flux; |
||||||
|
import reactor.core.publisher.Mono; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.aot.generate.GeneratedFiles; |
||||||
|
import org.springframework.aot.test.generate.TestGenerationContext; |
||||||
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
||||||
|
import org.springframework.context.annotation.ComponentScan; |
||||||
|
import org.springframework.context.annotation.FilterType; |
||||||
|
import org.springframework.context.aot.ApplicationContextAotGenerator; |
||||||
|
import org.springframework.core.io.InputStreamResource; |
||||||
|
import org.springframework.core.io.InputStreamSource; |
||||||
|
import org.springframework.data.aot.AotContext; |
||||||
|
import org.springframework.data.mongodb.config.AbstractMongoClientConfiguration; |
||||||
|
import org.springframework.data.mongodb.repository.config.EnableReactiveMongoRepositories; |
||||||
|
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; |
||||||
|
import org.springframework.data.repository.CrudRepository; |
||||||
|
import org.springframework.mock.env.MockPropertySource; |
||||||
|
|
||||||
|
import com.mongodb.client.MongoClient; |
||||||
|
|
||||||
|
/** |
||||||
|
* Integration tests for AOT processing of reactive repositories. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
*/ |
||||||
|
class ReactiveAotContributionIntegrationTests { |
||||||
|
|
||||||
|
@EnableReactiveMongoRepositories(considerNestedRepositories = true, includeFilters = { |
||||||
|
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = ReactiveQuerydslUserRepository.class) }) |
||||||
|
static class AotConfiguration extends AbstractMongoClientConfiguration { |
||||||
|
|
||||||
|
@Override |
||||||
|
public MongoClient mongoClient() { |
||||||
|
return mock(MongoClient.class); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected String getDatabaseName() { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
interface ReactiveQuerydslUserRepository |
||||||
|
extends CrudRepository<User, String>, ReactiveQuerydslPredicateExecutor<User> { |
||||||
|
|
||||||
|
Flux<User> findUserNoArgumentsBy(); |
||||||
|
|
||||||
|
Mono<User> findOneByUsername(String username); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test // GH-3830
|
||||||
|
void shouldGenerateMetadataForBaseRepositoryAndQuerydslFragment() throws IOException { |
||||||
|
|
||||||
|
TestGenerationContext generationContext = generate(AotConfiguration.class); |
||||||
|
|
||||||
|
InputStreamSource metadata = generationContext.getGeneratedFiles().getGeneratedFile(GeneratedFiles.Kind.RESOURCE, |
||||||
|
ReactiveQuerydslUserRepository.class.getName().replace('.', '/') + ".json"); |
||||||
|
|
||||||
|
InputStreamResource isr = new InputStreamResource(metadata); |
||||||
|
String json = isr.getContentAsString(StandardCharsets.UTF_8); |
||||||
|
|
||||||
|
assertThatJson(json).isObject() //
|
||||||
|
.containsEntry("name", ReactiveQuerydslUserRepository.class.getName()) //
|
||||||
|
.containsEntry("module", "MongoDB") //
|
||||||
|
.containsEntry("type", "REACTIVE"); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'findBy')].fragment").isArray().first().isObject() |
||||||
|
.containsEntry("interface", "org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor") |
||||||
|
.containsEntry("fragment", |
||||||
|
"org.springframework.data.mongodb.repository.support.ReactiveQuerydslMongoPredicateExecutor"); |
||||||
|
|
||||||
|
assertThatJson(json).inPath("$.methods[?(@.name == 'existsById')].fragment").isArray().first().isObject() |
||||||
|
.containsEntry("fragment", "org.springframework.data.mongodb.repository.support.SimpleReactiveMongoRepository"); |
||||||
|
} |
||||||
|
|
||||||
|
private static TestGenerationContext generate(Class<?>... configurationClasses) { |
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); |
||||||
|
context.getEnvironment().getPropertySources() |
||||||
|
.addFirst(new MockPropertySource().withProperty(AotContext.GENERATED_REPOSITORIES_ENABLED, "true")); |
||||||
|
context.register(configurationClasses); |
||||||
|
|
||||||
|
ApplicationContextAotGenerator generator = new ApplicationContextAotGenerator(); |
||||||
|
|
||||||
|
TestGenerationContext generationContext = new TestGenerationContext(); |
||||||
|
generator.processAheadOfTime(context, generationContext); |
||||||
|
return generationContext; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.support; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*; |
||||||
|
import static org.mockito.Mockito.*; |
||||||
|
|
||||||
|
import java.util.Iterator; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.MongoOperations; |
||||||
|
import org.springframework.data.mongodb.core.convert.MappingMongoConverter; |
||||||
|
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; |
||||||
|
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; |
||||||
|
import org.springframework.data.mongodb.repository.User; |
||||||
|
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; |
||||||
|
import org.springframework.data.querydsl.QuerydslPredicateExecutor; |
||||||
|
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; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragment; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link MongoRepositoryFragmentsContributor}. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
*/ |
||||||
|
class MongoRepositoryFragmentsContributorUnitTests { |
||||||
|
|
||||||
|
@Test // GH-3279
|
||||||
|
void composedContributorShouldCreateFragments() { |
||||||
|
|
||||||
|
MongoMappingContext mappingContext = new MongoMappingContext(); |
||||||
|
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); |
||||||
|
MongoOperations operations = mock(MongoOperations.class); |
||||||
|
when(operations.getConverter()).thenReturn(converter); |
||||||
|
|
||||||
|
MongoRepositoryFragmentsContributor contributor = MongoRepositoryFragmentsContributor.DEFAULT |
||||||
|
.andThen(MyMongoRepositoryFragmentsContributor.INSTANCE); |
||||||
|
|
||||||
|
RepositoryComposition.RepositoryFragments fragments = contributor.contribute( |
||||||
|
AbstractRepositoryMetadata.getMetadata(QuerydslUserRepository.class), |
||||||
|
new MappingMongoEntityInformation<>(mappingContext.getPersistentEntity(User.class)), operations); |
||||||
|
|
||||||
|
assertThat(fragments).hasSize(2); |
||||||
|
|
||||||
|
Iterator<RepositoryFragment<?>> iterator = fragments.iterator(); |
||||||
|
|
||||||
|
RepositoryFragment<?> querydsl = iterator.next(); |
||||||
|
assertThat(querydsl.getImplementationClass()).contains(QuerydslMongoPredicateExecutor.class); |
||||||
|
|
||||||
|
RepositoryFragment<?> additional = iterator.next(); |
||||||
|
assertThat(additional.getImplementationClass()).contains(MyFragment.class); |
||||||
|
} |
||||||
|
|
||||||
|
enum MyMongoRepositoryFragmentsContributor implements MongoRepositoryFragmentsContributor { |
||||||
|
|
||||||
|
INSTANCE; |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, MongoOperations operations) { |
||||||
|
return RepositoryComposition.RepositoryFragments.just(new MyFragment()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) { |
||||||
|
return RepositoryComposition.RepositoryFragments.just(new MyFragment()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static class MyFragment { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
interface QuerydslUserRepository extends Repository<User, Long>, QuerydslPredicateExecutor<User> {} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,93 @@ |
|||||||
|
/* |
||||||
|
* 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.mongodb.repository.support; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*; |
||||||
|
import static org.mockito.Mockito.*; |
||||||
|
|
||||||
|
import java.util.Iterator; |
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test; |
||||||
|
|
||||||
|
import org.springframework.data.mongodb.core.ReactiveMongoOperations; |
||||||
|
import org.springframework.data.mongodb.core.convert.MappingMongoConverter; |
||||||
|
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver; |
||||||
|
import org.springframework.data.mongodb.core.mapping.MongoMappingContext; |
||||||
|
import org.springframework.data.mongodb.repository.User; |
||||||
|
import org.springframework.data.mongodb.repository.query.MongoEntityInformation; |
||||||
|
import org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor; |
||||||
|
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; |
||||||
|
import org.springframework.data.repository.core.support.RepositoryFragment; |
||||||
|
|
||||||
|
/** |
||||||
|
* Unit tests for {@link ReactiveMongoRepositoryFragmentsContributor}. |
||||||
|
* |
||||||
|
* @author Mark Paluch |
||||||
|
*/ |
||||||
|
class ReactiveMongoRepositoryFragmentsContributorUnitTests { |
||||||
|
|
||||||
|
@Test // GH-3279
|
||||||
|
void composedContributorShouldCreateFragments() { |
||||||
|
|
||||||
|
MongoMappingContext mappingContext = new MongoMappingContext(); |
||||||
|
MappingMongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext); |
||||||
|
ReactiveMongoOperations operations = mock(ReactiveMongoOperations.class); |
||||||
|
when(operations.getConverter()).thenReturn(converter); |
||||||
|
|
||||||
|
ReactiveMongoRepositoryFragmentsContributor contributor = ReactiveMongoRepositoryFragmentsContributor.DEFAULT |
||||||
|
.andThen(MyMongoRepositoryFragmentsContributor.INSTANCE); |
||||||
|
|
||||||
|
RepositoryComposition.RepositoryFragments fragments = contributor.contribute( |
||||||
|
AbstractRepositoryMetadata.getMetadata(QuerydslUserRepository.class), |
||||||
|
new MappingMongoEntityInformation<>(mappingContext.getPersistentEntity(User.class)), operations); |
||||||
|
|
||||||
|
assertThat(fragments).hasSize(2); |
||||||
|
|
||||||
|
Iterator<RepositoryFragment<?>> iterator = fragments.iterator(); |
||||||
|
|
||||||
|
RepositoryFragment<?> querydsl = iterator.next(); |
||||||
|
assertThat(querydsl.getImplementationClass()).contains(ReactiveQuerydslMongoPredicateExecutor.class); |
||||||
|
|
||||||
|
RepositoryFragment<?> additional = iterator.next(); |
||||||
|
assertThat(additional.getImplementationClass()).contains(MyFragment.class); |
||||||
|
} |
||||||
|
|
||||||
|
enum MyMongoRepositoryFragmentsContributor implements ReactiveMongoRepositoryFragmentsContributor { |
||||||
|
|
||||||
|
INSTANCE; |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments contribute(RepositoryMetadata metadata, |
||||||
|
MongoEntityInformation<?, ?> entityInformation, ReactiveMongoOperations operations) { |
||||||
|
return RepositoryComposition.RepositoryFragments.just(new MyFragment()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public RepositoryComposition.RepositoryFragments describe(RepositoryMetadata metadata) { |
||||||
|
return RepositoryComposition.RepositoryFragments.just(new MyFragment()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static class MyFragment { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
interface QuerydslUserRepository extends Repository<User, Long>, ReactiveQuerydslPredicateExecutor<User> {} |
||||||
|
|
||||||
|
} |
||||||
Loading…
Reference in new issue