Хранение данных, SQLite
Мы добрались до самого важного и популярного способа хранить данные — базы данных (БД). Базы данных — отдельная тема и целая специализация, о которой можно сделать пару отдельных курсов.
В следующих двух уроках мы дадим вам лишь самые основы на простых примерах, чтобы при необходимости вы дальше смогли сами разобраться. Итак:
План урока
- Что такое базы данных (БД) и SQL-запросы
- Какие бывают системы управления базами данных (СУБД)
- SQLite
- Наша первая БД
Что такое базы данных (БД)
Можно было бы, конечно, хранить данные просто в файликах, тем более, что теперь мы знаем такие замечательные форматы как XML и JSON. Чего же не хватало программистам, зачем нужно было создавать ещё один способ хранения данных.
Если совсем просто, то база данных — это собственно, информация, которую могут использовать в своей работе программы. Строго говоря, наши XML-файлики, которые мы использовали в предыдущих уроках — это тоже базы данных, но только очень простые.
Более серьезные базы данных обладают специальной программой, которая сама «раздает» и «собирает» данные: системой управления. Система управление — это специальная программа которая хранит ваши данные особым образом.
Представьте себе два типа магазина: огромный склад, где всё лежит, как попало, отсортированное кем-то однажды, в огромных штабелях. Вам приходится самому лазить по ним, искать, что нужно и смотреть, как бы вас там всем этим не завалило. А потом тащить на кассу, оплачивать и тащить домой. Как-то так можно представить себе работу с файликами.
А работа с базой данных — это заказ в удобном интернет-магазине с доставкой. Вы говорите, что вам надо, а заботливые сотрудники сами всё доставляют и привозят в лучшем виде.
У использования баз данных следующие преимущества:
- Удобство поиска
- Конфиденциальность, разделение доступа
- Оптимизация времени (поиск, сортировка результатов)
- Распределённость, многопоточность
Какие бывают системы управления базами данных (СУБД)
СУБД — это системы, которые фактически, исполняют ваши запросы к базе данных. Например, вы хотите узнать количество людей на планете Земля. Если бы у нас была подходящая база данных, мы могли бы сказать ей:
НАЙДИ всех ЛЮДЕЙ, которые живут на планете "Земля" и ПОКАЖИ их количество
(заглавными буквами выделены ключевые слова, чтобы придать этой фразе вид компьютерного запроса, хотя в реальности запросы выглядят немного иначе).
Базы данных бывают очень разные для разных задач: они могут отличаться способом хранения данных, способом получения данных, возможными функциями и так далее. Важнейший для программиста параметр — способ получения данных или, другими словами, «язык запросов».
SQL — structured query language
Самый распространенный сейчас язык запросов — SQL. Это специальный язык, на котором базы разговаривают с внешним миром. Вот самые популярные базы, система управления которых использует SQL: MySQL, PostgreSQL, sqlite, Oracle.
Сейчас мы на примере самой простой из них (sqlite) рассмотрим основные идеи и разберем основные команды SQL для создания и чтения данных. Кстати, именно эта БД используется для хранения данных в вашем смартфонах на айос или андроид, и поэтому можно смело сказать, что эта система управления базами одна из самых популярных.
Как устроена база данных внутри?
Данные в базах структурированы в виде таблиц, почти как в Экселе. Каждая таблица обычно соответствует какой-то одной сущности, например таблица «Люди» ( people ) может представлять собой список с информацией о людях. Строка таблицы содержит информацию о единице сущности: имя, пол, возраст. У каждого столбца есть название ( name , sex , age ) и тип (строка, целое число, булевский тип и т.д.).
Для того, чтобы к любой строке в любой таблице можно было обратиться, однозначно её идентифицировав, у каждой строки есть строго уникальное поле ID . Это столбец, содержащий «айдишники», он может называться по-разному, но суть одна — это уникальный идентификатор, который, как правило, записан в виде целого числа (для первой строки 1, для второй — 2 и т.д.). Чтобы всегда можно было однозначно обратиться к нужной строке, этот ID неизменен, то есть, если первую строку из таблицы удалить, ID второй строки всё равно останется 2. А третья строка добавится с ID равным 3.
Связи в БД, реляционные БД
Также ID-шники используются для того, чтобы создавать связи между таблицами. Допустим, у нас есть таблица людей people и таблица компаний companies . Тогда мы можем прописать у каждого человека в поле company_id идентификатор компании, в которой он работает и таким образом отобразить, какую строку из таблицы companies нужно достать, если мы хотим узнать о месте работы конкретного person .

