Microstream – Java Objektgraph als Datenbank

Die neue in Memory Datenbank

Microstream ist die neue in Memory Datenbank für Java. Aus dem Hause XDev stammt diese revolutionäre Datenbank. Nachdem im Frühjahr das Produkt umbenannt worden ist, heißt es nicht mehr JetstreamDB sondern Microstream. Die Microstream Datenbank ist kostenlos und kann frei verwendet werden.

Was ist Microstream?

Microstream ist ein Framework das Java Objekte ultraschnell serialisieren und desialisieren des kann. Das macht sich Microstream zu nutze und umgeht damit unvermeidbare Netzwerklatenzen, die ansonsten bei klassischen RDBMs entstehen.

Wie funktioniert Microstream?

Es gibt eine Root Entität. Diese enthält alles was in der Datenbank gespeichert werden soll. Es gibt also hier kein Datenbank Schema oder ähnliches. Die Java Objekte sind die Entitäten. Das war es. Alles was sich also in dem Objektgraph unterhalb der Root Entität befindet, kann persistiert werden.

Was ist mit Queries?

Abfragen, wie man es bei einer normalen RDBMs kennt, gibt es nicht. Und braucht man auch nicht. Man verwendet einfach das Streaming API von Java und erreicht Wahnsinnig schnelle Abfragen in Millisekunden Bereich.

Demoanwendung

Es soll hier in einer kleinen Demoanwendung die Verwendung der Microstream API veranschaulicht werden. Ich verwende hier das SpringBoot Framework in der aktuellsten Version 2.1.4. Die Anwendung erzeugt bei jedem Start eine neue Einladung. Die Einladungen bestehen aus einer ID und einem Namen. Super einfach. Die ID wird hierbei immer um einen erhöht. Der Name ist hier statisch. Um den Code vom unnötigen Boilerplate frei zu halten, verwende ich Lombok.

Abhängigkeiten einbinden

Es muss das Repository von microstream eingebunden werden. Die Group ID .heißt one.microstream und es muss das Artefakt storage.embedded in der Version 01.00.00-MS-RC1 Hier das Gradle Buildscript:

repositories {
    mavenCentral()
    maven { url 'https://repo.microstream.one/repository/maven-releases/' }
}


dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation('one.microstream:storage.embedded:01.00.00-MS-RC1')
    annotationProcessor('org.projectlombok:lombok')
    compileOnly('org.projectlombok:lombok')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

Die Entität

Die Invocartion stellt die Entität des Repositories dar. Sie besteht aus der ID und dem Namen der Einladung.

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Invocation {
    private Long id;

    private String name;
}

Pretty simple, aber erfüllt für die Demo genau seinen zweck.

Insert new Invocation

Der CommandLineRunner wird beim Starten des SpringBoot Container ausgeführt. Es sind für die Verwendung der in Memory Datenbank zwei statische Variable nötig. Die eine enthält das Root des Objektgraphen hier ROOT genannt und eine STORAGE die für das Persistieren auf der Festplatte.

Wie gelange jetzt die Daten in den Objektgraphen?

@Component
@Log
public class InitDatabase implements CommandLineRunner {
    // to stop spring boot application
    @Autowired
    private ConfigurableApplicationContext cac;

    // this is the ROOT of object graph
    private static Reference<InvocationRepository>ROOT = 
        X.Reference(new InvocationRepository());

    // Storage manager
    private static EmbeddedStorageManager STORAGE = 
        EmbeddedStorage.start(ROOT);

    @Override
    public void run(String... args) throws Exception {
        // load all into a local list
        var rootData = ROOT.get();
        List<Invocation> invocations = rootData.getInvocations();

        // print size of Repository
        log.info("size: " + rootData.size());

        // add a new Incocation to Repository
        Invocation invocation = new Invocation();
        invocation.setId(Long.valueOf(rootData.size()+1));
        invocation.setName("Test");
        invocations.add(invocation);
        invocations.forEach(i-> log.info(i.toString()));

        // store
        STORAGE.store(ROOT.get().getInvocations());

        // close
        STORAGE.shutdown();

        // shutdown SpringBoot container
        cac.close();
    }
}

Das Repository

Das Repository ist sehr einfach gehalten.

public class InvocationRepository {
    private final List<Invocation> invocations = new ArrayList<>();

    /**
     * get a List of Invocations
     * @return List of Invocation
     */
    public List<Invocation> getInvocations() {
        return invocations;
    }


    /**
     * Size of Repository
     * @return
     */
    public int size() {
        return invocations.size();
    }
}

Einschränkungen

Leider gibt es mit dem Classloader ein Problem, wenn man die DevTools von SpringBoot mit einbindet. Daher habe ich diese aktuell noch nicht eingebunden.