Spring Data'nın MongoTemplate'i ile MongoRepository arasındaki fark nedir?


104

Spring-data ve mongodb kullanarak karmaşık sorgular yapabileceğim bir uygulama yazmam gerekiyor. MongoRepository'yi kullanarak başlıyordum, ancak örnekler bulmak veya Sözdizimini gerçekten anlamak için karmaşık sorgularla uğraştım.

Bunun gibi sorgulardan bahsediyorum:

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

veya sözdizimini doğru anlamadığım için deneme yanılma yoluyla denediğim JSON tabanlı sorguların kullanımı. Mongodb belgelerini okuduktan sonra bile (yanlış sözdizimi nedeniyle çalışmayan örnek).

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

Tüm belgeleri okuduktan sonra, o mongoTemplatezaman çok daha iyi belgelenmiş görünüyor MongoRepository. Aşağıdaki belgelere atıfta bulunuyorum:

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

Bana neyin daha kullanışlı ve daha güçlü olduğunu söyleyebilir misiniz? mongoTemplateveya MongoRepository? İkisi de aynı olgun mu yoksa biri diğerinden daha fazla özelliğe mi sahip?

Yanıtlar:


138

"Kullanışlı" ve "kullanımı güçlü", hedeflerle bir dereceye kadar çelişen hedeflerdir. Depolar şablonlardan çok daha kullanışlıdır, ancak ikincisi elbette size neyi çalıştıracağınız konusunda daha ayrıntılı kontrol sağlar.

Depo programlama modeli birden fazla Spring Data modülü için mevcut olduğundan, Spring Data MongoDB referans belgelerinin genel bölümünde bununla ilgili daha ayrıntılı belgeler bulacaksınız .

TL; DR

Genellikle aşağıdaki yaklaşımı öneriyoruz:

  1. Depo özetiyle başlayın ve yalnızca sorgu türetme mekanizmasını veya manuel olarak tanımlanmış sorguları kullanarak basit sorgular bildirin.
  2. Daha karmaşık sorgular için, depoya manuel olarak uygulanan yöntemler ekleyin (burada belgelendiği gibi). Uygulama kullanımı için MongoTemplate.

Detaylar

Örneğiniz için bu şuna benzer:

  1. Özel kodunuz için bir arayüz tanımlayın:

    interface CustomUserRepository {
    
      List<User> yourCustomMethod();
    }
    
  2. Bu sınıf için bir uygulama ekleyin ve sınıfı bulabildiğimizden emin olmak için adlandırma kuralını izleyin.

    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. Şimdi temel depo arayüzünüzün özel olanı genişletmesine izin verin ve altyapı otomatik olarak özel uygulamanızı kullanacaktır:

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

Bu şekilde esasen seçim yaparsınız: Bildirilmesi kolay olan UserRepositoryher şey içine girer , manuel olarak daha iyi uygulanan her şey içine girer CustomUserRepository. Özelleştirme seçenekleri burada belgelenmiştir .


1
Merhaba Oliver, bu aslında işe yaramıyor. spring-data, özel addan otomatik olarak bir sorgu oluşturmaya çalışır. yourCustomMethod (). "Sizin" alan sınıfında geçerli bir alan olmadığını söyleyecektir. Kılavuzu takip ettim ve bunu nasıl yaptığınızı iki kez kontrol ettim yay-verisi-jpa-örnekleri Şanssız. Spring-data, özel arayüzü depo sınıfına genişlettiğimde her zaman otomatik olarak oluşturmaya çalışır. Tek fark, şimdilik Yineleyicilerle çalışmak istemediğim için CrudRepository değil MongoRepository kullanıyorum. Bir ipucunuz varsa memnun oluruz.
Christopher Armstrong

12
En yaygın hata, uygulama sınıfını yanlış adlandırmaktır: temel repo arayüzünüz çağrılırsa YourRepository, uygulama sınıfının adlandırılması gerekir YourRepositoryImpl. Durum bu mu? Eğer ben GitHub üzerinde örnek bir proje bir göz ya ... gibi almaya mutluyum bu yüzden
Oliver Drotbohm

5
Merhaba Oliver, tahmin ettiğiniz gibi Impl sınıfı yanlış adlandırıldı. İsmi değiştirdim ve şimdi çalışıyor gibi görünüyor. Geri bildiriminiz için çok teşekkürler. Bu şekilde farklı türden sorgu seçeneklerini kullanabilmek gerçekten harika. İyi düşünülmüş!
Christopher Armstrong

Bu cevap o kadar net değil. Her şeyi bu örnekle yaptıktan sonra şu konuya düşüyorum: stackoverflow.com/a/13947263/449553 . Dolayısıyla, adlandırma kuralı bu örnekte göründüğünden daha katıdır.
msangel

1
# 2'deki uygulama sınıfı yanlış olarak adlandırılmıştır: olmalı CustomUserRepositoryve olmamalı CustomerUserRepository.
Cotta

29