Совокупность таблиц и связей между ними — это модель данных. Связи играют важную роль, это отражено в названии: SQL-базы относятся к категории «реляционных». Связи ускоряют доступ к данным и помогают сохранять порядок в базе.
Установка Dev-Kit
Для того, чтобы создать нашу первую базу данных, нам необходимо немного подготовиться. Для начала, давайте установим специальный пакет разработчика, который можно скачать с сайта установщика руби.

Если у вас не установлен архиватор 7zip, его можно скачать бесплатного с сайта 7zip. После скачивания, распаковываем архив в папку с:\dev .

И, наконец, устанавливаем пакет с утилитами в систему: для этого необходимо зайти в папку с:\dev в консоли и запустить установку:
cd c:\dev ruby dk.rb init ruby dk.rb install

Теперь у нас в системе есть инструмент для сборки более низкоуровневых программ, написанных на языке C/C++. Нам он пригодиться для сборки и установки sqlite.
Установка sqlite
Для этого нам необходимо скачать с сайта sqlite.org архив, в названии которого есть слово autoconf . На всякий случай вот прямая ссылка на версию 3.8.11.1. Вы также можете найти этот архив в материалах к уроку.
Для простоты дальнейшей работы распакуйте (можно всё тем же 7zip) архив, переименуйте получившуюся папку в sqlite и перенесите в корень, на диск c:\ .
Пришло время установить sqlite. Запустите файл msys.bat , который лежит в папке c:\dev , куда вы (надеюсь) распаковали наш Dev-Kit. Это специальная консоль, в которой доступно немного больше команд, чем в обычной.

Перейдите в консоли в папку sqlite и установите sqlite
cd c:\sqlite ./configure make make install
Если всё прошло гладко и никаких ошибок не возникло, в вашей системе установлен sqlite! Поздравляем!
Установка SQLite Manager
У нас на компьютере установлена база SQlite, но для того, чтобы с ней работать нам нужен ещё любой клиент. Давайте скачаем один из самых простых и лёгких в настройке: плагин для браузера FireFox (если у вас ещё нет этого браузера — скачайте скорее).
Итак, устанавливаем SQLite Manager для Firefox. Это довольно просто.

Язык SQL — кратко
Когда мы открывали файлы, мы сами руками выбирали как формат, в котором мы будем в них писать (либо просто текст, либо используя XML-парсер или конвертируя данные в JSON-текст), в базе данных мы ничего не пишем напрямую, мы формулируем базе свои желания в виде запросов. Вернее, в виде SQL-запросов.
Язык SQL-запросов — это отдельный язык со своим синтаксисом. Например, чтобы попросить базу вернуть нам все записи из таблицы people , нам нужно сформировать такой запрос:
SELECT * FROM people;
А чтобы добавить запись о компании Google в таблицу companies , нужно сформировать вот такой запрос:
INSERT INTO companies VALUES (`Google`);
Обратите внимание, все запросы завершаются знаком точки с запятой. Чуть больше о запросах мы узнаем на практике.
Создаём первую БД
Запустите плагин и создайте новую базу. Он предложит сохранить её где-нибудь на диске. Сохраните, например, в папке урока c:\rubytut2\lesson11\hellow_world.sqlite .
Создайте таблицу людей:

Обратите внимание, что при создании таблицы SQlite Manager показал нам запрос, который отправляется для создания этой таблицы. Да, для того, чтобы создавать таблицы (а в некоторых СУБД даже для создания баз данных) также используются SQL-запросы.
CREATE TABLE "main"."people" ("name" TEXT, "phone" TEXT, "birthday" DATETIME);
Создайте в вашей новой БД запись. Для этого во вкладке Выполнить запрос напишите INSERT-запрос:
INSERT INTO people VALUES ('Вадик', '+7 926 982-58-19', '1984-09-14 03:00:00');
Теперь в вашей новой таблице есть даже одна строка. Обратите внимание на поле rowid — это тот самый уникальный идентификатор, о котором мы говорили. В SQlite он называется rowid.

