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

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, Параметры настройки описаны здесь.

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

  • MongoTemplate обеспечивает из коробки updateFirst, updateMulti, findAndModify, upsert... методы, которые позволяют изменять документ за одну операцию. Update Объект, используемый этими методами, также позволяет ориентироваться только на соответствующие поля.
  • MongoRepository только дает вам основные find, insert, save, delete операции, которые работают с POJO, содержащими все поля. Это заставляет вас либо обновить документы в несколько шагов (find документ для обновления, измените соответствующие поля из возвращенного POJO, а затем save это), или определите свои собственные запросы на обновление вручную, используя @Query,

В многопоточной среде, такой как, например, серверная часть 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 операция попадает в БД в первую очередь.

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

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

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

  1. Создать MongoClientFactory класс, который будет работать на уровне приложения и даст вам MongoClient объект. Вы можете реализовать это как Singleton или Enum Singleton (это потокобезопасно)
  2. Создайте базовый класс доступа к данным, из которого вы можете наследовать объект доступа к данным для каждого объекта домена). Базовый класс может реализовывать метод для создания объекта MongoTemplate, который вы можете использовать для конкретных методов класса для всех обращений к БД.
  3. Каждый класс доступа к данным для каждого объекта домена может реализовывать базовые методы или вы можете реализовать их в базовом классе.
  4. Методы контроллера могут затем вызывать методы в классах доступа к данным по мере необходимости.
Другие вопросы по тегам