Apa perbedaan antara MongoTemplate Spring Data dan MongoRepository?

102

Saya perlu menulis aplikasi yang dapat saya gunakan untuk melakukan kueri kompleks menggunakan data musim semi dan mongodb. Saya telah mulai dengan menggunakan MongoRepository tetapi berjuang dengan kueri kompleks untuk menemukan contoh atau benar-benar memahami Sintaksnya.

Saya berbicara tentang pertanyaan seperti ini:

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

atau penggunaan kueri berbasis JSON yang saya coba dengan coba-coba karena saya tidak mendapatkan sintaks yang benar. Bahkan setelah membaca dokumentasi mongodb (contoh tidak berfungsi karena sintaks yang salah).

@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);
} 

Setelah membaca semua dokumentasi, tampaknya itu mongoTemplatejauh lebih baik didokumentasikan MongoRepository. Saya mengacu pada dokumentasi berikut:

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

Bisakah Anda memberi tahu saya apa yang lebih nyaman dan kuat untuk digunakan? mongoTemplateatau MongoRepository? Apakah keduanya sama dewasa atau apakah salah satu dari mereka kekurangan lebih banyak fitur daripada yang lain?

Christopher Armstrong
sumber

Jawaban:

136

"Nyaman" dan "kuat untuk digunakan" adalah tujuan yang saling bertentangan sampai tingkat tertentu. Repositori jauh lebih nyaman daripada templat tetapi yang terakhir tentu saja memberi Anda kontrol yang lebih baik atas apa yang harus dieksekusi.

Karena model pemrograman repositori tersedia untuk beberapa modul Data Musim Semi, Anda akan menemukan dokumentasi yang lebih mendalam tentangnya di bagian umum dokumen referensi MongoDB Data Musim Semi .

TL; DR

Kami biasanya merekomendasikan pendekatan berikut:

  1. Mulailah dengan repositori abstrak dan cukup nyatakan kueri sederhana menggunakan mekanisme derivasi kueri atau kueri yang ditentukan secara manual.
  2. Untuk kueri yang lebih kompleks, tambahkan metode yang diimplementasikan secara manual ke repositori (seperti yang didokumentasikan di sini). Untuk penggunaan implementasi MongoTemplate.

Detail

Untuk contoh Anda, ini akan terlihat seperti ini:

  1. Tentukan antarmuka untuk kode kustom Anda:

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
    
  2. Tambahkan implementasi untuk kelas ini dan ikuti konvensi penamaan untuk memastikan kita dapat menemukan kelas tersebut.

    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. Sekarang biarkan antarmuka repositori dasar Anda memperluas yang khusus dan infrastruktur akan secara otomatis menggunakan implementasi khusus Anda:

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

Dengan cara ini Anda pada dasarnya mendapatkan pilihan: semua yang mudah dideklarasikan masuk ke UserRepository, semua yang lebih baik diterapkan secara manual masuk ke dalamnya CustomUserRepository. Opsi penyesuaian didokumentasikan di sini .

Oliver Drotbohm
sumber
1
Hai Oliver, ini sebenarnya tidak berhasil. spring-data mencoba membuat kueri dari nama kustom secara otomatis. yourCustomMethod (). Ini akan mengatakan "Anda" bukan bidang yang valid di kelas domain. Saya mengikuti manual dan juga memeriksa ulang bagaimana Anda melakukannya dengan contoh pegas-data-jpa. Tidak beruntung. spring-data selalu mencoba untuk menghasilkan otomatis segera setelah saya memperluas antarmuka kustom ke kelas repositori. Satu-satunya perbedaan adalah saya menggunakan MongoRepository dan bukan CrudRepository karena saya tidak ingin bekerja dengan Iterator untuk saat ini. Jika Anda memiliki petunjuk, itu akan dihargai.
Christopher Armstrong
11
Kesalahan paling umum adalah menamai kelas implementasi dengan salah: jika antarmuka repo dasar Anda dipanggil YourRepository, kelas implementasi harus dinamai YourRepositoryImpl. Apakah itu masalahnya? Jika demikian, saya senang melihat proyek sampel di GitHub atau sejenisnya…
Oliver Drotbohm
5
Hai Oliver, kelas Impl dinamai salah seperti yang Anda asumsikan. Saya menyesuaikan nama dan sepertinya berfungsi sekarang. Terima kasih banyak atas tanggapan Anda. Sangat keren untuk dapat menggunakan berbagai jenis opsi kueri dengan cara ini. Dipikirkan dengan baik!
Christopher Armstrong
Jawaban ini tidak begitu jelas. Setelah melakukan semuanya dengan contoh ini saya jatuh ke masalah ini: stackoverflow.com/a/13947263/449553 . Jadi konvensi penamaan lebih ketat daripada yang terlihat dari contoh ini.
msangel
1
Kelas implementasi pada # 2 dinamai salah: seharusnya CustomUserRepositorydan tidak CustomerUserRepository.
Cotta
27

Jawaban ini mungkin sedikit tertunda, tetapi saya akan merekomendasikan untuk menghindari seluruh rute repositori. Anda mendapatkan sangat sedikit metode yang diimplementasikan dengan nilai praktis yang besar. Untuk membuatnya berfungsi, Anda mengalami omong kosong konfigurasi Java yang dapat Anda habiskan berhari-hari dan berminggu-minggu tanpa banyak bantuan dalam dokumentasi.