Философия БД
Базы данных — это отдельная вселенная. Ни одно современное приложение не обходится без базы данных, а в разделе вакансий для программистов всегда есть подраздел: программисты баз данных. Но даже если вы не планируете связать вашу жизнь с программированием баз данных, хороший программист должен знать основы и уметь использовать БД для решения своих задач.
Сейчас мы просто узнали, что такое БД и создали одну на пробу. А в следующем уроке мы подключим нашу СУБД к нашему блокноту на Ruby и будем хранить наши записи в SQlite-базе.
Как хранить чат в базе данных
Здравствуйте!
нужно написать чат, для переписки между сотрудниками. Чат будет использоваться в другой клиент-серверной программе, там уже есть БД сотрудников
id, фио, должность, уровень права доступа.
Так вот нужно организовать БД, для хранения сообщений чата. хранится все должно на сервере. при запросе от клиентов определенные сообщения должны передаваться, по сети, клиенту.
Думаю создать SQL таблицу:
id от кого, id кому, дата создания сообщения, дата когда прочитали сообщение.
собственно вопрос, вот в чем: как лучше хранить текст самого сообщения. На длину текста сообщения ограничений нет. По этому как лучше- запихнуть текст в таблицу? или хранить в отдельном файле?
Все это дело написано на Qt
Регистрация: 13.07.2012
Сообщений: 6,487
Если будет одна таблица то нельзя будет посылать одно сообщение многим адресатам.
Лучше две таблицы:
id от кого, дата создания, текст
и
id сообщения, id получателя, дата получение, всякие флаги — удалено и т.д.
Смысл хранить текст в отдельном файле нет, вот если пересылать файлы, тогда уже можно думать.
| Похожие темы | ||||
| Тема | Автор | Раздел | Ответов | Последнее сообщение |
| Чат бот, знакомства и чат с людьми | Corp | Gamedev — cоздание игр: Unity, OpenGL, DirectX | 0 | 17.06.2016 15:13 |
| Чат | Drago56 | C/C++ Сетевое программирование | 6 | 18.09.2015 22:55 |
| Чат | f3arnil | Фриланс | 5 | 22.01.2013 18:01 |
| Чат | eldar | PHP | 2 | 29.04.2009 00:30 |
БД для хранения истории сообщений
Добрый день. Пишу чат на qt. Нужно сделать так, чтобы при нажатии на определенного пользователя подгружалась история переписки с ним. Возникло несколько вопросов. Какую бд посоветуете для хранения сообщений? Я пока остановился на sqlite или postgresql. И как можно сделать так, чтобы более старые сообщения подгружались по мере необходимости, ведь если переписка будет огромная и будет подтягивать сразу целиком, то будут проблемы с производительностью. Буду признателен за помощь.

Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.
Подписка на обсуждение 2
Подписка на раздел 237
Вам это нравится? Поделитесь в социальных сетях!

- Evgenii Legotckoi
- #
- 25 мая 2018 г. 22:01
Для серверной части используйте postresql, если планируете и сервер писать для вашего чата.
Для кеширования сообщения в клиенте используйте однозначно sqlite. Использовать для клиента postgresql — это неправильно.
Что касается подгрузки, то здесь нужно отталкиваться от даты последнего сообщения, либо отталкиваться от ID (который должен быть уникальным для каждого сообщения).
Примерный мехнизм может быть такой. Если вы используете для отображения сообщений что-нибудь наподобие QListView, то у него можно использовать изменение значений вертиклаьного скроллбара. Если значения изменились до крайних, например до 0, то нужно проверить ID, самого старого сообщения в списке и попытаться подгрузить например ещё 50, которые ещё старше.
- evgenm27 → Evgenii Legotckoi
- #
- 26 мая 2018 г. 11:29
Спасибо за ответ, я с чатом раньше дела не имел, разъясните пару моментов? Получается на сервере будет postgre, где должна быть вся информация о пользователях (логин, пароль, имя, дата рождения к примеру). А на клиенте будет sqlite, где будет храниться именно переписка с пользователями?

