суббота, 24 февраля 2018 г.

Взаимодействие SpringBoot с RabbitMq

RabbitMQ - одно из популярных решений для отправки сообщений, предоставляется клиентские библиотеки для таких языков как Java, Scala, .NET, Go, Python, PHP  и другие. В данном турториале мы рассмотрим отправку и обработку сообщений, а так же как отправлять сообщение с JSON содержимым и работать с ошибками при помощи Dead Letter Queues (DLQ)

Для начала установим сервер RabbitMQ на вашей локальной машине, как указано здесь или запустим с помощью docker-compose:

version: '3'
services:
  rabbitmq:
    container_name: rabbitmq
    image: 'rabbitmq:management'
    ports:
      - "5672:5672"
      - "15672:15672"
 
Теперь можно запустить RabbitMQ используя docker-compose up и открыть UI администратора по адресу http://localhost:15672/.

Если вы знакомы с другими брокерскими службами обмена сообщениями, такими как ActiveMQ,  в них обычно используются очереди(Queues) и топики (Topics) для отправки индивидуальной и общедоступной модели связи. В RabbitMQ используется Exchange и в зависимости от ключа маршрутизации, сообщение будет отправлено в очередь (очереди) (Queues). Вы можете больше узнать о концепциях RabbitMQ здесь.

Приложение SpringBoot c RabbitMQ

Создадим SpringBoot приложение, описав следующие стартеры:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.sivalabs</groupId>
    <artifactId>springboot-rabbitmq-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.0.RC1</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
      
</project>
Начнем с конфигурации RabbitMQ, создадим RabbitConfig и опишем следующие бины: Queue, Exchange и Binding:
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
  
@Configuration
public class RabbitConfig 
{
    public static final String QUEUE_ORDERS = "orders-queue";
    public static final String EXCHANGE_ORDERS = "orders-exchange";

    @Bean
    Queue ordersQueue() {
        return QueueBuilder.durable(QUEUE_ORDERS).build();
    }

    @Bean
    Queue deadLetterQueue() {
        return QueueBuilder.durable(QUEUE_DEAD_ORDERS).build();
    }
    @Bean
    Exchange ordersExchange() {
        return ExchangeBuilder.topicExchange(EXCHANGE_ORDERS).build();
    }

    @Bean
    Binding binding(Queue ordersQueue, TopicExchange ordersExchange) {
        return BindingBuilder.bind(ordersQueue).to(ordersExchange).with(QUEUE_ORDERS);
    }
}
Мы объявляем Queue имен orders-queue и Exchange с именем orders-exchange.
Также определили привязку между orders-queue и orders-exchange, чтобы любое сообщение, отправленное на orders-exchange с ключом направления как orders-queue, отправлялось в orders-queue.

Конфигурация RabbitMQ представлена в application.properties:

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
Создадим бин OrderMessageSender для отправки сообщений в orders-exchange. Spring Boot автоматически конфигурирует инфраструктурные бины, необходимые для отправки/обработки сообщений в/от брокера сообщений RabbitMQ. Нам необходимо просто проинжектить RabbitTemplate и отправить сообщение вызвав метод  rabbitTemplate.convertAndSend(“routingKey”, Object).

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Order implements Serializable{
    private String orderNumber;
    private String productId;
    private double amount;
}

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class OrderMessageSender {
    private final RabbitTemplate rabbitTemplate;
    @Autowired
    public OrderMessageSender(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
    }

    public void sendOrder(Order order) {
        this.rabbitTemplate.convertAndSend(RabbitConfig.QUEUE_ORDERS, order);

    }
}
По умолчанию в SpringBoot используется org.springframework.amqp.support.converter. SimpleMessageConverterand, который сериализует объект в byte[].
После отправки сообщения вы можете просмотреть сообщение из интерфейса администратора, выполнив вход с учетными данными guest/guest.

Теперь создадим слушателя, используя аннотацию RabbitListener.
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import ru.config.Config;
import ru.model.Order;

@Component
public class OrderMessageListener {
    static final Logger logger = LoggerFactory.getLogger(OrderMessageListener.class);

    @RabbitListener(queues = Config.QUEUE_ORDERS)
    public void process(Order order){
        logger.info("Received order");
        logger.info(order.toString());
    }

}

