Что такое Map<D,B>
Вот так они рекламируют себя на сайте:
MapDB provides Java Maps, Sets, Lists, Queues and other collections backed by off-heap or on-disk storage. It is a hybrid between java collection framework and embedded database engine.
Что можно перевести при мерно как:
MapDB представляет возможность работы с Java Maps, Sets, Lists, Queues и другими коллекциями с хранением их вне кучи или на диске. Это что-то среднее между java collection framework и embedded database engine.
В данном случае посмотрим как можно использовать Map<D,B>
для хранения данных на диске, чтобы не заморачиваться с разворачиванием отдельной СУБД.
Пример использования
Рассмотрим небольшой пример Spring boot
приложения, в котором будем использовать Map<D,B>
в качестве основного хранилища данных. Предположим что у нас уже есть готовы проект со базовым набором Spring boot зависимостей, а в качестве системы сборки используется Maven
.
Зависимости
Для начала добавим зависимость Map<D,B>
в наш проект с актуальной на момент написания поста версией.
<dependency>
<groupId>org.mapdb</groupId>
<artifactId>mapdb</artifactId>
<version>3.1.0</version>
</dependency>
Определим Bean-ы
Начнем с класса конфигурации в котором определим bean нашей БД.
import org.mapdb.DB;
import org.mapdb.DBMaker;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MapDBConfig {
@Bean
public DB mapDBContext(@Value("${app.mapdb.path-to-file}") String filename) {
return DBMaker.fileDB(filename)
.fileMmapEnableIfSupported()
.closeOnJvmShutdown()
.transactionEnable()
.make();
}
}
В данном случае мы выносим путь к файлу, в котором будут хранится наши данные в properties
-файл конфигурации, чтобы иметь чуть больше контроля над тем где наши данные хранятся. И упростить деплой нашего приложения в случае контейнеризации, чтобы иметь возможность смонтировать директорию с файлом на диск вне контейнера, для сохранения данных между перезапусками и повторным деплоем.
Дальше создадим репозиторий, который будет отвечать за сохранение и чтение данных. Предположим что для целей нашего приложения нам достаточно хранить данные в виде пар строк ключ-значение. Тогда конфигурация нашего репозитория будет примерно следующая:
import org.mapdb.DB;
import org.mapdb.Serializer;
import org.springframework.stereotype.Repository;
@Repository
public class MapDbRepository {
private final DB db;
private final DB.HashMapMaker<String, String> keyValueStore;
public MapDbRepository(DB db) {
// инжектим наш бин с БД в поле класса, он нам ещё пригодиться
this.db = db;
// инициализируем наше хранилище ключ-значение
this.keyValueStore = db.hashMap("keyValueStore") // указываем название
// указываем сериализатор для ключей и значений нашего хранилища
.keySerializer(Serializer.STRING).valueSerializer(Serializer.STRING);
}
}
Теперь добавим обработчик события удаления бина из контекста с помощью аннотации @PreDestroy
, чтобы корректно завершить доступ к файлам и исзбежать возможной потери данных
@PreDestroy
void close() {
db.close(); // закрываем БД чтобы не потерять данные
}
Дальше можем добавить метод сохранения значения в наш репозиторий.
public void save(String key, String value) {
// открываем наш ресурс для записи или создаем новый, если мы еще ничего не сохраняли
HTreeMap<String, String> map = keyValueStore.createOrOpen();
// добавляем наше значение
map.put(key, value);
// пишем данные на диск
db.commit();
}
Тут IDE или статические анализаторы кода могут ругаться что мы не закрываем ресурс HTreeMap<String, String> map
, но мы это делаем сознательно. В противном случае при повторном обращении к ресурсу мы получим ошибку что он не доступен.
Метод для чтения данных с диска будет выглядеть примерно так:
public String findValue(String key) {
HTreeMap<String, String> map = keyValueStore.createOrOpen();
return map.get(key);
}
На этом наш простенький репозиторий готов и мы можем его использовать как Spring bean внутри нашего приложения.