- Evgenii Legotckoi → evgenm27
- #
- 27 мая 2018 г. 12:08
Если вы делаете чат с клиент-серверной архитектурой, то да, на этом самом сервере имеет смысл использовать postresql, но поначалу можно использовать SQLite для тестирования и т.д. Подключить postgresql на серверное приложение труда не составит. По сути сервер может даже быть написан на чём-то ином, а не на Qt.
У клиентских приложений можно использовать базу данных для кеширования сообщений, чтобы каждый раз не грузить все сообщения с сервера.
Это означает, что на сервере может храниться абсолютно вся переписка, а на sqlite у клиента дублированная переписка как кеш, в данном случае пользователь может запрашивать сообения сначала из кеша, а потом догружать то, чего у него нет из сервера.
Задача по проектированию чат-сервера
Как бы вы подошли к проектированию чат-сервера? Предоставьте информацию о компонентах внутренней системы (backend), классах и методах. Перечислите самые трудные задачи, которые необходимо решить.
Условие задачи
Как бы вы подошли к проектированию чат-сервера? Предоставьте информацию о компонентах внутренней системы (backend), классах и методах. Перечислите самые трудные задачи, которые необходимо решить.
Решение
Разработка чат-сервера — огромный проект, команды из многих людей проводят месяцы и годы, пытаясь создать хороший чат-сервер. Вы должны сосредоточиться на отдельном аспекте задачи. Ваше решение не обязано точно отражать реальность, но должно давать хорошее представление о фактической реализации.
Мы займёмся основными аспектами управления пользователями и организацией общения: добавлением пользователя, созданием беседы, обновлением статуса и т. д. Для экономии времени и места мы не будем углубляться в сетевые аспекты задачи и не будем рассматривать вопросы передачи данных между клиентами и сервером.
Предполагается, что «дружба» взаимна: вы дружите со мной только в том случае, если и я дружу с вами. Наша чат-система будет поддерживать как групповые, так и приватные беседы. Мы не будем рассматривать голосовой чат, видеочат и передачу файлов.
Какие конкретные операции должен поддерживать чат?
Мы приведем несколько примеров:
- Вход и выход из чата;
- Добавление запроса (отправка, приём и отклонение);
- Обновление информации о статусе;
- Создание приватных и групповых чатов;
- Добавление новых сообщений в приватные и групповые чаты.
Это далеко не полный список. Если вам хватит времени, добавьте дополнительные действия.
Что можно узнать из этих требований?
Необходимо реализовать концепцию пользователей, статуса добавления запроса, статуса подключения и сообщений.
Какие базовые компоненты должны присутствовать в системе?
Система будет состоять из базы данных, клиентов и серверов. Мы не включаем эти части в объектно-ориентированный проект, но можем обсудить общее видение системы.
База данных будет использоваться для долгосрочного хранения данных, например списка пользователей и архивов чата. В большинстве случаев подойдет SQL-бaзa данных, но, если нам понадобится большая масштабируемость, можно использовать BigTable или другую аналогичную систему. Подробнее вы можете узнать в нашем разделе, посвящённом базам данных.
Для обмена данными между клиентами и серверами подойдет XML. Хотя его компактность оставляет желать лучшего, он наиболее удобен для восприятия как человеком, так и компьютером. Использование XML также упрощает отладку приложения, а это имеет большое значение.
Примечание переводчика Ещё лучше для передачи данных подойдёт формат JSON.
Сервер будет состоять из множества компьютеров. Данные будут распределены между машинами, что требует «переключения» с одного устройства на другое. Возможно, некоторые данные придется перераспределять между машинами. Чтобы сократить время поиска, нужно избегать узких мест. Например, если аутентификацией пользователей занимается только одна машина, то её выход из строя закроет доступ к системе миллионам пользователей.
Какие основные объекты и методы должны поддерживаться системой?
Ключевые объекты нашей системы — пользователи, беседы и сообщения о статусах. Всё это можно реализовать в классе UserManager . Если бы мы уделяли больше внимания сетевым аспектам задачи или другим компонентам, вероятно, нам пришлось бы создать для них дополнительные объекты.
/* UserManager — центральный класс для основных действий пользователя. */ public class UserManager < private static UserManager instance; /* Связывает идентификатор с пользователем */ private HashMapusersById = new HashMap(); /* Связывает имя учётной записи с пользователем */ private HashMap usersByAccountName = new HashMap(); /* Связывает идентификатор пользователя с подключённым пользователем */ private HashMap onlineUsers = new HashMap(); public static UserManager getInstance() < if (instance == null) < instance = new UserManager(); >return instance; > public void addUser(User fromUser, String toAccountName) < User toUser = usersByAccountName.get(toAccountName); AddRequest req = new AddRequest(fromUser, toUser, new Date()); toUser.receivedAddRequest(req); fromUser.sentAddRequest(req); >public void approveAddRequest(AddRequest req) < req.status = RequestStatus.Accepted; User from = req.getFromUser(); User to = req.getToUser(); from.addContact(to); to.addContact(from); >public void rejectAddRequest(AddRequest req) < req.status = RequestStatus.Rejected; User from = req.getFromUser(); User to = req.getToUser(); from.removeAddRequest(req); to.removeAddRequest(req); >public void userSignedOn(String accountName) < User user = usersByAccountName.get(accountName); if (user != null) < user.setStatus(new UserStatus(UserStatusType.Available, "")); onlineUsers.put(user.getId(), user); >> public void userSignedOff(String accountName) < User user = usersByAccountName.get(accountName); if (user != null) < user.setStatus(new UserStatus(UserStatusType.Offline, "")); onlineUsers.remove(user.getId()); >> >
Метод receivedAddRequest класса User оповещает пользователя В о том, что пользователь А запросил добавление его в список контактов. Пользователь В соглашается или отклоняет запрос (при помощи UserManager.approveAddRequest или rejectAddRequest ), а класс UserManager обеспечивает добавление пользователей в списки контактов двух пользователей.
Метод sentAddRequest класса User вызывается UserManager для добавления AddRequest в список запросов пользователя А. Поэтому последовательность операций должна выглядеть так:
- Пользователь А нажимает кнопку «добавить пользователя» в клиентской программе, запрос отправляется на сервер.
- Пользователь В вызывает requestAddUser(User В) .
- Этот метод вызывает UserManager.addUser .
- UserManager вызывает методы UserA.sentAddRequest и UserB.receivedAddRequest .
Это всего лишь один из возможных способов проектирования подобных взаимодействий, но не единственный и даже не самый лучший.
import java.util.ArrayList; import java.util.Date; import java.util.HashMap; public class User < private int id; private UserStatus status = null; /* Связывает идентификатор пользователя другого участника с чатом */ private HashMapprivateChats = new HashMap(); /* Список групповых чатов */ private ArrayList groupChats = new ArrayList(); /* Связывает идентификатор другого пользователя с полученным запросом на добавление */ private HashMap receivedAddRequests = new HashMap(); /* Связывает идентификатор другого пользователя с отправленным запросом на добавление*/ private HashMap sentAddRequests = new HashMap(); /* Связывает идентификатор пользователя с объектом пользователя */ private HashMap contacts = new HashMap(); private String accountName; private String fullName; public User(int id, String accountName, String fullName) < this.accountName = accountName; this.fullName = fullName; this.id = id; >public boolean sendMessageToUser(User toUser, String content) < PrivateChat chat = privateChats.get(toUser.getId()); if (chat == null) < chat = new PrivateChat(this, toUser); privateChats.put(toUser.getId(), chat); >Message message = new Message(content, new Date()); return chat.addMessage(message); > public boolean sendMessageToGroupChat(int groupId, String content) < GroupChat chat = groupChats.get(groupId); if (chat != null) < Message message = new Message(content, new Date()); return chat.addMessage(message); >return false; > public void setStatus(UserStatus status) < this.status = status; >public UserStatus getStatus() < return status; >public boolean addContact(User user) < if (contacts.containsKey(user.getId())) < return false; >else < contacts.put(user.getId(), user); return true; >> public void receivedAddRequest(AddRequest req) < int senderId = req.getFromUser().getId(); if (!receivedAddRequests.containsKey(senderId)) < receivedAddRequests.put(senderId, req); >> public void sentAddRequest(AddRequest req) < int receiverId = req.getFromUser().getId(); if (!sentAddRequests.containsKey(receiverId)) < sentAddRequests.put(receiverId, req); >> public void removeAddRequest(AddRequest req) < if (req.getToUser() == this) < receivedAddRequests.remove(req); >else if (req.getFromUser() == this) < sentAddRequests.remove(req); >> public void requestAddUser(String accountName) < UserManager.getInstance().addUser(this, accountName); >public void addConversation(PrivateChat conversation) < User otherUser = conversation.getOtherParticipant(this); privateChats.put(otherUser.getId(), conversation); >public void addConversation(GroupChat conversation) < groupChats.add(conversation); >public int getId() < return id; >public String getAccountName() < return accountName; >public String getFullName() < return fullName; >>
Класс Conversation реализован как абстрактный, потому что все экземпляры Conversation должны относиться либо к классу GroupChat , либо к классу PrivateChat , а каждый из этих классов обладает собственной функциональностью.
import java.util.ArrayList; import java.util.Date; public abstract class Conversation < protected ArrayListparticipants = new ArrayList(); protected int id; protected ArrayList messages = new ArrayList(); public ArrayList getMessages() < return messages; >public boolean addMessage(Message m) < messages.add(m); return true; >public int getId() < return id; >> public class GroupChat extends Conversation < public void removeParticipant(User user) < participants.remove(user); >public void addParticipant(User user) < participants.add(user); >> public class PrivateChat extends Conversation < public PrivateChat(User user1, User user2) < participants.add(user1); participants.add(user2); >public User getOtherParticipant(User primary) < if (participants.get(0) == primary) < return participants.get(1); >else if (participants.get(1) == primary) < return participants.get(0); >return null; > > public class Message < private String content; private Date date; public Message(String content, Date date) < this.content = content; this.date = date; >public String getContent() < return content; >public Date getDate() < return date; >>
Классы AddRequest и UserStatus — простые классы с минимальной функциональностью. Их основное назначение — группировка данных, используемых другими классами.
import java.util.Date; public class AddRequest < private User fromUser; private User toUser; private Date date; RequestStatus status; public AddRequest(User from, User to, Date date) < fromUser = from; toUser = to; this.date = date; status = RequestStatus.Unread; >public RequestStatus getStatus() < return status; >public User getFromUser() < return fromUser; >public User getToUser() < return toUser; >public Date getDate() < return date; >> public class UserStatus < private String message; private UserStatusType type; public UserStatus(UserStatusType type, String message) < this.type = type; this.message = message; >public UserStatusType getStatusType() < return type; >public String getMessage() < return message; >> public enum UserStatusType < Offline, Away, Idle, Available, Busy >public enum RequestStatus
Какие проблемы окажутся самыми трудными (или интересными)?
Попробуйте ответить на следующие вопросы:
Как определить активных пользователей?
Подключённый пользователь — не всегда активный пользователь. Если сеанс не был корректно завершён, сессия может «зависнуть», расходуя ресурсы сервера и искажая реальное количество активных пользователей. Как с достаточно большой точностью определить активность пользователей?
Что делать с информационными конфликтами?
Часть информации должна храниться в оперативной памяти, а часть — в базе данных. Что случится, если синхронизация между ними будет нарушена? Какую информацию считать правильной?
Что делать с возрастающим количеством пользователей?
Как на этапе проектирования заложить многократное увеличение количества пользователей?
Как защититься от DоS-атак?
Клиенты могут передавать данные на сервер, но что, если сервер попытаются «завалить» потоком запросов? Как предотвратить атаку?