Вот и все! Просто добавив @RabbitListener и определив, какую очередь слушать, мы можем создать слушателя.

Теперь, если вы отправляете сообщение в очередь, то с помощью метода OrderMessageListener.process(), вы обработаете данное сообщение и в логах приложения будет написано Received order и само содержимое объекта.

Отправка и прием сообщений c JSON содержимым.

Как мы видели, механизм сериализации по умолчанию преобразует объект сообщения в byte [] с помощью SimpleMessageConverter и на принимающей стороне, он будет десериализован из byte [] в тип объекта (в нашем случае Order) с помощью GenericMessageConverter.

Чтобы изменить это поведение, нам нужно настроить автоконфигурацию бинов Spring Boot RabbitMQ.

Отправить сообщение как JSON

Одним быстрым способом отправки сообщения с JSON является использование ObjectMapper, мы можем конвертировать Orderobject в JSON и отправлять его.
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ru.config.Config;
import ru.model.Order;

@Service
public class OrderMessageService {
    private final RabbitTemplate rabbitTemplate;
    private ObjectMapper objectMapper;

    @Autowired
    public OrderMessageService(RabbitTemplate rabbitTemplate, ObjectMapper objectMapper) {
        this.rabbitTemplate = rabbitTemplate;
        this.objectMapper = objectMapper;
    }

    public void sendOrder(Order order) throws Exception{
        String orderJson = objectMapper.writeValueAsString(order);
        Message message = MessageBuilder
                .withBody(orderJson.getBytes())
                .setContentType(MessageProperties.CONTENT_TYPE_JSON)
                .build();

        rabbitTemplate.convertAndSend(Config.QUEUE_ORDERS, order);
    }
}
Но преобразование объектов в JSON, как это, является своего рода избыточностью. Вместо этого мы можем сделать изменить конфигурацию, настроив компонент org.springframework.amqp.support.converter.Jackson2JsonMessageConverter, который будет использоваться в RabbitTemplate, чтобы сообщение было сериализовано как JSON вместо byte[].
@Configuration
public class RabbitConfig{
    ...
    ...

    @Bean
    public RabbitTemplate rabbitTemplate(final ConnectionFactory connectionFactory) {
        final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
        return rabbitTemplate;
    }

    @Bean
    public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}
Теперь сообщения отправляются в виде JSON. Так же необходимо настроить прием JSON сообщения. Для этого необходимо имплиментировать для нашего класса конфигурций интерфейс RabbitListenerConfigurer.
 @Configuration
public class RabbitConfig implements RabbitListenerConfigurer {
    ...
    ...
    @Override
    public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
        registrar.setMessageHandlerMethodFactory(messageHandlerMethodFactory());
    }
    @Bean
    MessageHandlerMethodFactory messageHandlerMethodFactory() {
        DefaultMessageHandlerMethodFactory messageHandlerMethodFactory = new DefaultMessageHandlerMethodFactory();
        messageHandlerMethodFactory.setMessageConverter(consumerJackson2MessageConverter());
        return messageHandlerMethodFactory;
    }
  
    @Bean
    public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
        return new MappingJackson2MessageConverter();
    }
}

Обработка ошибок и невалидных сообщений, используя DeadLetterQueues(DLQ)

Мы можем отправить невалидные сообщения в отдельную очередь, чтобы мы могли их проверять и обрабатывать позже. Для этого можно использовать концепцию DLQ для автоматизации, вместо написания кода и обработки такого события.
Мы можем объявить dead-letter-exchange, dead-letter-routing-key для очереди, тогда объявление бина будет следующим:

@Configuration
public class RabbitConfig implements RabbitListenerConfigurer {

    public static final String QUEUE_ORDERS = "orders-queue";
    public static final String EXCHANGE_ORDERS = "orders-exchange";
    public static final String QUEUE_DEAD_ORDERS = "dead-orders-queue";

    @Bean
    Queue ordersQueue() {
        return QueueBuilder.durable(QUEUE_ORDERS)
                .withArgument("x-dead-letter-exchange", "")
                .withArgument("x-dead-letter-routing-key", QUEUE_DEAD_ORDERS)
                .withArgument("x-message-ttl", 15000) //Если сообщение не бработано за 15 сек - оно отправляется в DLQ
                .build();

    }
 