Sebaliknya, ikuti MongoTemplaterute dan buat lapisan akses Data Anda sendiri yang membebaskan Anda dari mimpi buruk konfigurasi yang dihadapi oleh pemrogram Spring. MongoTemplatebenar-benar penyelamat bagi para insinyur yang merasa nyaman merancang kelas dan interaksi mereka sendiri karena ada banyak fleksibilitas. Strukturnya bisa seperti ini:

  1. Buat MongoClientFactorykelas yang akan berjalan di tingkat aplikasi dan memberi AndaMongoClient objek. Anda dapat menerapkan ini sebagai Singleton atau menggunakan Enum Singleton (ini thread safe)
  2. Buat kelas basis akses Data tempat Anda dapat mewarisi objek akses data untuk setiap objek domain). Kelas dasar dapat mengimplementasikan metode untuk membuat objek MongoTemplate yang dapat digunakan metode khusus kelas Anda untuk semua akses DB
  3. Setiap kelas akses data untuk setiap objek domain dapat menerapkan metode dasar atau Anda dapat menerapkannya di kelas dasar
  4. Metode Pengontrol kemudian dapat memanggil metode di kelas akses Data sesuai kebutuhan.
rameshpa
sumber
Hai @rameshpa Dapatkah saya menggunakan MongoTemplate & repositori dalam proyek yang sama? .. Apakah mungkin untuk digunakan
Gauranga
1
Anda bisa tetapi MongoTemplate yang Anda implementasikan akan memiliki koneksi yang berbeda ke DB dari koneksi yang digunakan oleh Repositori. Atomicity bisa menjadi masalah. Juga saya tidak akan merekomendasikan menggunakan dua koneksi berbeda pada satu utas jika Anda memiliki kebutuhan pengurutan
rameshpa
27

FWIW, terkait pembaruan di lingkungan multi-utas:

  • MongoTemplatememberikan "atom" out-of-the-box operasi updateFirst , updateMulti, findAndModify, upsert... yang memungkinkan Anda untuk memodifikasi dokumen dalam satu operasi. The Updateobjek yang digunakan oleh metode ini juga memungkinkan Anda untuk menargetkan hanya bidang yang relevan .
  • MongoRepositoryhanya memberikan Anda operasi CRUD dasar find , insert, save, delete, yang bekerja dengan POJOs yang berisi semua bidang . Ini memaksa Anda untuk memperbarui dokumen dalam beberapa langkah (1. finddokumen untuk diperbarui, 2. memodifikasi bidang yang relevan dari POJO yang dikembalikan, dan 3. kemudian save), atau menentukan kueri pembaruan Anda sendiri dengan menggunakan tangan @Query.

Dalam lingkungan multi-utas, seperti misalnya back-end Java dengan beberapa titik akhir REST, pembaruan metode tunggal adalah cara yang harus dilakukan, untuk mengurangi kemungkinan dua pembaruan bersamaan menimpa perubahan satu sama lain.

Contoh: diberi dokumen seperti ini: { _id: "ID1", field1: "a string", field2: 10.0 }dan dua utas berbeda secara bersamaan memperbaruinya ...

Dengan MongoTemplateitu akan terlihat seperti ini:

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

dan status akhir untuk dokumen selalu { _id: "ID1", field1: "another string", field2: 15.0 }karena setiap utas mengakses DB hanya sekali dan hanya bidang yang ditentukan yang diubah.

Sedangkan skenario kasus yang sama dengan MongoRepositoryakan terlihat seperti ini:

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)
|                                                               |
|                                                               |

dan dokumen akhir menjadi salah satu { _id: "ID1", field1: "another string", field2: 10.0 }atau { _id: "ID1", field1: "a string", field2: 15.0 }bergantung pada saveoperasi mana yang terakhir kali mencapai DB.
(CATATAN: Bahkan jika kita menggunakan anotasi Spring Data@Version seperti yang disarankan dalam komentar, tidak banyak yang akan berubah: salah satu saveoperasi akan membuang OptimisticLockingFailureException, dan dokumen terakhir akan tetap menjadi salah satu dari yang di atas, dengan hanya satu bidang yang diperbarui, bukan keduanya. )

Jadi menurut saya itu MongoTemplateadalah opsi yang lebih baik , kecuali Anda memiliki model POJO yang sangat rumit atau memerlukan kemampuan kueri khusus MongoRepositorykarena alasan tertentu.

walen
sumber
Poin / contoh bagus. Namun contoh kondisi balapan Anda dan hasil yang tidak diinginkan dapat dihindari menggunakan @Version untuk mencegah skenario itu.
Madbreaks
@Madbreaks Dapatkah Anda memberikan sumber daya tentang cara mencapai ini? Mungkin ada dokter resmi?
Karthikeyan
Dokumen data Spring tentang anotasi @Version: @Versi docs.spring.io/spring-data/mongodb/docs/current/reference/html/…
Karim Tawfik
1
@Madbreaks Terima kasih telah menunjukkan hal itu. Iya,@Version akan "menghindari" utas kedua menimpa data yang disimpan oleh yang pertama - "menghindari" dalam arti bahwa ia akan membuang pembaruan dan sebagai OptimisticLockingFailureExceptiongantinya. Jadi, Anda harus menerapkan mekanisme coba lagi jika ingin pembaruan berhasil. MongoTemplate memungkinkan Anda menghindari seluruh skenario.
Walen