Spring Integration

Spring Data MongoDBをコア依存関係として取り込まずに、ReactiveMongoTemplateのマッピング、コレクション名付け、リアクティブライフサイクルをMongoExecutionContextアダプターに接続します。


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に直接依存しません。既存のSpringアプリケーションのReactiveMongoTemplate設定を使用するには、アプリケーション層で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 / カスタム変換

エンティティ ↔ Document 変換にMongoConverterを使用します。

リアクティブ監査

beforePersist(...)をReactiveBeforeConvertCallbackに接続すると、save()系列でリアクティブ監査コールバックが適用されます。

ReactiveAfterSaveCallback

書き込み成功後、afterPersist(...)で呼び出すことができます。

Spring QueryMapper

適用されません。DSL文字列フィールドパスは実際のMongoDBドキュメントフィールド名に基づいています。

Spring DataAccessException

自動変換しません。実際の操作はReactive Streams Driverが実行します。

@Fieldを使用する場合

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

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

エンティティの保存/取得変換にはSpringマッピングが適用されますが、DSL条件の文字列パスはSpring QueryMapperを通過しません。ただし、正確なidパスセグメントはDSL自体の規則により_idに正規化されます。

SpringトランザクションとDSLトランザクションは別々です

© 2026 Byeolnaerim. 無断転載を禁じます。紹介プライバシーポリシー