    @Bean
    Queue deadLetterQueue() {
        return QueueBuilder.durable(QUEUE_DEAD_ORDERS).build();
    }

    ...
    ...
}

Теперь можно отправить сообщение с JSON отличным от объекта Order и оно попадет в очередь dead-orders-queue. Необходимо заметить, что если очередь orders-queue была создана - ее необходимо удалить, чтобы приложение запустилось.

PS это мой перевод данной статьи

среда, 22 ноября 2017 г.

Mongo Java driver

Рассмотрим простой пример приложения, которое обращается к Mongo. Содержимое оставим из предыдущих частей.
В зависимостях будет лишь драйвер для Mongo:
    <dependencies>
        <dependency>
            <groupId>org.mongodb</groupId>
            <artifactId>mongo-java-driver</artifactId>
            <version>3.5.0</version>
        </dependency>
    </dependencies>
Необходимо заметить, что я использую драйвер 3.х.х. В долшинстве примеров, что я нашел, использовался драйвер версии 2.х.х, в 3 версии большинство используемых в примерах объектов отмечены ка устаревшие (Deprecated).

Для начала посчитаем количество записей в коллекции и выведем ее:

MongoClient mongo = new MongoClient( "localhost" , 27017 );

MongoDatabase database = mongo.getDatabase("test");
FindIterable<Document> unicorns = database.getCollection("employees").find();
long employeesCount = database.getCollection("employees").count();

System.out.println("count = " + employeesCount);
printD(id);

Функция для вывода коллекции:
private static void printD(Iterable<Document> objects){
 objects.forEach(document -> document.forEach((key, value) -> System.out.println(key + " = " + value)));
}
Создадим  новую запись:
        Document document = new Document();
        document.put("name", "Martin");
        document.put("hired", new Date());
        document.put("skills", "[smart, responsible]");
        document.put("age", 22);
        document.put("gender", "m");
        document.put("salary", 2468);
        database.getCollection("employees").insertOne(document);

        employeesCount = database.getCollection("employees").count();
        System.out.println("count = " + employeesCount);
Количество записей увеличилось на 1.  Попробуем обновить, созданную запись:
 Document find = new Document();
        find.put("name", "Martin");
        FindIterable<Document> beforeUpdate = database.getCollection("employees").find(find);
        printD(beforeUpdate);

        System.out.println("update:");

        Document update = new Document();
        Document set = new Document();
        update.put("salary", 2479);
        set.put("$set", update);
        database.getCollection("employees").updateMany(find, set);
        FindIterable<Document> updated = database.getCollection("employees").find(find);
        printD(updated);
Поиск записи по ИД. Внимание, я использую свой ИД, необходимо использовать именно ваш, сегенерированный ид:
 System.out.println("find by id");
        Document findById = new Document();
        findById.put("_id", new ObjectId("5a1525ea0f6ce550de6fc392"));
        FindIterable<Document> id = database.getCollection("employees").find(findById);
        printD(id);
Удалим созданный объект:
        database.getCollection("employees").deleteMany(find);
        employeesCount = database.getCollection("employees").count();
        System.out.println("count = " + employeesCount);

Количество записей уменьшилось на 1.

Полностью код приведен ниже:
package com;

import com.mongodb.MongoClient;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.bson.types.ObjectId;

import java.util.Date;
public class Application {

    public static void main(String[] args) {

        MongoClient mongo = new MongoClient( "localhost" , 27017 );

        MongoDatabase database = mongo.getDatabase("test");
        FindIterable<Document> unicorns = database.getCollection("employees").find();
        long employeesCount = database.getCollection("employees").count();
        System.out.println("count = " + employeesCount);
        printD(unicorns);

        Document document = new Document();
        document.put("name", "Martin");
        document.put("hired", new Date());
        document.put("skills", "[smart, responsible]");
        document.put("age", 22);
        document.put("gender", "m");
        document.put("salary", 2468);
        database.getCollection("employees").insertOne(document);

        employeesCount = database.getCollection("employees").count();
        System.out.println("count = " + employeesCount);


        Document find = new Document();
        find.put("name", "Martin");
        FindIterable<Document> beforeUpdate = database.getCollection("employees").find(find);
        printD(beforeUpdate);

        System.out.println("update:");

        Document update = new Document();
        Document set = new Document();
        update.put("salary", 2479);
        set.put("$set", update);
        database.getCollection("employees").updateMany(find, set);
        FindIterable<Document> updated = database.getCollection("employees").find(find);
        printD(updated);


        System.out.println("find by id");
        Document findById = new Document();
        findById.put("_id", new ObjectId("5a1525ea0f6ce550de6fc392"));
        FindIterable<Document> id = database.getCollection("employees").find(findById);
        printD(id);

        database.getCollection("employees").deleteMany(find);
        employeesCount = database.getCollection("employees").count();
        System.out.println("count = " + employeesCount);
      
    }

