Spring Integration
نقوم بربط تخطيط ReactiveMongoTemplate، وتسمية المجموعات، ودورة الحياة التفاعلية بمحول MongoExecutionContext دون سحب Spring Data MongoDB كاعتماد أساسي.
تحتفظ Spring Data MongoDB بالتطبيق.
build.gradle
textdependencies {
implementation("com.byeolnaerim:reactive-mongo-dsl:1.0.0")
implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
}المكتبة الأساسية لا تعتمد مباشرة على Spring Data MongoDB. لتطبيق إعدادات ReactiveMongoTemplate لتطبيق Spring الحالي، يتم توفير محول MongoExecutionContext في طبقة التطبيق.
محول SpringReactiveMongoExecutionContext
SpringReactiveMongoExecutionContext.java
javapublic class SpringReactiveMongoExecutionContext implements MongoExecutionContext {
private final ReactiveMongoTemplate reactiveMongoTemplate;
private final MongoClient mongoClient;
private final MongoConverter mongoConverter;
private final ReactiveEntityCallbacks entityCallbacks;
public SpringReactiveMongoExecutionContext(
ReactiveMongoTemplate reactiveMongoTemplate,
MongoClient mongoClient,
ReactiveEntityCallbacks entityCallbacks
) {
this.reactiveMongoTemplate = Objects.requireNonNull(reactiveMongoTemplate);
this.mongoClient = Objects.requireNonNull(mongoClient);
this.mongoConverter = reactiveMongoTemplate.getConverter();
this.entityCallbacks = Objects.requireNonNull(entityCallbacks);
}
@Override
public Mono<MongoDatabase> getDatabase() {
return reactiveMongoTemplate.getMongoDatabase();
}
@Override
public Mono<ClientSession> startSession() {
return Mono.from(mongoClient.startSession());
}
@Override
public String getCollectionName(Class<?> entityClass) {
return reactiveMongoTemplate.getCollectionName(entityClass);
}
@Override
public Document write(Object source) {
Document document = new Document();
mongoConverter.write(source, document);
return document;
}
@Override
public <T> T read(Class<T> targetType, Document source) {
return mongoConverter.read(targetType, source);
}
@Override
public Object getId(Object entity) {
MongoPersistentEntity<?> persistentEntity =
mongoConverter.getMappingContext().getPersistentEntity(entity.getClass());
if (persistentEntity == null || !persistentEntity.hasIdProperty()) {
return null;
}
Object id = persistentEntity.getIdentifierAccessor(entity).getIdentifier();
if (id == null) return null;
Document document = new Document();
mongoConverter.write(entity, document);
return document.get("_id");
}
@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public void setId(Object entity, Object id) {
MongoPersistentEntity persistentEntity =
mongoConverter.getMappingContext().getPersistentEntity(entity.getClass());
if (persistentEntity == null || !persistentEntity.hasIdProperty()) return;
MongoPersistentProperty idProperty =
(MongoPersistentProperty) persistentEntity.getRequiredIdProperty();
Object targetId = id;
if (id instanceof ObjectId objectId && idProperty.getType() == String.class) {
targetId = objectId.toHexString();
} else if (
id != null
&& !idProperty.getType().isInstance(id)
&& mongoConverter.getConversionService()
.canConvert(id.getClass(), idProperty.getType())
) {
targetId = mongoConverter.getConversionService()
.convert(id, idProperty.getType());
}
PersistentPropertyAccessor accessor = persistentEntity.getPropertyAccessor(entity);
accessor.setProperty(idProperty, targetId);
}
@Override
public <T> Mono<T> beforePersist(T entity, String collectionName) {
return entityCallbacks.callback(
ReactiveBeforeConvertCallback.class,
entity,
collectionName
);
}
@Override
public <T> Mono<T> afterPersist(
T entity,
Document document,
String collectionName
) {
return entityCallbacks.callback(
ReactiveAfterSaveCallback.class,
entity,
document,
collectionName
);
}
@Override
public Object getSessionScope() {
return mongoClient;
}
@Override
public Object getNative() {
return reactiveMongoTemplate;
}
@Override
public <T> T getNative(Class<T> nativeType) {
if (nativeType.isInstance(reactiveMongoTemplate)) {
return nativeType.cast(reactiveMongoTemplate);
}
if (nativeType.isInstance(mongoClient)) {
return nativeType.cast(mongoClient);
}
return MongoExecutionContext.super.getNative(nativeType);
}
}في التنفيذ الفعلي، يتم إضافة استيرادات Spring/MongoDB اللازمة إلى الفئة أعلاه. النقطة الأساسية هي ربط قراءة/كتابة الكيان بـ MongoConverter، واسم المجموعة بـ ReactiveMongoTemplate، وبدء الجلسة بـ MongoClient المقابل.
تسجيل Resolver و DSL Bean
MongoTemplateNameResolver.java
java@Component
public class MongoTemplateNameResolver
implements MongoTemplateResolver<MongoTemplateName> {
private final MongoExecutionContext frontExecutionContext;
private final MongoExecutionContext backExecutionContext;
public MongoTemplateNameResolver(
@Qualifier("frontMongoTemplate") ReactiveMongoTemplate frontTemplate,
@Qualifier("frontMongoClient") MongoClient frontMongoClient,
@Qualifier("backMongoTemplate") ReactiveMongoTemplate backTemplate,
@Qualifier("backMongoClient") MongoClient backMongoClient,
ApplicationContext applicationContext
) {
ReactiveEntityCallbacks callbacks =
ReactiveEntityCallbacks.create(applicationContext);
this.frontExecutionContext = new SpringReactiveMongoExecutionContext(
frontTemplate, frontMongoClient, callbacks
);
this.backExecutionContext = new SpringReactiveMongoExecutionContext(
backTemplate, backMongoClient, callbacks
);
}
@Override
public MongoExecutionContext getTemplate(MongoTemplateName key) {
return switch (key) {
case FRONT -> frontExecutionContext;
case BACK -> backExecutionContext;
};
}
}ReactiveMongoDslConfiguration.java
java@Bean
public ReactiveMongoDsl<MongoTemplateName> reactiveMongoDsl(
MongoTemplateResolver<MongoTemplateName> resolver
) {
return new ReactiveMongoDsl<>(resolver);
}نطاق الاتصال في المحول
@Document / اسم المجموعة
يتم استخدام ReactiveMongoTemplate#getCollectionName(...).
@Id / @Field / تحويلات مخصصة
يتم استخدام MongoConverter لتحويل الكيان ↔ Document.
التدقيق التفاعلي
عند ربط beforePersist(...) بـ ReactiveBeforeConvertCallback، يتم تطبيق ردود الفعل على التدقيق التفاعلي في سلسلة save().
ReactiveAfterSaveCallback
يمكن استدعاؤه في afterPersist(...) بعد نجاح الكتابة.
Spring QueryMapper
لا يتم تطبيقه. مسار حقل DSL String يعتمد على اسم حقل مستند MongoDB الفعلي.
Spring DataAccessException
لا يتم التحويل تلقائيًا. العملية الفعلية تتم بواسطة Reactive Streams Driver.
عند استخدام @Field
@Field("user_name")
private String userName;
// MongoDB document field 이름을 사용
.fields(pair("user_name", "kim"))يتم تطبيق تخطيط Spring على تحويل حفظ/استرجاع الكيان، لكن مسار String الشرط DSL لا يمر عبر QueryMapper الخاص بـ Spring. ومع ذلك، يتم تطبيع الجزء الدقيق من مسار id إلى _id وفقًا لقواعد DSL نفسها.