Android Coden
Android 6 min lesen

Entities in Android

Entities beschreiben gespeicherte Datensätze klar. Du lernst, Tabellen, Primärschlüssel und Spalten sauber von UI-Modellen zu trennen.

Entities sind ein Kernbaustein der lokalen Datenspeicherung in Android-Apps. Wenn du mit Room, SQLite oder einer Offline-First-Architektur arbeitest, brauchst du ein klares Modell dafür, welche Datensätze dauerhaft gespeichert werden, wie sie identifiziert werden und welche Spalten sie besitzen. Genau hier helfen Entities: Sie machen aus Kotlin-Klassen eine verlässliche Beschreibung deiner Tabellen, ohne dass du deine UI-Struktur direkt an die Datenbank bindest.

Was ist das?

Eine Entity beschreibt einen Datensatz, der persistent gespeichert wird. Im Android-Kontext bedeutet das häufig: Eine Kotlin-Datenklasse wird mit Room-Annotationen so markiert, dass daraus eine Tabelle in der lokalen Datenbank entsteht. Die Properties der Klasse werden zu Columns, also Spalten. Eine Property wird meist als Primary Key festgelegt, damit jeder gespeicherte Datensatz eindeutig adressierbar ist.

Das wichtigste mentale Modell ist: Eine Entity gehört zur Datenebene, nicht zur Oberfläche. Sie beantwortet Fragen wie: Welche Felder speichere ich dauerhaft? Welche Spalte ist eindeutig? Welche Werte dürfen fehlen? Wie sieht der Datensatz aus, wenn die App neu gestartet wird oder offline weiterarbeitet? Sie beantwortet nicht direkt, wie ein Compose-Screen aussehen soll oder welche Texte, Icons und Farben dort angezeigt werden.

Diese Trennung ist in modernen Android-Architekturen wichtig. Die Datenebene stellt Daten bereit, Repositories koordinieren Quellen wie Netzwerk und lokale Datenbank, und die UI beobachtet daraus abgeleitete Zustände. Wenn du eine Entity direkt als UI-Modell verwendest, wirkt das am Anfang praktisch. Später wird es jedoch eng: Die Datenbank braucht stabile Spalten, die UI braucht oft berechnete Texte, Formatierungen, Ladezustände oder zusammengefasste Informationen. Diese Bedürfnisse ändern sich unterschiedlich schnell.

Wie funktioniert es?

In Room definierst du eine Entity mit @Entity. Der Tabellenname kann aus dem Klassennamen abgeleitet oder explizit gesetzt werden. Jede Property wird standardmäßig zu einer Spalte. Mit @PrimaryKey legst du fest, wie ein Datensatz eindeutig erkannt wird. Das kann eine lokale automatisch erzeugte ID sein, eine vom Server gelieferte ID oder ein fachlicher Schlüssel, etwa eine Artikelnummer.

Bei Columns solltest du bewusst entscheiden, welche Daten wirklich in die Tabelle gehören. Ein createdAt-Zeitstempel, ein Serverstatus oder ein Synchronisationsflag kann sinnvoll sein, wenn die App offline arbeiten soll. Ein bereits formatierter Anzeigename, eine Farbe für den Screen oder ein temporärer Auswahlzustand gehört meist nicht in die Entity. Solche Werte entstehen besser in einem Domain-Modell oder UI-State.

Eine typische Struktur sieht so aus: Die Entity bildet die Tabelle ab. Ein DAO lädt oder schreibt Entities. Ein Repository entscheidet, welche Datenquelle verwendet wird, und wandelt Entities bei Bedarf in fachliche Modelle um. Die Compose-UI bekommt dann einen UI-State, der stabil zur Oberfläche passt. So bleibt die Datenbank langlebig, während du Screens umbauen kannst, ohne bei jeder UI-Anpassung deine Tabellenstruktur zu ändern.

Primärschlüssel sind dabei mehr als ein technisches Detail. Sie bestimmen, ob ein Insert einen neuen Datensatz erzeugt, ob ein Update den richtigen Datensatz trifft und ob Synchronisation zuverlässig funktioniert. Wenn du Daten vom Server spiegelst, ist eine stabile Server-ID oft besser als eine zufällige lokale ID. Wenn Datensätze nur lokal existieren, kann eine automatisch erzeugte ID passen. Wichtig ist, dass du die Identität des Datensatzes nicht mit seiner aktuellen Darstellung verwechselst.

In der Praxis

