Если серьезно, то сегодня мы поговорим про БАЗЫ данных. Как-то один мой друг разработчик сказал, что программирование можно понимать как
"настройка передачи данных из точки А в точку Б"

Повторюсь! Кажется при чем тут летящая машина. А это символика того, что данные летят из точки А в точку Б.
Кажется, при чем тут летящая машина. А это символика того, что данные летят из точки А в точку Б. Повторюсь! Кажется, при чем тут летящая машина. А это символика того, что данные летят из точки А в точку Б. Так же осмелюсь сказать, что так ощущается отрыв от реальности, когда ты погружаешься в любую тему со мной =) Поначалу это утверждение показалось мне недостаточно полным. Однако если убрать шелуху и оставить только суть, то программирование это буквально передача данных. Во время этого процесса мы можем изменять данные, но по итогу мы получаем буквально движение информации из точки А в точку Б.
И сегодня мы поговорим про точку Б — Базу данных.
Обычно итоговые данные нужно хранить, передавать и обрабатывать. Хранение данных как раз обеспечивают базы данных. Обычно для хранения используют именно таблицы, хотя есть еще и хранилища, которые тоже внутри себя представляют таблицы, которые пользователям не показывают. И формат хранения определяется фундаментальной возможностью таблиц. Всего у нас два основных инструмента:
Таблица — это удобный и структурированный подход к хранению данных, представляет собой колонки с названиями и строчки записей. Базы данных представляются в виде таблиц.
Реляционная БД (PostgreSQL, MySQL, Oracle) — из названия следует relation — связь. Связанные таблицы — это мощный инструмент для хранения данных и дальнейшего использования. Создание связи между таблицами позволит вам быстро находить информацию. Например, у вас есть таблица "мировая таблица Бренды АВТО" — внутри есть разные бренды: AUDI, Mercedes, BMW. В свою очередь, внутри, например, бренда BMW вы можете увидеть связанную таблицу автомобилей M5, 320i xDrive, M8, а внутри каждого автомобиля — таблицу с характеристиками. В такой таблице у вас появляется возможность быстро находить связанные данные и выгружать их.
Нереляционная БД — очевидно, что тут речь идет уже о самостоятельных таблицах, которые не имеют никаких связей. Это менее удобный и ограниченный инструмент для хранения данных, потому что мы не сможем связывать данные из разных таблиц между собой. У вас есть одна таблица, которая ни с чем не связана. Грустно. Однако у нее есть свои плюсы: такие таблицы удобны, когда вам достаточно одной таблицы, но необходима скорость поиска.
Вернемся к реляционным базам. Чаще всего в разработке приложений мы работаем с такими БД, потому что, как уже говорил выше, мы можем связать сущности и доставать необходимые данные в удобном формате. Вы, вероятно, обратили внимание на названия таких БД и заметили там приписку SQL.
SQL (Structured Query Language) — это структурный язык запросов, предназначенный для управления, хранения, изменения и извлечения данных в реляционных базах данных.
СУБД (система управления базами данных) — это комплекс программного обеспечения, предназначенный для создания, хранения, изменения, поиска и администрирования данных в электронном виде.
Что такое язык? На нем кто-то говорит? Да, посредством такого языка мы общаемся с БД. Пример: у вас есть друг-библиотекарь, он работает в библиотеке. В данном примере библиотека — это наша база данных, например PostgreSQL, а библиотекарь, то есть ваш друг, — это и есть движок на базе SQL. Вы говорите ему: "Принеси мне все книги по физике", и он это делает. Но так как мы общаемся посредством языка не с человеком, а с программой, то мы должны использовать строгий язык, понятный движку SQL. Правильно написанные запросы позволят доставать сгруппированные, отфильтрованные данные.
Теперь коснемся той части, с которой мы работаем как Java-разработчики. Мы пишем код, то есть достаем данные из нашего хранилища БД. Получаем, изменяем, сохраняем и удаляем данные. Нас интересует, как мы в коде можем взаимодействовать с БД. То есть мы можем писать запросы на SQL вручную. Но благо есть инструменты, которые ускоряют написание запросов.
Самый низкоуровневый инструмент — это JDBC.
JDBC (Java Database Connectivity) — прямой API для отправки SQL в БД из Java. Ты пишешь строку запроса, выполняешь, получаешь ResultSet. Такой инструмент дает нам возможность получать данные из БД и сохранять. Однако мы получаем данные из БД в определенном формате ResultSet — табличные данные в виде строк и колонок, а нам нужно получить объекты. Ты сам вручную пишешь rs.getString("name") и кладешь в поле объекта, мапя соответствующие поля, и получаешь удобный и понятный объект для дальнейшей работы в среде Java.
Термины:
JPA (Java Persistence API) — это спецификация ORM (Object-Relational Mapping). Hibernate — самая популярная реализация. Ты работаешь с объектами Java, а JPA сама генерирует SQL, управляет кешем первого уровня, lazy-загрузкой.
Hibernate — это реализация спецификации JPA. Но если JPA — это только интерфейс (набор аннотаций и правил), то Hibernate — это конкретный код, который:
Читает твои Java-классы с аннотациями (@Entity, @Id, @OneToMany).
По ним генерирует SQL-запросы (CREATE, INSERT, SELECT, JOIN...).
Выполняет эти запросы через JDBC.
Забирает ResultSet и превращает строки обратно в Java-объекты.
ORM (Object-Relational Mapping) — это инструмент для маппинга, то есть соединяет по названию колонки значения с полями в объекте и возвращает объект. То же самое делает в обратную сторону.
JPA — это спецификация, которая еще сильнее облегчает работу с базами данных за счет сильного инструмента Hibernate. Как видно, всю магию кибер берет на себя, генерирует запросы. Внутри JPA-спецификации есть набор базовых обращений в БД. Есть еще более удобная надстройка над JPA.
JpaRepository — это часть Spring Data JPA (надстройка над JPA). Чистый JPA (без Spring) использует EntityManager и методы persist(), find(), merge(), remove(). Spring Data JPA добавляет удобные репозитории с готовыми методами. Писать ничего не надо, всё уже есть. Если вы наследуете от JpaRepository<Type_of_Object, Type_for_ID> — внутрь дженерика передаете сначала объект, который берется за основу при работе с таблицей, к которой будете обращаться посредством Hibernate, а вторым аргументом — тип данных для вашего id:
save(entity) — сохраняет объект (INSERT, если id=null, иначе UPDATE)
saveAll(entities) — сохраняет список объектов (пакетно)
findById(id) — ищет по первичному ключу, возвращает Optional
existsById(id) — проверяет, существует ли запись с таким id
findAll() — возвращает все записи
findAllById(ids) — возвращает записи по списку id
count() — возвращает количество записей
delete(entity) — удаляет объект
deleteById(id) — удаляет по id
deleteAll() — удаляет всё (осторожно!)
flush() — принудительно синхронизирует изменения с БД
saveAndFlush(entity) — сохраняет и сразу выполняет flush
Также будут доступны производные запросы типа поиска по одному или нескольким полям. Обычно они так и называются: findByName(). Внутри по ключевому слову пишется SQL-запрос WHERE name.
После того как мы узнали про базы данных, про язык запросов SQL, теперь различаем реляционные и нереляционные. Разработчику становится важна скорость поиска и скорость вставки информации в данных.
Если вы водитель, то вам будет понятно как работает изучение сложных программ.

