В чем разница между MongoTemplate Spring Data и MongoRepository?

Мне нужно написать приложение, с помощью которого я могу выполнять сложные запросы с использованием 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? Оба они одинаковы для взрослых или у одного из них не хватает функций, чем у другого?


person Christopher Armstrong    schedule 09.06.2013    source источник


Ответы (3)


«Удобный» и «мощный в использовании» в некоторой степени противоречат целям. Репозитории намного удобнее шаблонов, но последние, конечно, дают вам более точный контроль над тем, что выполнять.

Поскольку модель программирования репозитория доступна для нескольких модулей 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. Параметры настройки задокументированы здесь .

person Oliver Drotbohm    schedule 09.06.2013
comment
Привет, Оливер, на самом деле это не работает. spring-data пытается автоматически сгенерировать запрос из настраиваемого имени. yourCustomMethod (). Он скажет, что ваше поле не является допустимым полем в классе домена. Я следил за руководством, а также дважды проверил, как вы это делаете, в spring-data-jpa-examples. Не повезло. spring-data всегда пытается автоматически сгенерировать, как только я расширяю настраиваемый интерфейс до класса репозитория. Единственная разница в том, что я использую MongoRepository, а не CrudRepository, так как пока я не хочу работать с итераторами. Если у вас есть подсказка, мы будем признательны. - person Christopher Armstrong; 12.06.2013
comment
Наиболее частая ошибка - неправильное имя класса реализации: если ваш базовый интерфейс репо называется YourRepository, класс реализации должен иметь имя YourRepositoryImpl. Так ли это? Если да, то я рад взглянуть на образец проекта на GitHub и т.п. - person Oliver Drotbohm; 12.06.2013
comment
Привет, Оливер, класс Impl был назван неправильно, как вы и предполагали. Я изменил имя, и теперь похоже, что оно работает. Большое спасибо за ваш отзыв. Действительно здорово иметь возможность таким образом использовать разные варианты запросов. Хорошо продумано! - person Christopher Armstrong; 13.06.2013
comment
Этот ответ не так однозначен. Сделав все по этому примеру, я столкнулся с этой проблемой: stackoverflow.com/a/13947263/449553. Таким образом, соглашение об именах более строгое, чем кажется в этом примере. - person msangel; 26.09.2013
comment
и как объединить их с автоматически реализуемыми запросами по свойствам deviantpics.com/ images / 2015/01/06 / y.png - person fpopic; 06.01.2015
comment
Обратите внимание, что класс реализации должен быть основан на объявленном классе интерфейса. Итак, объявленный класс на основе Spring Data - это UserRepository, объявленный настраиваемый метод - CustomUserRepository. Имя класса реализации - UserRepositoryImpl. Данные Spring ищут существующий класс реализации для объявленного репозитория. - person rohitmohta; 27.03.2015
comment
@OliverGierke Поддерживается ли индивидуальная реализация в автономных репозиториях MongoRepositories? Другими словами, я не использую Spring DI, а просто использую фабрику репозитория mongo для создания репозитория. В моем контейнере DI работают репозитории, но не пользовательские. Они просто ошибаются, потому что не могут найти имя свойства, подобное сообщению выше. вернуть mongoRepositoryFactory.getRepository (InvoiceSequenceRepository.class, InvoiceSequenceRepositoryImpl.class); - person Roy Kachouh; 30.06.2015
comment
@RoyKachouh - Пожалуйста, откройте новый вопрос. - person Oliver Drotbohm; 03.07.2015
comment
Класс реализации на # 2 назван неправильно: должен быть CustomUserRepository, а не CustomerUserRepository. - person Cotta; 18.04.2016
comment
@Cotta - Спасибо, вот и исправления! - person Oliver Drotbohm; 18.04.2016
comment
@OliverDrotbohm Поздно на вечеринку, но есть ли причина, по которой вы используете MongoOperations вместо MongoTemplate в своем примере? - person Bennett Dams; 26.03.2020
comment
Использование Spring Data Repository и MongoTemplate создаст 2 подключения к серверу базы данных Mongo. Не правда ли? Как это повлияет на производительность? - person iamcrypticcoder; 16.05.2020

FWIW, относительно обновлений в многопоточной среде:

В многопоточной среде, например, серверная часть Java с несколькими конечными точками REST, обновления с использованием одного метода - это путь, позволяющий снизить вероятность того, что два одновременных обновления перезапишут изменения друг друга.

Пример: задан такой документ: { _id: "ID1", field1: "a string", field2: 10.0 } и два разных потока одновременно обновляют его ...

С 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 }, поскольку каждый поток обращается к БД только один раз и изменяется только указанное поле.

Тогда как тот же сценарий с 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)
|                                                               |
|                                                               |

и последний документ будет либо { _id: "ID1", field1: "another string", field2: 10.0 }, либо { _id: "ID1", field1: "a string", field2: 15.0 } в зависимости от того, какая save операция попадает в БД последней.
(ПРИМЕЧАНИЕ: даже если мы использовали
@Version аннотация Spring Data, как предложено в комментариях, мало что изменится: одна из save операции будут выдавать OptimisticLockingFailureException, и окончательный документ все равно будет одним из перечисленных выше, с обновлением только одного поля вместо обоих.)

Поэтому я бы сказал, что MongoTemplate - лучший вариант, если у вас нет очень сложной модели POJO или вам по какой-то причине не нужны возможности пользовательских запросов MongoRepository.

person walen    schedule 17.04.2018
comment
Хорошие моменты / примеры. Однако вашего примера состояния гонки и нежелательного результата можно избежать с помощью @Version, чтобы предотвратить этот самый сценарий. - person Madbreaks; 06.07.2018
comment
@Madbreaks Можете ли вы предоставить какие-либо ресурсы о том, как этого добиться? Возможно, какой-нибудь официальный документ? - person Karthikeyan; 19.06.2019
comment
Документы Spring по аннотации @Version: docs.spring.io/spring-data/mongodb/docs/current/reference/html/ - person Karim Tawfik; 23.01.2020
comment
@Madbreaks Спасибо, что указали на это. Да, @Version предотвратит перезапись вторым потоком данных, сохраненных первым - избегайте в том смысле, что он отбросит обновление и вместо этого выдаст OptimisticLockingFailureException. Поэтому вам придется реализовать механизм повтора, если вы хотите, чтобы обновление прошло успешно. MongoTemplate позволяет избежать всего сценария. - person walen; 23.01.2020

Этот ответ может быть немного запоздалым, но я бы рекомендовал избегать всего пути к репозиторию. Вы получаете очень мало реализованных методов, имеющих большую практическую ценность. Чтобы заставить его работать, вы сталкиваетесь с ерундой конфигурации Java, на которую вы можете потратить дни и недели без особой помощи в документации.

Вместо этого выберите путь MongoTemplate и создайте свой собственный уровень доступа к данным, который избавит вас от кошмаров конфигурации, с которыми сталкиваются программисты Spring. MongoTemplate - действительно спаситель для инженеров, которым удобно создавать собственные классы и взаимодействия, поскольку существует большая гибкость. Структура может быть примерно такой:

  1. Создайте MongoClientFactory класс, который будет работать на уровне приложения и даст вам MongoClient объект. Вы можете реализовать это как Singleton или с помощью Enum Singleton (это потокобезопасно)
  2. Создайте базовый класс доступа к данным, от которого вы можете наследовать объект доступа к данным для каждого объекта домена). Базовый класс может реализовать метод для создания объекта MongoTemplate, который методы вашего класса могут использовать для всех обращений к БД.
  3. Каждый класс доступа к данным для каждого объекта домена может реализовывать базовые методы, или вы можете реализовать их в базовом классе.
  4. Затем методы контроллера могут при необходимости вызывать методы в классах доступа к данным.
person rameshpa    schedule 25.12.2014
comment
Привет @rameshpa Могу ли я использовать MongoTemplate и репозиторий в одном проекте? .. Можно ли использовать - person Gauranga; 16.07.2017
comment
Вы могли бы, но реализуемый вами MongoTemplate будет иметь другое соединение с БД, чем соединение, используемое репозиторием. Атомарность может быть проблемой. Также я бы не рекомендовал использовать два разных соединения в одном потоке, если у вас есть потребности в секвенировании. - person rameshpa; 07.08.2017