1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/cym1102-mongoHelper

Клонировать/Скачать
README.md 23 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
gitlife-traslator Отправлено 02.12.2024 05:13 728cb27

mongoHelper

Введение

Spring-data-mongodb — это расширенный набор инструментов, который упрощает операции CRUD и предоставляет функциональность, подобную mybatis plus, для работы с базами данных. Традиционные реляционные базы данных и ORM, построенные вокруг них, имеют множество неудобств при разработке проектов. MongoDB, документоориентированная база данных, идеально решает многие проблемы SQL в проектах разработки. После версии 4.0 MongoDB поддерживает транзакции и может быть успешно использована в инженерных проектах. Spring-data-MongoDB уже частично инкапсулирует операции MongoDB, но этого недостаточно. Операции Query Criteria Sort всё ещё имеют значительные ограничения, и людям, привыкшим к работе с SQL, всё равно может быть сложно понять, как их использовать. mongoHelper дополнительно инкапсулирует spring-data-Mongodb, делая его более удобным в использовании, и добавляет множество функций, облегчающих управление проектами.

Архитектура программного обеспечения

Этот проект подходит только для проектов springBoot. Проект также зависит от библиотек springBoot, а проекты springMVC не могут его использовать. Кроме того, проект зависит от hutool, предоставляющего различные инструменты Util, что делает код более лаконичным.

Демонстрационный проект: https://gitee.com/cym1102/mongoStudy

Инструкция по установке

  1. Импортируйте библиотеку maven:
    <dependency>
        <groupId>cn.craccd</groupId>
        <artifactId>mongoHelper</artifactId>
        <version>0.8.3</version>
    </dependency>
  1. Настройте файл конфигурации springBoot application.yml:
spring:
  data:
    mongodb:
      uri:     mongodb://user:pass@host:27017/study?replicaSet=rs0&authSource=admin&w=majority&j=true&wtimeout=5000&readPreference=primary
      print : true  #是否打印查询语句
      slowQuery : true  #是否记录慢查询到数据库中
      slowTime : 1000 #慢查询最短时间,默认为1000毫秒
  1. Добавьте класс конфигурации MongoConfig в подходящем месте:
@Configuration
@ComponentScan(basePackages = {"cn.craccd"}) // 必须填写此包路径
public class MongoConfig {

    // 开启事务(如使用单机mongodb,可不配置此@Bean)
    @Bean
    public MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
        return new MongoTransactionManager(dbFactory);
    }
}
  1. Включите аннотацию @EnableTransactionManagement в классе запуска springBoot для активации транзакций (если используется одиночный экземпляр MongoDB, этот шаг можно пропустить):

  2. Обратите внимание, что MongoDB должен работать в кластерной конфигурации, чтобы поддерживать транзакции. Этот ORM можно использовать в кластере MongoDB или в одиночном экземпляре MongoDB, но транзакции будут работать только в кластере. При использовании транзакций в одиночной MongoDB будет выдана ошибка. Для настройки кластера MongoDB обратитесь к соответствующей документации. Если объём бизнеса невелик, можно настроить кластер из одного экземпляра, который также поддерживает транзакции. В файле конфигурации springBoot application.yml, если spring.data.mongodb.uri настроен на кластерный URI, например:

mongodb://user:pass@host:27017/electric?replicaSet=rs0&authSource=admin&w=majority&j=true&wtimeout=5000&readPreference=primary

Использование

1. Основные операции

В контейнере будет внедрён объект MongoHelper, который предоставляет множество функций для однотабличных запросов, таких как:

  • deleteById(String, Class<?>) — удаление по id;
  • deleteByQuery(Criteria, Class<?>) — удаление по условию;
  • findAll(Class) — поиск всех;
  • findCount(Class<?>) — подсчёт количества;
  • findById(String, Class) — поиск по id;
  • findListByQuery(Criteria, Class<?>) — поиск по условию;
  • findPage(Criteria, Page, Class<?>) — поиск с разбиением на страницы по условию;
  • insert(Object) — вставка;
  • insertOrUpdate(Object) — вставить или обновить;
  • updateById(Object) — обновление по id;
  • updateAllColumnById(Object) — обновить все поля по id;
  • updateFirst(Criteria, Update, Class<?>) — обновить первую запись по условию;
  • updateMulti(Criteria, Update, Class<?>) — обновить все записи по условию;
  • addCountById(String id, String property, Long count, Class<?> clazz) — атомарное увеличение количества определённого поля по id.

MongoHelper может выполнять все задачи запроса, вставки и обновления. Он автоматически определяет тип POJO для операций вставки и обновления и сопоставляет его с соответствующей таблицей. Все операции с базой данных в этом ORM основаны на функциях MongoHelper. Не нужно создавать отдельные мапперы, XML, сервисы и модели, как в случае с mybatis. Это значительно сокращает объём кода в слое данных. MongoHelper можно напрямую внедрить в слой контроллера, где простые операции могут быть выполнены непосредственно через него без необходимости вызова сервисного слоя.