Angenommen, du baust eine Notiz-App mit Offline-Unterstützung. Die lokale Datenbank soll Notizen speichern, auch wenn kein Netzwerk verfügbar ist. Die Entity könnte so aussehen:

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "notes")
data class NoteEntity(
    @PrimaryKey
    @ColumnInfo(name = "id")
    val id: String,

    @ColumnInfo(name = "title")
    val title: String,

    @ColumnInfo(name = "body")
    val body: String,

    @ColumnInfo(name = "updated_at")
    val updatedAt: Long,

    @ColumnInfo(name = "pending_sync")
    val pendingSync: Boolean
)

Diese Klasse beschreibt eine Tabelle notes. Jede Notiz hat einen stabilen Primärschlüssel id, Textspalten für Titel und Inhalt, einen Zeitstempel und ein Flag für die Synchronisation. Das Flag ist ein Datenaspekt, kein UI-Detail: Es sagt aus, ob der lokale Datensatz noch mit einer entfernten Quelle abgeglichen werden muss.

Für die Anzeige würdest du daraus nicht zwingend direkt die Entity verwenden. Ein UI-Modell könnte anders aussehen:

data class NoteListItemUiState(
    val id: String,
    val headline: String,
    val preview: String,
    val syncLabel: String
)

Hier steht syncLabel, weil der Screen vielleicht “Wird synchronisiert” oder “Gespeichert” anzeigen soll. Dieser Text gehört nicht in die Datenbank. Er hängt von Sprache, Design und Produktentscheidung ab. Die Entity speichert den Zustand, die UI entscheidet über die Darstellung.

Eine gute Entscheidungsregel lautet: Speichere in Entities das, was du nach einem App-Neustart, bei Offline-Nutzung oder für Datenabgleich wieder exakt brauchst. Speichere nicht das, was nur aus Bequemlichkeit für einen einzelnen Screen berechnet wurde. Wenn du eine Property nur ergänzt, weil ein bestimmtes Composable sie gerade braucht, ist das ein Warnsignal.

Eine häufige Stolperfalle ist die Vermischung von API-Modellen, Entities und UI-Modellen. Ein JSON-Feld aus dem Backend wird dann direkt zur Room-Spalte und gleichzeitig im Screen verwendet. Das spart anfangs Mapping-Code, koppelt aber drei Bereiche: Serververtrag, lokale Speicherung und UI. Ändert sich später der Servername einer Property oder braucht die UI eine andere Darstellung, musst du an zu vielen Stellen umbauen. Besonders bei Offline-First-Apps wird das riskant, weil lokale Datenmigrationen sauber geplant werden müssen.

Eine zweite Stolperfalle betrifft Primärschlüssel. Wenn du autoGenerate = true verwendest, obwohl die eigentliche Identität vom Server kommt, können doppelte Datensätze entstehen. Beispiel: Du speicherst denselben Artikel nach zwei Netzwerkabrufen zweimal lokal, weil die lokale ID neu erzeugt wird. In solchen Fällen sollte die Server-ID häufig der Primärschlüssel sein oder zumindest über einen eindeutigen Index abgesichert werden.

Prüfen kannst du dein Verständnis sehr praktisch. Schreibe einen kleinen DAO-Test, der eine Entity einfügt, erneut lädt und ein Update über den Primärschlüssel ausführt. Kontrolliere im Debugger, ob wirklich derselbe Datensatz geändert wird. Im Code-Review kannst du gezielt fragen: Gehört jede Spalte dauerhaft in die Datenbank? Ist der Primärschlüssel fachlich stabil? Wird die Entity direkt an Compose weitergereicht, obwohl ein UI-State sinnvoller wäre? Solche Fragen finden viele Architekturprobleme früh, bevor sie als schwer änderbare Datenbankstruktur in deiner App landen.

Fazit

Entities geben deiner Android-App ein klares Fundament für persistierte Daten: Tabellen, Columns und Primary Keys werden explizit modelliert, während UI-Details außerhalb der Datenbank bleiben. Übe das an einem kleinen Room-Beispiel, schreibe mindestens einen DAO-Test und prüfe im Debugger, ob Insert, Update und Query so arbeiten, wie du es erwartest. Wenn du danach in einem Code-Review erklären kannst, warum eine Property in der Entity steht und nicht im UI-State, hast du den zentralen Punkt verstanden.

Quellen (5)
Redaktion

Geschrieben von

Redaktion

Das Redaktionsteam recherchiert und schreibt Artikel zu aktuellen Themen rund um Tech, Lifestyle und Ratgeber.