Spring DataのMongoTemplateとMongoRepositoryの違いは何ですか?


96

spring-dataとmongodbを使用して複雑なクエリを実行できるアプリケーションを作成する必要があります。私はMongoRepositoryを使用することから始めましたが、例を見つけたり、構文を実際に理解したりするための複雑なクエリに苦労していました。

私はこのようなクエリについて話している:

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    List<User> findByEmailOrLastName(String email, String lastName);
}

または、構文が正しくないので試行錯誤して試したJSONベースのクエリの使用。mongodbのドキュメントを読んだ後でも(構文が正しくないため機能しない例)。

@Repository
public interface UserRepositoryInterface extends MongoRepository<User, String> {
    @Query("'$or':[{'firstName':{'$regex':?0,'$options':'i'}},{'lastName':{'$regex':?0,'$options':'i'}}]")
    List<User> findByEmailOrFirstnameOrLastnameLike(String searchText);
} 

すべてのドキュメントを読んだ後、それmongoTemplateははるかにドキュメント化されているようですMongoRepository。私は次のドキュメントを参照しています:

http://static.springsource.org/spring-data/data-mongodb/docs/current/reference/html/

より便利で強力な使い方を教えてください。mongoTemplateまたはMongoRepository?どちらも成熟していますか、それとも一方が他方よりも多くの機能を欠いていますか?

回答:


130

「便利」と「強力」は、ある程度矛盾する目標です。リポジトリはテンプレートよりもはるかに便利ですが、後者はもちろん、何を実行するかをより細かく制御できます。

リポジトリプログラミングモデルは複数のSpring Dataモジュールで利用できるため、Spring Data MongoDB リファレンスドキュメントの一般的なセクションに、より詳細なドキュメントがあります。

TL; DR

通常、次のアプローチをお勧めします。

  1. リポジトリの概要から始め、クエリ派生メカニズムまたは手動で定義されたクエリを使用して単純なクエリを宣言します。
  2. より複雑なクエリの場合は、手動で実装したメソッドをリポジトリに追加します(ここに記載されています)。実装にはを使用しますMongoTemplate

細部

あなたの例では、これは次のようになります:

  1. カスタムコードのインターフェイスを定義します。

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
  2. このクラスの実装を追加し、命名規則に従ってクラスを見つけられるようにします。

    class UserRepositoryImpl implements CustomUserRepository {
    
      private final MongoOperations operations;
    
      @Autowired
      public UserRepositoryImpl(MongoOperations operations) {
    
        Assert.notNull(operations, "MongoOperations must not be null!");
        this.operations = operations;
      }
    
      public List<User> yourCustomMethod() {
        // custom implementation here
      }
    }
  3. 次に、ベースリポジトリインターフェースでカスタムインターフェースを拡張すると、インフラストラクチャがカスタム実装を自動的に使用します。

    interface UserRepository extends CrudRepository<User, Long>, CustomUserRepository {
    
    }

このようにして、基本的に選択肢を得ることができます。宣言するのが簡単なUserRepositoryものはすべて、手動で実装した方がよいものすべてが入りCustomUserRepositoryます。カスタマイズオプションはここに記載されています


こんにちはオリバー、これは実際には機能しません。spring-dataは、カスタム名からクエリを自動生成しようとします。yourCustomMethod()。「your」はドメインクラスの有効なフィールドではないと表示されます。私はマニュアルに従って、あなたがそれをどのようにしているかを再確認しましたspring-data-jpa-examples 運が悪い。spring-dataは、カスタムインターフェイスをリポジトリクラスに拡張するとすぐに、常に自動生成を試みます。唯一の違いは、今のところイテレータを使用したくないので、CrudRepositoryではなくMongoRepositoryを使用していることです。ヒントをお持ちの場合は、いただければ幸いです。
クリストファーアームストロング

10
最も一般的な間違いは、実装クラスの間違った名前を付けることです:お使いのベースレポインタフェースが呼び出された場合YourRepository、実装クラスは、指定する必要がありますYourRepositoryImpl。それは事実ですか?もしそうなら、私は...サンプルGitHubの上のプロジェクトなどを見てて幸せ
オリバーDrotbohm

5
こんにちはオリバー、あなたが想定したようにImplクラスは間違った名前が付けられました。私は名前を調整しました、そしてそれは今それが機能しているようです。フィードバックありがとうございます。この方法でさまざまな種類のクエリオプションを使用できるのは本当に素晴らしいことです。よく考え抜かれた!
クリストファーアームストロング

この答えは明確ではありません。この例ですべてを実行した後、私はこの問題に陥ります:stackoverflow.com/a/13947263/449553。したがって、命名規則は、この例のように見えるよりも厳密です。
msangel 2013

1
#2の実装クラスの名前が間違っています:である必要がCustomUserRepositoryありCustomerUserRepositoryます。
Cotta

28

この回答は少し遅れる可能性がありますが、リポジトリ全体のルートを回避することをお勧めします。実用的な価値のある実装されたメソッドはほとんどありません。それを機能させるために、ドキュメンテーションであまり助けなくても何日も何週間も過ごすことができるJava構成ナンセンスに遭遇します。