    private static void printD(Iterable<Document> objects){
        objects.forEach(document -> document.forEach((key, value) -> System.out.println(key + " = " + value)));
    }
}

среда, 8 ноября 2017 г.

MongoDb основные операции. Часть 3

Выбор полей

Команда find(), так же может принимать второй необязательный парметр, в котором перечисляются поля, которые будут в выборке. Так же необходимо заметить, что поле _id выбирается по умолчанию, если мы хотим его исключить - нужно это указать. Попробуем выбрать все имена:

db.employees.find({},{name:1, _id:0});
db.employees.find({},{name:1});
Второй вариант вернется так же поле _id.

Сортировка

Чтобы отсортировать результат к курсору find() добавляется sort, с очень похожими входными параметрами, необходимо указать поле и направление сортировки 1 - по возрастанию, -1 - по убыванию:

db.employees.find({},{name:1, _id:0}).sort({name:1});
Необходимо упомянуть, что если попытатьс отсортировать большой объем данных без индекса - мы получим ошибку.

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

Разбиение на страницы выполняется с помощью limit и skip, так же их можно использовать, чтобы избежать проблем при сортировке по полям, не содержащим индексы. Получим 4 и 5 имена:
db.employees.find({}, {name:1, _id:0}).sort({name:1}).limit(2).skip(3);

Подсчет записей

Консоль mongoDb позволяет выполнять count над коллекцией, некоторые драйвера не позволяют делать этого, в таком случае, count необходимо выполнить после find():