Только вперед!
Только вперед! Вспомните: когда вы первые разы сидели за рулем, на чем был ваш фокус? На том, как не бросить сцепление, не перегазовать, поставить верную скорость и не заглохнуть. А тут еще и сверху внимание на пешеходов, другие машины. Ваш мозг был занят важнейшими процессами — переключение скоростей и контроль габаритов. Однако спустя три месяца вы внезапно для себя обнаруживаете, что способны построить маршрут сложнее, успеваете перестроиться, учитываете тормозной путь и в целом способны осваивать новые горизонты вождения. А спустя год-два вам становится интересно, как удержать машину в заносе. Это я о чем?
О том, что программирование — то же самое. Сначала вы тратите ресурсы на умение читать код, не путаться в синтаксисе. Все сложные концепции проходят вас стороной. Однако, когда ресурсы освобождаются, вы легко читаете код, пишете сами из головы, можете позволить себе фривольность в реализации: тут протестировать такой подход, в другом месте объединить дублирующий код. Потом позволяете себе зайти на территорию, ранее не доступную — территорию скорости и распределения памяти между ресурсами. И вот как раз на этом этапе вам будет интересно узнать, что там по скорости записи.
Помним, что любая операция чтения или записи данных — это физическое перемещение информации между носителем и оперативной памятью. В зависимости от того, где хранятся физически данные, будет зависеть и скорость их поиска. Основные носители — это жесткий диск (HDD, SSD) и оперативная память RAM.
RAM читает данные за наносекунды. SSD — за миллисекунды. Разница в 1000 раз даже на самом быстром диске.
Оперативная память — мощный инструмент, но она оперативная, потому что держит данные прямо сейчас, оперативно. Как только ты отрубаешься от сети, RAM теряет все данные. Поэтому скорость RAM следует использовать с умом. Иногда это незаменимый мощный инструмент: Redis как раз позволяет пользоваться таким инструментом, как RAM, для записи и хранения данных in-memory.
Как мы можем ускорить поиск, если пользуемся памятью на жестком диске? Есть такой инструмент, как индексы. Они позволяют...
Индекс — это отдельная структура данных, которая хранит значения и ссылки на физическое расположение строк в таблице. Пример поиска:
Таблица 10 млн строк, full scan = 500 000 чтений с диска → 2-5 секунд
B-tree индекс = 4-5 чтений с диска → 1-2 миллисекунды
Есть основные типы индексов.
B-tree работает за O(log N). Используется в PostgreSQL, MySQL, Oracle. Применяется для равенства, диапазонов и сортировки.
Хеш работает за O(1). Живет в Redis и MEMORY-таблицах MySQL. Используй только для точного совпадения. Диапазоны и сортировку сделать будет сложно, поиск связанных сущностей — тоже, но есть мощный инструмент RediSearch, который позволит проиндексировать значения хэша, который передадим как значение.
Инвертированный индекс имеет особую скорость поиска. Работает в Elasticsearch и Lucene. Можно использовать, когда нужно искать по словам и тексту.
Важную тему затронули сегодня. Мы поговорили про базы данных: про то, как они устроены, чем отличаются реляционные от нереляционных, как мы к ним обращаемся через JDBC, JPA и Hibernate, и почему Spring Data JPA с его JpaRepository делает нашу жизнь проще.
А потом мы залезли чуть глубже. Поговорили про скорость. Понятно, что теория без практики ничего не значит. Но практика без теории — тоже, я вам скажу, ни о чем. Ты вроде делаешь, закрываешь задачки, но внутри пусто, ведь сам знаешь, что не сильно копаешь и тратишь много усилий впустую, особенно когда приходит бага. Поэтому, ребят, вникайте как можно глубже.
Приятно понимать, что твои возможности безграничны, стоит лишь продолжать.
Мой ник в ТГ: @karim_product — я говорю простыми словами о сложном.
Если было полезно, поддержи подпиской. Мне будет мотивация продолжать в том же духе. Мой канал - https://t.me/+uH8Hm6kPWhU2OTc6
Akina
Мда... возьмите чистый лист, скопируйте откуда-нибудь пару фактов, долейте воды, небрежно перемешайте, наклейте пару фотографий - статья готова.
Это - ужасно.
foxsoft2005
Есть и другое слово. Но мы же приличные люди, не так ли?
KarimAbushaev Автор
Чтобы улучшить качество моих статей, мне нужна обратная связь. Сейчас коммент бесполезный как на него не взглянуть. Но если будет дан мне развернутый фидбек на мою работу, я смогу сделать качество статей лучше и всем будет полезнее
VVitaly
:-( "Полезнее" для разработчиков работать не "с объектами и их свойствами" (через различные "прокладки" между сервером приложения и БД, типа указанных в статье), а с базой "напрямую". Тогда "проблемы производительности" типа - GetObject + GetProperties + SetProperties + SaveObject + и это все в цикле по хрен знает какому количеству "объектов" хранимых как xml строки (которые нужно парсить) - уйдут как страшный сон.
А еще если знать как оптимально и эффективно хранить и работать с данными в конкретной реализации БД - то вообще замечательно.... :-)