Einleitung: Warum Rust und Datenbanken die ideale Kombination sind
Rust ist eine moderne systemnahe Programmiersprache, die die Leistung von C/C++ mit der vom Compiler garantierten Speichersicherheit kombiniert. Bei der Entwicklung von Backend-Diensten, Webanwendungen und Microservices entsteht fast immer die Notwendigkeit der Interaktion mit Datenbanken. In diesem Artikel werden wir im Detail besprechen, wie man mit den beiden beliebtesten Datenbanktypen arbeitet: der relationalen PostgreSQL und der dokumentenorientierten MongoDB.
Wir werden die Verbindung, die Durchführung von CRUD-Operationen, Transaktionen und Aggregationen anhand praktischer Beispiele behandeln. Alle Codebeispiele sind in Rust unter Verwendung der asynchronen Laufzeitumgebung tokio geschrieben, was maximale Leistung bietet.
1. Projekteinrichtung und Installation der Abhängigkeiten
Zuerst erstellen wir ein neues Projekt und fügen die notwendigen Crates hinzu. Öffnen Sie das Terminal und führen Sie Folgendes aus:
cargo new rust_db_examplecd rust_db_exampleFügen wir die Abhängigkeiten in der Datei Cargo.toml hinzu:
[dependencies]tokio = { version = "1", features = ["full"] }sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }mongodb = "2.8"serde = { version = "1.0", features = ["derive"] }serde_json = "1.0"chrono = { version = "0.4", features = ["serde"] }Die Projektstruktur ist nun bereit. Wir werden sqlx für PostgreSQL verwenden – einen asynchronen Treiber mit Unterstützung für kompilierbare Abfragen – und den offiziellen Treiber mongodb für die Arbeit mit MongoDB.
1.1. Verbindung zu PostgreSQL über sqlx
Schreiben wir ein Modul für die Arbeit mit PostgreSQL. Erstellen wir die Datei src/postgres.rs:
use sqlx::postgres::PgPoolOptions;use sqlx::PgPool;use anyhow::Result;
pub async fn connect_to_postgres() -> Result<PgPool> { let database_url = "postgres://user:password@localhost:5432/mydb"; let pool = PgPoolOptions::new() .max_connections(5) .connect(database_url) .await?; println!("✅ Mit PostgreSQL verbunden"); Ok(pool)}
// Definieren des Benutzermodells#[derive(Debug, sqlx::FromRow)]pub struct User { pub id: i32, pub name: String, pub email: String, pub created_at: chrono::NaiveDateTime,}1.2. Verbindung zu MongoDB
Erstellen wir die Datei src/mongodb.rs:
use mongodb::{Client, Collection, Database};use mongodb::bson::{doc, Document};use anyhow::Result;
pub async fn connect_to_mongodb() -> Result<(Database, Collection<Document>)> { let client = Client::with_uri_str("mongodb://localhost:27017")?; let database = client.database("mydb"); let collection = database.collection::<Document>("users"); println!("✅ Mit MongoDB verbunden"); Ok((database, collection))}2. CRUD-Operationen in PostgreSQL
Jetzt implementieren wir die grundlegenden Operationen: Erstellen, Lesen, Aktualisieren und Löschen von Datensätzen.
2.1. Daten einfügen (CREATE)
pub async fn create_user(pool: &PgPool, name: &str, email: &str) -> Result<User> { let user = sqlx::query_as::<_, User>( "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email, created_at" ) .bind(name) .bind(email) .fetch_one(pool) .await?; println!("Benutzer erstellt: {:?}", user); Ok(user)}2.2. Daten lesen (READ)
pub async fn get_user_by_id(pool: &PgPool, user_id: i32) -> Result<Option<User>> { let user = sqlx::query_as::<_, User>( "SELECT id, name, email, created_at FROM users WHERE id = $1" ) .bind(user_id) .fetch_optional(pool) .await?; match user { Some(u) => println!("Benutzer gefunden: {:?}", u), None => println!("Benutzer mit id={} nicht gefunden"