db.employees.find({}, {name:1, _id:0}.count();

воскресенье, 5 ноября 2017 г.

MongoDb основные операции. Часть 2

Обновление данных

Чтобы обновить данные воспользуемся, командой update, например добавил з/п для Бориса:
db.employees.update({name:'Boris'}, {salary:7000});
После обновления попробуем получить запись:
db.employees.find({name:'Boris'});
Запись не найдена, т.к. обычный update полностью заменяет документ. Чтобы обновлять только нужные поля необходимо воспользоваться модификатором $set:

db.employees.update({salary:7000}, {$set:{name: 'Boris', hired: new Date(2002,2,13,7,47), skills: ['smart','happy'], age: 60, gender: 'm'}});
Кроме модификатора $set есть другие, например, $inc увеличивает на заданную величину число или $push добавлет элемент в массив:

db.employees.update({name:'Boris'}, {$inc:{salary:200}, $push:{skills:'responsible'}});

Вставка/Обновление

Чтобы выполнить обновление или вствку записи при ее отсутствии необходимо передать третий параметр-флаг, true, например, на склад завезли канцелярские товары, а именно ручки:
db.storage.update({stationery:'pen'}, {$inc:{count: 200}},true)
При первом выполнении произойдет создание документы, а при втором - увеличение счетчика на 200 единиц.

Множественное обновление

Команда update обновляет только первую найденную запись, чтобы обновить все записи, удовлетворяющие заданному условию, необходимо установить четвертый параметр в true. Так же необходимо заметить, что множественное обновление работает только с модификатором, чтобы не произошло замены документов, только обновление полей:

db.employees.update({}, {$set:{isIll:false}}, false, true);

пятница, 3 ноября 2017 г.

MongoDb основные операции

1. Скачивание и запуск

Прежде чем рассматривать основные операции в MongoDb - скачаем ее с офф сайта. Разархивируем скачанный файл и перейдем в папку bin. Больше всего нас интересуют файлы: mongod - сервер и mondo - клиентская консоль. В папке bin, создадим конфигурационный файл mongo.config. В него добавим: dbpath=путь_куда_создать_базу_данных. Теперь, запустим mongod, в консоли выполним: mongod.exe --config mongo.config. Для проверки запустим  файл mongo и выполним db.version(). В результате получим версию своей MongoDb.

2. Выбор БД и ее наполнение

Запустим консоль -  mongo.  Она работает на JavaScript. Существует несколько глобальных команд, например exit или help. Большинство команд выполняются применительно к каому-либо объекту, например, выполним db.stats(), получим информацию о текущей базе данных:
Текущая база - test, сменим ее, выполнив: use learn:
При создании первой коллекции произойдет и создание БД. Создим несколько коллекций:
db.employees.insert({name: 'Boris', hired: new Date(2002,2,13,7,47), skills: ['smart','happy'], age: 60, gender: 'm', salary: 6300}); 
db.employees.insert({name: 'Aurora', hired: new Date(2011, 0, 24, 13, 0), skills: ['smart', 'responsible'], age: 45, gender: 'f', salary: 4300}); 
db.employees.insert({name: 'Tom', hired: new Date(2003, 1, 9, 22, 10), skills: ['energetic', 'enthusiast'], age: 34, gender: 'm', salary: 18200}); 
db.employees.insert({name: 'Rod', hired: new Date(2009, 7, 18, 18, 44), skills: ['serious'], age: 57, gender: 'm', salary: 9900}); 
db.employees.insert({name: 'Solnara', hired: new Date(2005, 6, 4, 2, 1), skills:['serious', 'smart', 'serious'], age:55, gender:'f', salary:8000}); 
db.employees.insert({name:'Kenny', hired: new Date(2007, 6, 1, 10, 42), skills: ['responsible', 'independent'], age: 39, gender: 'm', salary: 3900}); 
db.employees.insert({name: 'Ralf', hired: new Date(2005, 4, 3, 0, 57), skills: ['serious', 'independent'], age: 21, gender: 'm', salary: 200}); 
db.employees.insert({name: 'Leia', hired: new Date(2001, 9, 8, 14, 53), skills: ['serious', 'easily trained'], age: 40, gender: 'f', salary: 3300}); 
db.employees.insert({name: 'Bill', hired: new Date(2007, 2, 1, 5, 3), skills: ['serious', 'easily trained'], age: 35, gender: 'm', salary: 5400}); 
db.employees.insert({name: 'Nimue', hired: new Date(2009, 11, 20, 16, 15), skills: ['responsible', 'smart'], age: 54, gender: 'f'}); 
db.employees.insert({name: 'Max', hired: new Date(2006, 6, 18, 18, 18), skills: ['responsible', 'easily trained'], age: 44, gender: 'm', salary: 16500}); 
 Каждая команда вставляет в коллекцию employees перевадаваемый аргумент, который представляет из себя обычный JSON. Выполним команду поиска: db.employees.find(). В результате получим все введенные данные. У них повилось поле _id. У каждой записи должно быть уникальное поле, можно его генерировать самому или позволить это делать MongoDb. Поле _id, по умолчанию индексируется.
Когда у нас есть данные, можно начать осваивать выборки. Поиск происходит, так же как и вставка, ипользуя связку: {поле:значение}, так де есть операторы: $lt, $lte, $gt, $gte и $ne, обозначающие "меньше", "меньше или равно", "больше", "больше или равно", и "не равно". Попробуем выбрать всех людей, кто младше 30:
db.employees.find({"age":{$lte:30}})
Теперь попробуем выбрать тех, что младже 30 или у кого з/п выше 10000:
db.employees.find({$or:[{"age":{$lte:30}},{"salary":{$gte:10000}}]})
Если хотим выбрать только серьезных работников:
db.employees.find({"skills":'serious'})

вторник, 31 октября 2017 г.

MongoDb NoSql Общие сведения

1. Введение

MongoDb - документоорентированная NoSql база данных, используемая для хранения больших объемов информации, предоставляющая высокую производительность, высокую доступность и автоматическое масштабирование.

2. Что такое MongoDb?

  • MongoDb - документная база данных, каждая база данных содержит коллекции, которые в свою очередь содержат документы. Каждый документа может сосостоять из различного числа полей. Размер и содержимое каждого документа может быть различен и хранится в JSON подобном формате.
  • Строкам (или документам, если называть в стиле MongoDb), не нужно заранее созданной схемы. Вместо этого, поля можно создавать на лету.
  • Модель данных, доступная в MongoDb, позволяет разработчикам легче представлять иерархические отношения, хранить массивы и другие более сложные структуры

3. Что такое NoSql

БД NoSql обозначают не-SQL БД или без реляционный подход к БД.
  • Предоставляет механизм хранения и извлечения данных, отличных от табличной реляционной модели, используемой в реляционныз БД
  • Предоставляет большую гибкость, т.к. все записи не ограничены именами столбцов и типами, определенными во всей таблице.
  • Не использует стандартный язык SQL для запроса данных и не предусматривает строгую схему.
  • С NoSql, ACID ( Атомарность,Консистенция, Изоляция, Долговечность) свойства транзакций БД не гарантируются.
  • Предлагает масштабируемость и высокую производительность для обработки огромных объемов данных.

4. Почему использовать MongoDb

Ниже приведены несколько причин, почему, необходимо начать использовать MongoDb
  • Документо-ориентированность: т.к. MongoDb - NoSql БД, то вместо того, чтобы хранить данные в релационном формате - они хранятся в виде документа. Это делает MongoDb более гибкой.
  • Специальные запросы: MongoDb поддерживает поиск по полю,  диапазону и регулярным выражениям. Так же есть возможность вернуть поле документа.
  • Индексация: Индексы создаются, чтобы увеличить производительность поиска.
  • Автоматическая балансировка нагрузки: MongoDb использует концепцию Шардирования для горизонтального масштабирования , разбивая данные на нескольких экземплярах MongoDb.
  • Репликация: MongoDb может обеспечить высокую доступность набора реплик.

5. Различия с РСУБД

Ниже рассмотрены основные различия между NoSql и РСУБД.

  • В РСУБД используется термин Таблица, в MongoDb - Коллекция.  В РСУБД  таблица состоит из строк и столбцов, которые используются для хранения данныйх, в MongoDb, коллекции состоят документов, которые состоят из полей, которые состоят пары ключ-значение.
  • Строка  в РСУБД, Документ в MongoDb.
  • Столбец в РСУБД, поле в MongoDb.
  • Join в РСУБД, встроенные документы в MongoDb. В РСУБД часто данные хранятся в различных таблицах, чтобы показать полный набор данных - необходимо произвести Join таблиц. В MongoDb данные обычно хранятся в одной коллекции, но разделены встроенными документами, т.о Join, как концепция - отсутствует.
  • В РСУБД - Первичный ключ, в MongoDb - _id, который создается автоматически.

четверг, 26 октября 2017 г.

Java 9: Фабричный метод для создания неизменяемых коллекций

В данной статье рассмотрим новую фичу JDK 9 - создание неизменяемых коллекций. До Java9, если мы хотели получить неизменяемые коллекции - необходимо было вызвать unmodifiableXXX();  методы у java.util.Collections. Например, чтобы создать неизменяемый список, необходимо выполнить:

List<Strong> immutablelist = Collections.unmodifiableList(new ArrayList<String>(){{
            add("Smart");
            add("Techie");
}});

immutablelist.add("Smart_1");
При попытки добавлении новой записи "Smart_1" - получаем ошибку:

java.lang.UnsupportedOperationException thrown:
        at Collections$UnmodifiableCollection.add (Collections.java:1056)

Код выше - слегка многословен, чтобы создать немодифицированный список. Т.к. Java адоптирует функциональный стиль в Java 9 разработаны удобные, более компактные фабричные методы для создания немодифицируемых коллекций, как указано в JEP 269. Рассмотрим как это работает:

//Пустой список
List immutableList = List.of();
// Не пустой список
immutableList = List.of("Smart","Techie");
// Не пустая Map
Map immutableMap = Map.of(1,"Smart",2,"Techie");

 Если вы посмотрите на вышеупомянутый фабричный метод Java 9, код представляет собой простой однострочный набор для создания неизменяемых коллекций

PS это мой перевод данной стать.