代わりに、MongoTemplateルートを使用して独自のデータアクセスレイヤーを作成し、Springプログラマーが直面する構成の悪夢から解放されます。MongoTemplate柔軟性が非常に高いので、独自のクラスやインタラクションの設計に慣れているエンジニアにとって本当に救世主です。構造は次のようになります。

  1. MongoClientFactoryアプリケーションレベルで実行するクラスを作成し、MongoClientオブジェクトます。これをシングルトンとして、またはEnumシングルトンを使用して実装できます(これはスレッドセーフです)。
  2. 各ドメインオブジェクトのデータアクセスオブジェクトを継承できるデータアクセス基本クラスを作成します)。基本クラスは、MongoTemplateオブジェクトを作成するためのメソッドを実装できます。これは、特定のメソッドをクラス化して、すべてのDBアクセスに使用できます。
  3. 各ドメインオブジェクトの各データアクセスクラスは、基本的なメソッドを実装するか、基本クラスに実装できます。
  4. その後、コントローラーメソッドは、必要に応じてデータアクセスクラスのメソッドを呼び出すことができます。

こんにちは@rameshpaは、私は、同じプロジェクト内のMongoTemplate&リポジトリの両方を使用することができます。..を使用することが可能ですか?
Gauranga

1
ただし、実装するMongoTemplateは、Repositoryで使用される接続とは異なるDBへの接続を持つ可能性があります。原子性が問題になる可能性があります。また、シーケンス処理が必要な場合は、1つのスレッドで2つの異なる接続を使用することはお勧めしません
rameshpa

23

FWIW、マルチスレッド環境での更新に関して:

  • MongoTemplate提供「アトミック」アウト・オブ・ボックスの操作は updateFirstupdateMultifindAndModifyupsert...あなたは、単一の操作でドキュメントを変更することができます。Updateこれらの方法で使用されるオブジェクトも、あなたが唯一の関連分野をターゲットとすることができます
  • MongoRepositoryあなたに与えられるだけで、基本的なCRUD操作を findinsertsavedelete含むのPOJOと、その作業のすべてのフィールドを。これにより、いくつかの手順でfindドキュメントを更新する(1.更新するドキュメント、2。返されたPOJOから関連するフィールドを変更し、3。saveそれを変更する)、またはを使用して手動で独自の更新クエリを定義する必要があります@Query

複数のRESTエンドポイントを持つJavaバックエンドなどのマルチスレッド環境では、2つの同時更新が互いの変更を上書きする可能性を減らすために、単一メソッドの更新が適しています。

例:次のようなドキュメントがあるとします: { _id: "ID1", field1: "a string", field2: 10.0 } 2つの異なるスレッドが同時に更新する...

ではMongoTemplate、それはやや次のようになります。

THREAD_001                                                      THREAD_002
|                                                               |
|update(query("ID1"), Update().set("field1", "another string")) |update(query("ID1"), Update().inc("field2", 5))
|                                                               |
|                                                               |

文書のための最終的な状態は、常に{ _id: "ID1", field1: "another string", field2: 15.0 }、各スレッドが一度だけDBをaccesingているため指定されたフィールドが変更されるのみ。

これと同じケースのシナリオMongoRepositoryは次のようになります。

THREAD_001                                                      THREAD_002
|                                                               |
|pojo = findById("ID1")                                         |pojo = findById("ID1")
|pojo.setField1("another string") /* field2 still 10.0 */       |pojo.setField2(pojo.getField2()+5) /* field1 still "a string" */
|save(pojo)                                                     |save(pojo)
|                                                               |
|                                                               |

最終的なドキュメントは、最後にDBにヒットする操作のいずれか、{ _id: "ID1", field1: "another string", field2: 10.0 }またはそれに{ _id: "ID1", field1: "a string", field2: 15.0 }依存しsaveます。
(注:コメントで提案されているようにSpring Dataの@Versionアノテーションを使用したとしても、それほど大きな変化はありません。save操作の1つがOptimisticLockingFailureException、最終的なドキュメントは上記のいずれかであり、両方ではなく1つのフィールドのみが更新されます。 )

したがって、非常に複雑なPOJOモデルがある場合や、何らかの理由でのカスタムクエリ機能が必要な場合を除いて、これがMongoTemplateより良いオプションだと思いますMongoRepository


良い点/例。ただし、@ Versionを使用すると、競合状態の例と望ましくない結果を回避して、まさにそのシナリオを回避できます。
Madbreaks、

@Madbreaksこれを達成する方法に関するリソースを提供できますか?おそらく正式なドキュメントはありますか?
Karthikeyan

@Versionアノテーションについて春データドキュメント:docs.spring.io/spring-data/mongodb/docs/current/reference/html/...
カリムTawfik

1
@Madbreaksそれを指摘してくれてありがとう。はい、@Version2番目のスレッドが最初のスレッドによって保存されたデータを上書きすることを「回避」します—更新を破棄してOptimisticLockingFailureException代わりにスローするという意味で「回避」します。したがって、更新を成功させるには、再試行メカニズムを実装する必要があります。MongoTemplateを使用すると、シナリオ全体を回避できます。
ワレン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.