6. Слой источников данных - MaxCiv/LibraryArchitecture GitHub Wiki
Для реализации слоя источников данных используется реляционная база данных. В качестве СУБД выбрана система MySQL. Для подключения к базе данных в исходном коде используется синглтон-класс DataGateway. Данный класс содержит параметры подключения к базе данных, подключается к ней и предоставляет всем остальным классам слоя источника данных доступ к БД.
Для отображения объектов программы в таблицы БД были использованы следующие структурные паттерны источников данных:
- Поле идентификации (Identity Field) — каждый класс имеет поле id, которое позволяет идентифицировать экземпляры объектов этого класса. Данное поле является автогенерируемым (средствами БД);
- Отображение внешних ключей (Foreign Key Mapping) — внутренние ссылки объектов друг на друга отображаются в БД в качестве внешних ключей.
База данных имеет следующий вид:
В качестве шлюза к слою используется интерфейс Repository, который реализует паттерн "Репозиторий". Это посредник между уровнями области определения (хранилище) и распределения данных. Использует интерфейс, похожий на коллекции, для доступа к объектам области определения. Репозиторий инкапсулирует набор объектов, сохраняемых в хранилище данных, и операции выполняемые над ними, обеспечивая более объектно-ориентированное представление реальных данных. Репозиторий также преследует цель достижения полного разделения и односторонней зависимости между уровнями области определения и распределения данных.
Интерфейс Repository
(пакет storage
) определяет следующие методы:
-
User logInUser(String login, String realPassword)
— выполнить авторизацию пользователя; -
User findUserById(int id)
— найти пользователя по id; -
User findUserByLogin(String login)
— найти пользователя по логину; -
Book findBookById(int id)
— найти книгу по id; -
List<Book> getAllBooks()
— получить список всех книг; -
List<BookBorrow> getAllBorrows()
— получить список всех выдачей книг читателям; -
List<BookBorrow> getAllUserBorrows(Reader reader)
— получить список всех выдачей книг читателю с id; -
List<BookExchange> getAllExchanges()
— получить список всех обменов книг читателями; -
List<BookExchange> getAllUserExchanges(Reader reader)
— получить список всех обменов книг читателя с id; -
List<BookOrder> getAllOrderings()
— получить список всех заказов книг поставщикам; -
List<BookOrder> getAllUserOrderings(Supplier supplier)
— получить список всех заказов книг поставщика с id; -
List<BookRecord> getAllRequireConfirmation()
— получить список всех выдачей книг читателям, требующих подтверждения от библиотекаря; -
List<User> getAllUsers()
— получить список всех пользователей; -
void addNewUser(User newUser)
— добавить нового пользователя; -
void addNewBook(Book book)
— добавить новую книгу; -
void addNewBookBorrow(BookBorrow bookBorrow)
— добавить новую выдачу книги; -
void openNewExchange(BookExchange bookExchange)
— открыть новый обмен; -
void openNewOrder(BookOrder bookOrder)
— открыть новый заказ; -
void update(Object obj)
— обновить объект; -
void updateAll()
— обновить все объекты; -
void clearAll()
— очистить весь кэш объектов; -
void dropAll()
— очистить базу данных.
Для создания слоя источников данных используется архитектурный паттерн "Преобразователь данных" (Data Mapper). Для всех классов слоя бизнес-логики был создан свой преобразователь. Преобразователи данных позволяют иерархически считывать и обновлять данные в БД.
Каждый класс-преобразователь данных реализует интерфейс Mapper
. Интерфейс объявляет следующие методы:
-
T findById(final int id)
— найти объект в БД по идентификатору; -
Map<Integer, T> findAll()
— найти все объекты в БД; -
void update(T item)
— обновить объект; -
void update()
— обновить все объекты; -
void closeConnection()
— закрыть соединение с БД.
Как видно из методов интерфейса, каждый преобразователь данных так же имеет свой локальный кэш объектов, извлеченных из БД. При обращении к преобразователю за объектом, он сначала проверяет его наличие в кэше. Если объект найден в кэше, возвращается ссылка на него. Если объекта в кэше нет, то производится обращение к БД. Извлеченный из БД объект так же сразу добавляется в локальный кэш. Данное решение позволяет обеспечить, чтобы в каждый момент времени в системе существовал только один экземпляр какого-либо объекта. При этом, преобразователи данных при синхронизации с БД могут не обновлять некоторые элементы (например, если объект неизменяемый), что позволяет немного оптимизировать работу.
Все преобразователи находятся в пакете storage.mappers
.
Для тестирования слоя источников данных написан JUnit тест MappersRepositoryTest
(находится в модуле test
, пакете storage
), который проверяет все основные методы класса репозитория и преобразователей.