diff --git a/src/components/slides/databases/index.astro b/src/components/slides/databases/index.astro new file mode 100644 index 0000000..b89f8b2 --- /dev/null +++ b/src/components/slides/databases/index.astro @@ -0,0 +1,15 @@ +--- +import Title from './title.astro'; +import Intro from './intro.astro'; +import Setup from './setup.astro'; +import Sql from './sql.astro'; +import Usage from './usage.astro'; +--- + +
\ No newline at end of file diff --git a/src/components/slides/databases/intro.astro b/src/components/slides/databases/intro.astro new file mode 100644 index 0000000..5f0b981 --- /dev/null +++ b/src/components/slides/databases/intro.astro @@ -0,0 +1,69 @@ +Wenn wir (Nutzer)-Daten speichern wollen, legen wir in großen Projekten keine Dateien im Datei-System ab. Gründe hierfür sind:
+Hierfür gibt es Datenbanken. Sie sorgen dafür das die Daten optimiert und sicherer gespeichert werden.
+Je nach Anwendungsfall gibt es auch weiter optimierte Datenbanken, die in einer Niche besonders gut sind.
+In einer Relationelen Datenbank werden Daten strukturiert abgelegt. Hierfür muss die "Form" der zu speichernden Daten vordefiniert sein.
+Daten in Relationelen Datenbanken werden in Tabellen gespeichert. Tabellen können miteinander Verknüpft werden (sie haben eine relation zueinander).
+Auch genannt "NoSQL"-Datenbanken, verfolgt den genau Gegenteiligen Ansatz einer relationelen Datenbank. Daten werden in einer strukturierten Art und Weise (z.B. JSON) abgelegt.
+Das Ziel der abgelegten Daten ist hier aber dabei nicht relationel aufgebaut, alle notwendigen Informationen sollten eher in einem "Dokument" gespeichert sein.
+Diese Art von Datenbanken sind spezieller Natur. Daten werden primär im Arbeitsspeicher abgelegt.
+Die abgelegten Daten sind normalerweise in einer einfachen Datenstruktur (z.B. Strings) gespeichert.
+In unserem kurzen Kurs versuchen wir es einfach und standardisiert zu halten.
+Hierzu verwenden wir das vereinfachte Datenbank-System "SQLite".
+SQLite ist eine relatione Datenbank. Die Inhalte werden dabei innerhalb einer Datenbank-Datei lokal gespeichert, eine eigene Datenbank-Applikation ist hierbei nicht notwendig, alle Transaktionen werden durch eine eigene Bibliothek gemanaged.
+Vorab anzumerken: Um die Nachfolgenden Code-Snippets auszuführen, ist NodeJS in der Mindestversion 22.5.0 notwendig. Seit dieser Version wurde die libsql in die JavaScript Engine miteingebaut.
+Hiermit ist es möglich eine SQLite Datenbank zu bedienen, ohne auf externe Pakete zurückgreifen zu müssen.
+Wir bleiben weiterhin mit fastify unterwegs. Hier verwenden wir das Plugin-System um die Datenbank im Web-Server zu nutzen.
+Beginnen wir nun auch damit. Im existierenden Projekt legen wir eine db.ts im Plugin-Ordner an.
+Initialer Aufbau:
+
+ import fp from 'fastify-plugin';
+ import { DatabaseSync } from 'node:sqlite';
+ import path from 'node:path';
+
+ export default fp(async (fastify, opts) => {
+ // ToDo
+ })
+
+ declare module 'fastify' {
+ export interface FastifyInstance {
+ db: DatabaseSync,
+ }
+ }
+
+ Plugin-Implementierung
+
+ export default fp(async (fastify, opts) => {
+ const p = path.join(process.cwd(), 'db', 'store.db');
+ const db = new DatabaseSync(p);
+
+ db.exec(`
+ CREATE TABLE IF NOT EXISTS test(
+ key INTEGER PRIMARY KEY,
+ value TEXT
+ ) STRICT
+ `);
+
+ fastify.decorate('db', db);
+ });
+
+ Mit diesen Zeilen haben wir nun in allen Fastify-Umgebungen auf die Datenbank-Konstante zugreifen.
+Um im weiteren mit der Datenbank zu arbeiten, müssen wir kurz auf ein paar Basics mit SQL (Structured Query Language) eingehen.
+Da wir hier mit SQLite arbeiten, müssen wir uns nicht um User-Management und Rechte kümmern, das haben wir hier nicht.
+Um mit Tabellen und deren Daten zu arbeiten, müssen wir zuerst eine oder mehrere Tabellen anlegen, in der wir die Daten speichern wollen.
+
+ CREATE TABLE IF NOT EXISTS tasks (
+ id INTEGER PRIMARY KEY,
+ title TEXT NOT NULL,
+ completed INTEGER DEFAULT 0,
+ due_date TEXT
+ ) STRICT
+
+ SQLite unterstützt nur 5 Daten-Typen:
+
+ CREATE TABLE IF NOT EXISTS tasks
+
+ Erstelle die Tabelle tasks, falls diese noch nicht angelegt ist
+
+ "id" - "INTEGER" "PRIMARY KEY"
+
+ "name" - "Datentype" "Primärschlüssel"
+
In Datenbanken ist ein PRIMARY KEY notwendig um Einträge in der Tabelle eindeutig zu identifizieren.
+Die Anweisung PRIMARY KEY zeigt an, dass diese Spalte eindeutig (UNIQUE) ist und das Datenbanksystem die Zahl automatisch hochzählen soll für jeden Eintrag.
+Die Anweisung NOT NULL zeigt der Datenbank an, dass dieses Feld (Spalte) nicht leer sein darf. Leere Werte werden in relationalen Datenbanken als NULL definiert.
+Die Tabelle ist nun angelegt, wir können also jetzt Daten darin speichern:
+
+ INSERT INTO tasks (title) VALUES
+ ("Für Web-Engineering lernen");
+
+ Mit diesem Eintrag speichern wie eine neue Aufgabe in die neue Tabelle. Defaults werden automatisch gesetzt und der erste Eintrag erhält die id 1.
+Die wahre Stärke von relationalen Datenbanken besteht in der effizienten Abfrage von Daten. Fragen wir zum Anfang alle in der befindlichen Tabelle abgelegten Aufgaben ab:
+
+ SELECT * FROM tasks;
+
+ Wenn wir nur einzelne Spalten abfragen wollen, können wir die Spalten-Namen anstatt der Wildcard * verwenden. Die Spalten-Liste muss Komma-Separiert sein.
+
+ SELECT title, completed FROM tasks;
+
+ Wenn wir bestimmte Einträge filtern wollen können wir dies auch erreichen:
+
+ SELECT * FROM tasks WHERE completed = 0;
+ SELECT * FROM tasks WHERE completed <> 1;
+
+ Wir können unsere Aufgaben nun abhaken:
+
+ UPDATE tasks SET completed = 1 WHERE id = 1;
+
+ Zu guter letzt ist es auch möglich, Einträge aus einer Datenbank zu löschen
+
+ DELETE FROM tasks WHERE completed = 1;
+
+ Eine der Stärken / Features von relationelen Datenbanken ist die Möglichkeit, Tabellen miteinander zu verknüpfen. So lassen sich komplexe Datenstrukturen stark vereinfachen. Besonders wenn es 1:n oder m:n Relationen gibt.
+Wir können also Einträge einer Tabelle als Referenz in einer zweiten Tabelle verwenden.
+Legen wir zuerst eine neue Tabelle an, die Nutzer abbilden soll:
+
+ CREATE TABLE IF NOT EXISTS user (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL
+ ) STRICT
+
+ Im zweiten Schritt müssen wir die existierende Tabelle um eine Spalte erweitern, die die Referenz zum Nutzer (genauer gesagt dessen id) speichern kann.
+
+ ALTER TABLE tasks ADD COLUMN
+ user_id INTEGER REFERENCES user(id);
+
+ Lasst uns einen kleinen Finanz-Tracker bauen:
+Accounts "Managen"
+
+ CREATE TABLE accounts (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL UNIQUE,
+ type TEXT NOT NULL
+ ) STRICT
+
+ Kategorien bereitstellen
+
+ CREATE TABLE categories (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL UNIQUE
+ ) STRICT
+
+ Transaktionspartner
+
+ CREATE TABLE transactors (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL
+ ) STRICT
+
+ Zu guter letzt, die Transaktionstabelle selbst:
+
+ CREATE TABLE transactions (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ date TEXT NOT NULL,
+ cat_id INTEGER REFERENCES categories(id),
+ acc_id INTEGER NOT NULL REFERENCES accounts(id),
+ tra_id INTEGER NOT NULL REFERENCES transactors(id),
+ value INTEGER
+ ) STRICT
+
+