Spring Integration

نقوم بربط تخطيط ReactiveMongoTemplate، وتسمية المجموعات، ودورة الحياة التفاعلية بمحول MongoExecutionContext دون سحب Spring Data MongoDB كاعتماد أساسي.


تحتفظ Spring Data MongoDB بالتطبيق.

build.gradle
text
dependencies {
  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
java
public 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

java
@Field("user_name")
private String userName;

// MongoDB document field 이름을 사용
.fields(pair("user_name", "kim"))

يتم تطبيق تخطيط Spring على تحويل حفظ/استرجاع الكيان، لكن مسار String الشرط DSL لا يمر عبر QueryMapper الخاص بـ Spring. ومع ذلك، يتم تطبيع الجزء الدقيق من مسار id إلى _id وفقًا لقواعد DSL نفسها.

معاملات Spring ومعاملات DSL منفصلة.

© 2026 بيولناريم. جميع الحقوق محفوظة.مقدمةسياسة معالجة المعلومات الشخصية