Сложные операции и управление транзакциями требуют сервисного уровня. Внедрение MongoHelper в сервис и использование аннотации @Transactional springBoot позволяет использовать функции управления транзакциями springBoot.

2. Сложные запросы

Функции запросов в MongoHelper находятся в методах findByQuery и findPage. Условия запроса инкапсулированы в объекты CriteriaAndWrapper и CriteriaOrWrapper. Они аналогичны операторам and и or в SQL и могут использоваться для создания сложных условий запроса.

// Поиск по входным условиям
public List<User> search(String word, Integer type) {
    CriteriaAndWrapper criteriaAndWrapper = new CriteriaAndWrapper();

    if (StrUtil.isNotEmpty(word)) {
        criteriaAndWrapper.and(new CriteriaOrWrapper().like(User::getName, word).like(User::getPhone, word));
    }
    if (type != null) {
        criteriaAndWrapper.eq(User::getType, type);
    }
        
    List<User> userList = mongoHelper.findListByQuery(criteriaAndWrapper, User.class);

    return userList ;
}

Приведённый выше код создаёт запрос, аналогичный select * from user where (name like '%xxx%' or phone like '%xxx%') and type = xxx. Это упрощает понимание запроса. Конечно, вы можете использовать Criteria для выполнения запросов, если вы привыкли к нему.

3. Разбиение на страницы

Этот ORM предоставляет класс Page, который содержит общее количество записей count, количество записей на странице limit, начальную страницу curr (начинается с 1), и список результатов records. Просто передайте объект Page, содержащий данные curr и limit, в метод findPage, и он вернёт результаты records и данные count в объект Page. Здесь три атрибута соответствуют параметрам разбиения на страницы в layui, что позволяет легко интегрировать его с контроллером разбиения на страницы layui. Текст 1:

if (word).like(User::getPhone, word));
}
if (type != null) {
    criteriaAndWrapper.eq(User::getType, type);
}
page = mongoHelper.findPage(criteriaAndWrapper, new SortBuilder(User::getCreatTime, Direction.DESC), page, User.class);
return page;

Текст 2:

Проект запускается, и этот ORM сканирует все классы с аннотацией @Document в проекте и создаёт соответствующие таблицы, индексы и значения по умолчанию для полей. Эта аннотация определена в spring-data-mongodb. В этом проекте также добавлена новая аннотация @InitValue, которая используется для предоставления начальных значений полей, аналогично значениям по умолчанию в MySQL. Важно отметить, что MongoDB не имеет значений по умолчанию для полей, поэтому проект реализует эту функциональность программно.

Для создания индексов используется аннотация @Indexed, также определённая в spring-data-mongodb. Если поле не требуется в базе данных, можно использовать аннотацию @IgnoreColumn, чтобы игнорировать это поле.

Если необходимо наследовать класс, помеченный аннотацией @Document, но не хотите, чтобы родительские аннотации влияли на создание новой таблицы, можно использовать @IgnoreDocument. Это предотвратит создание ненужных таблиц с помощью mongoHelper.

Пример класса:

@Document
public class User extends BaseModel {
    @InitValue("0")
    Integer type; // 类型 0 клиент 1 дилер

    @Indexed // 添加索引
    String name;

    @IgnoreColumn // 忽略字段
    String phone;

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    public Integer getType() {
        return type;
    }

    public void setType(Integer type) {
        this.type = type;
    }
}

Также этот ORM предоставляет базовый класс BaseModel, который содержит обязательные атрибуты id (с аннотацией @Id), createTime и updateTime. Эти атрибуты могут быть унаследованы другими классами POJO, что упрощает процесс разработки.

Пример базового класса:

@Document
public class BaseModel implements Serializable{
    @Id // 主键注解
    String id;
    @CreateTime // 自动插入创建时间戳的注解
    Long createTime;
    @UpdateTime // 自动插внение обновления времени
    Long updateTime;
        
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public Long getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }
    public Long getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Long updateTime) {
        this.updateTime = updateTime;
    }
}

Примечание: В тексте запроса присутствуют фрагменты кода на языке программирования Java. Перевод текста на русский язык:

1. Некоторые фреймворки позволяют сканировать POJO-классы и автоматически изменять структуру таблиц базы данных, достигая эффекта отказа от Flyway. Например, существует фреймворк MyBatis Enhance (https://gitee.com/sunchenbin/mybatis-enhance), но он всё равно не решает проблему ожидания одним разработчиком, пока другой не отправит POJO-класс, чтобы нормально работать с новой версией базы данных.

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

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

  • findIdsByQuery(Criteria criteria, Class<?> clazz) — возвращает список идентификаторов таблицы;
  • findPropertiesByQuery(Criteria criteria, Class<?> documentClass, String property, Class propertyClass) — возвращает список значений определённого поля таблицы.

Пример использования:

// Получение списка всех товаров для заказа (OrderProduct.class является сопоставлением между заказом и товаром)
public List<Product> getProductList(String orderId) {
    List<String> productIds = mongoHelper.findPropertiesByQuery(new CriteriaOrWrapper().eq(OrderProduct::getOrderId, orderId), OrderProduct.class,  OrderProduct::getProductId, String.class);
    return mongoHelper.findListByQuery(new CriteriaOrWrapper().in(Product::getId, productIds), Product.class);
}


// Поиск всех заказов по названию товара
public Page search(Page page, String keywords) {
    CriteriaOrWrapper criteriaOrWrapper = new CriteriaOrWrapper();
        
    if (StrUtil.isNotEmpty(keywords)) {
            
        List<String> productIds = mongoHelper.findIdsByQuery(new CriteriaAndWrapper().like(Product::getName, keywords), Product.class);
        List<String> orderIds = mongoHelper.findPropertiesByQuery(new CriteriaAndWrapper().in(OrderProduct::getProductId, productIds),OrderProduct.class,  OrderProduct::getOrderId, String.class);
    
        criteriaOrWrapper.in(Order::getId, orderIds);
            
    }

    page = mongoHelper.findPage(criteriaOrWrapper, page, Order.class);
    return page;
}

2. Соединение таблиц в реляционных базах данных может решить множество проблем, но в крупных компаниях его уже не рекомендуют использовать, так как сложно оптимизировать базу данных, а при больших объёмах данных запросы выполняются медленно. Если необходимо выполнить соединение таблиц, сначала нужно получить набор идентификаторов другой стороны, а затем использовать оператор «in» для выполнения запроса с включением. Это позволяет эффективно использовать индексы и упрощает изменение при разделении на несколько баз данных. Однако использование реляционной базы данных таким образом фактически превращает её в базу данных, приближённую к документоориентированным, где таблицы перестают быть связанными друг с другом. Поэтому возникает вопрос, почему бы сразу не перейти к использованию документоориентированных баз данных и полностью отказаться от соединения таблиц?

На основе этой идеи ORM также предлагает некоторые дополнительные функции для оптимизации многократных соединений. В классе mongoHelper доступны следующие методы:

  • findIdsByQuery(Criteria criteria, Class<?> clazz) — возвращает список идентификаторов таблицы;
  • findPropertiesByQuery(Criteria criteria, Class<?> documentClass, String property, Class propertyClass) — возвращает список значений определённого поля таблицы.

3. MongoDB имеет встроенный пул соединений, который используется по умолчанию. Размер пула можно настроить через URI, что устраняет необходимость в использовании сторонних библиотек, таких как Druid или Hikari.

4. Для реализации кластера и разделения чтения и записи в MySQL требуются сторонние компоненты, что усложняет настройку. MongoDB же изначально поддерживает кластеры и разделение чтения и записи. Все настройки для этого можно указать в URI. Нет необходимости писать код для управления источниками данных и предпочтениями чтения и записи внутри ORM.

MongoDB поддерживает пять режимов ReadPreference:

  • primary — основной режим, чтение выполняется только с основного узла. Если основной узел недоступен, операция чтения завершается ошибкой или исключением.
  • primaryPreferred — предпочтительный основной режим. Чтение обычно выполняется с основного узла, но если основной узел недоступен (например, из-за сбоя и перехода на другой ресурс), чтение выполняется с вторичного узла.
  • secondary — вторичный режим. Чтение выполняется только со вторичного узла. Если вторичный узел недоступен, операция чтения завершается ошибкой или исключением.
  • secondaryPreferred — предпочтительный вторичный режим. Обычно чтение выполняется со вторичного узла, но при определённых условиях (например, в архитектуре с одним основным узлом) чтение выполняется с основного узла.
  • nearest — ближайший режим. Чтение выполняется с ближайшего узла, который может быть основным или вторичным.

Важно отметить, что операции записи и транзакции возможны только на основном узле.

5. Опыт использования MongoDB показывает, что она действительно может помочь в решении многих проблем, возникающих при быстрой разработке. После многолетнего опыта работы с MySQL мы обнаружили, что, стремясь использовать его эффективно, мы добавили слишком много компонентов, таких как MyBatis, MyBatis-Plus, JPA, Flyway, Druid, MyCat и другие. Но это не помогло нам полностью освоить MySQL, и кривая обучения стала ещё более крутой. Отказавшись от MySQL и перейдя на MongoDB, мы обнаружили, что многие проблемы просто исчезли. MongoDB предоставляет множество функций, которые помогают решать задачи без дополнительных усилий.

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/cym1102-mongoHelper.git
git@api.gitlife.ru:oschina-mirror/cym1102-mongoHelper.git
oschina-mirror
cym1102-mongoHelper
cym1102-mongoHelper
master