FWIW, çok iş parçacıklı bir ortamdaki güncellemelerle ilgili olarak:

  • MongoTemplatesağlayan "Atom" out-of-the-box işlemleri updateFirst , updateMulti, findAndModify, upsert... tek bir operasyonda bir belgeyi değiştirmek için izin verir. UpdateBu yöntemlerle kullanılan nesne de sadece ilgili alanları hedeflemesine olanak sağlar .
  • MongoRepositorySadece verir temel CRUD işlemleri find , insert, save, delete, içeren POJOs ile hangi iş tüm alanları . Bu sizi ya belgeleri birkaç adımda güncellemenize (1. findgüncellenecek belge, 2. döndürülen POJO'dan ilgili alanları değiştirmeye ve ardından 3. saveonu) ya da kullanarak kendi güncelleme sorgularınızı tanımlamaya zorlar @Query.

Çok iş parçacıklı bir ortamda, örneğin birkaç REST uç noktasına sahip bir Java arka ucu gibi, iki eşzamanlı güncellemenin birbirinin değişikliklerinin üzerine yazma şansını azaltmak için tek yöntemli güncellemeler gitmenin yoludur.

Örnek: böyle bir belge verildiğinde: { _id: "ID1", field1: "a string", field2: 10.0 }ve iki farklı iş parçacığı aynı anda güncelleniyor ...

Bununla MongoTemplatebiraz şuna benzeyecekti:

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

ve belgenin son durumu, { _id: "ID1", field1: "another string", field2: 15.0 }her iş parçacığının DB'ye yalnızca bir kez erişmesi ve yalnızca belirtilen alanın değiştirilmesidir.

Oysa aynı durum senaryosu MongoRepositoryşöyle görünecektir:

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

ve belgenin son olmanın ya { _id: "ID1", field1: "another string", field2: 10.0 }ya { _id: "ID1", field1: "a string", field2: 15.0 }bağlı olarak hangi saveişlemi son DB vurur.
(NOT: Spring Data'nın @Versionaçıklamasını yorumlarda önerildiği gibi kullansak bile , pek bir şey değişmez: saveişlemlerden biri bir atar OptimisticLockingFailureExceptionve son belge yine de yukarıdakilerden biri olur ve ikisi yerine yalnızca bir alan güncellenir. )

Bu nedenle , çok ayrıntılı bir POJO modeliniz yoksa veya herhangi bir nedenle özel sorgulama yeteneklerine ihtiyacınız yoksa MongoTemplate, bunun daha iyi bir seçenek olduğunu söyleyebilirim MongoRepository.


İyi noktalar / örnekler. Bununla birlikte, bu senaryoyu önlemek için yarış durumu örneğiniz ve istenmeyen sonucunuz @ Sürüm kullanılarak önlenebilir.
Madbreaks

@Madbreaks Bunu nasıl başaracağınıza dair herhangi bir kaynak sağlayabilir misiniz? Muhtemelen resmi bir doktor var mı?
Karthikeyan

@ Sürüm ek açıklamasıyla ilgili bahar verileri belgeleri: docs.spring.io/spring-data/mongodb/docs/current/reference/html/…
Karim Tawfik

1
@Madbreaks Bunu belirttiğiniz için teşekkürler. Evet, @Versionikinci iş parçacığının birinci iş parçacığı tarafından kaydedilen verilerin üzerine yazmasını "önler" - bu, güncellemeyi iptal edip OptimisticLockingFailureExceptionyerine bir tane atması anlamında "kaçınır" . Dolayısıyla, güncellemenin başarılı olmasını istiyorsanız bir yeniden deneme mekanizması uygulamanız gerekir. MongoTemplate, tüm senaryodan kaçınmanıza izin verir.
walen

28

Bu cevap biraz gecikmeli olabilir, ancak tüm depo rotasından kaçınmanızı tavsiye ederim. Herhangi bir büyük pratik değere sahip çok az uygulanan yöntem elde edersiniz. Çalışmasını sağlamak için, belgelerde çok fazla yardım almadan günler ve haftalar harcayabileceğiniz Java yapılandırma saçmalığıyla karşılaşıyorsunuz.

Bunun yerine, MongoTemplaterotayı takip edin ve sizi Spring programcılarının karşılaştığı yapılandırma kabuslarından kurtaran kendi Veri erişim katmanınızı oluşturun. MongoTemplateçok fazla esneklik olduğu için kendi sınıflarını ve etkileşimlerini rahatça tasarlayan mühendisler için gerçekten kurtarıcıdır. Yapı şunun gibi bir şey olabilir:

  1. MongoClientFactoryUygulama düzeyinde çalışacak ve size bir MongoClientnesne verecek bir sınıf oluşturun . Bunu bir Singleton olarak veya bir Enum Singleton kullanarak uygulayabilirsiniz (bu, iş parçacığı açısından güvenlidir)
  2. Her etki alanı nesnesi için bir veri erişim nesnesini devralabileceğiniz bir Veri erişimi temel sınıfı oluşturun). Temel sınıf, belirli yöntemlerin tüm DB erişimleri için kullanabileceği bir MongoTemplate nesnesi oluşturmak için bir yöntem uygulayabilir.
  3. Her etki alanı nesnesi için her veri erişim sınıfı, temel yöntemleri uygulayabilir veya bunları temel sınıfta uygulayabilirsiniz.
  4. Denetleyici yöntemleri daha sonra gerektiğinde Veri erişim sınıflarındaki yöntemleri çağırabilir.

Merhaba @rameshpa Aynı projede hem MongoTemplate hem de depoyu kullanabilir miyim? .. Kullanmak mümkün mü
Gauranga

1
Yapabilirsiniz, ancak uyguladığınız MongoTemplate, Depo tarafından kullanılan bağlantıdan farklı bir DB bağlantısına sahip olacaktır. Atomiklik bir sorun olabilir. Ayrıca sıralama ihtiyaçlarınız varsa bir iş parçacığı üzerinde iki farklı bağlantı kullanmanızı
önermem
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.