Categorías: Programación

Interactuar con bases de datos en Rust usando Diesel vs SQLx

En este tutorial, exploraremos dos bibliotecas utilizadas al interactuar con bases de datos relacionales en Rust: Diesel y SQLx.

Este artículo utilizará una base de datos de clase simple con estudiantes para demostrar cada enfoque. Realizaremos operaciones CRUD utilizando Diesel ORM y SQLx.

Lo que cubriremos:

Para completar este tutorial, necesitará un conocimiento práctico de Rust, así como la capacidad de acceder y usar Rust, el sistema de compilación y el administrador de paquetes de Rust’s Cargo, y una instancia de un servidor MySQL. .

¿Qué es Diésel?

Diesel es un ORM que soporta PostgreSQL, MySQL, SQLite. ORM significa mapeo relacional de objetos. Los ORM ayudan a los programadores orientados a objetos a abstraer los detalles de las bases de datos relacionales.

Los ORM vienen con generadores de consultas, por lo que no tiene que preocuparse por escribir consultas SQL sin formato. Mediante el uso de ORM, puede comunicarse con bases de datos relacionales como si estuvieran orientadas a objetos.

Para los desarrolladores menos experimentados, el uso de ORM puede ser preferible porque los ORM crean consultas SQL optimizadas. Los ORM también lo hacen menos propenso a los ataques de inyección SQL.

¿Qué es SQLx?

A diferencia de Diesel, SQLx no es un ORM. SQLx es una caja asíncrona de Rust SQL que ofrece verificaciones de consultas SQL en tiempo de compilación. Es independiente tanto de la base de datos como del tiempo de ejecución.

SQLx admite la agrupación de conexiones, el desarrollo multiplataforma, la agrupación anidada, las notificaciones asincrónicas, la seguridad de la capa de transporte y otras características excelentes. Al usar SQLx, debe crear las consultas SQL y las migraciones usted mismo.

Habiendo arañado la superficie, exploremos cómo interactuar con bases de datos relacionales con Diesel y SQLx.

Primeros pasos con Diesel ORM

Los siguientes pasos muestran cómo configurar un proyecto de Rust con Cargo que usa Diesel ORM.

Inicialización de un nuevo proyecto con Diesel ORM

El primer paso es inicializar el proyecto ejecutando el siguiente comando:

cargo new -- lib classroom_diesel
cd classroom_diesel

En el código anterior, configuramos el proyecto y lo llamamos class_diesel. El nuevo directorio del proyecto debería verse así:

./
│
├── src/
│   └── lib.rs
│
├── .gitignore
└── Cargo.toml

También necesitamos actualizar el archivo Cargo.toml con las dependencias que necesitamos en el proyecto, así:

[dependencies]
diesel = { version = "1.4.4", features = ["mysql"] }
dotenv = "0.15.0"

La dependencia dotenv nos ayuda a administrar las variables de entorno en el proyecto.

Instalación de la CLI diésel

Diesel usa una herramienta CLI separada. Es un binario independiente; no necesitamos agregarlo como una dependencia en el archivo cargo.toml. Simplemente instálelo con el siguiente comando:

cargo install diesel_cli

Configurando nuestro entorno Diesel

Necesitamos definir una variable DATABASE_URL en nuestro entorno. Así es como Diesel sabe a qué base de datos MySQL conectarse:

Más artículos interesantes de LogRocket:

echo DATABASE_URL=mysql://<username>:<password>@localhost/<database>  > .env

Cambie la cadena de conexión para que coincida con las credenciales de su base de datos local.

El directorio de su proyecto ahora se verá así:

./
│
├── src/
│   └── lib.rs
│
├── .env
├── .gitignore
└── Cargo.toml

Ahora ejecute el siguiente comando:

diesel setup

Este comando nos ayudará a configurar la base de datos y crear un directorio de migraciones vacío para administrar el esquema de la base de datos.

Implementación de migraciones Diesel

Las migraciones ayudan al ORM a realizar un seguimiento de las operaciones de la base de datos, como agregar un campo o eliminar una tabla. Puede pensar en ellos como un sistema de control de versiones para su base de datos.

Comencemos creando migraciones para la aplicación de clase usando Diesel CLI. Idealmente, deberíamos tener una tabla que contenga datos sobre los estudiantes de la clase.
Necesitamos crear archivos de migración vacíos y luego llenarlos con SQL para crear una tabla.

diesel migration generate create_students

Su árbol de archivos se verá así:

./
│
├── migrations/
│   │
│   ├── 2022-07-04-062521_create_students/
│   │   ├── down.sql
│   │   └── up.sql
│   │
│   └── .gitkeep
│
├── src/
│   └── lib.rs
│
├── .env
├── .gitignore
├── Cargo.toml
└── diesel.toml

El archivo up.sql se usa para crear una migración, mientras que el archivo down.sql se usa para revertirla.

Actualice el archivo up.sql con el SQL para la migración:

sql
CREATE TABLE students (
  id INTEGER AUTO_INCREMENT PRIMARY KEY,
  firstname VARCHAR(255) NOT NULL,
  lastname TEXT NOT NULL,
  age INTEGER NOT NULL
);

Edite el archivo down.sql con SQL que puede revertir la migración:

sql
DROP TABLE students;

Después de crear las migraciones ascendentes y descendentes, debemos ejecutar el SQL en la base de datos:

diesel migration run

Podemos comenzar a escribir Rust para realizar consultas en la tabla.

Creando filas con Diesel ORM

Escribamos algo de código para establecer una conexión con el servidor MySQL utilizando la cadena de conexión definida en el archivo .env.

#[macro_use]
extern crate diesel;
extern crate dotenv;

pub mod models;
pub mod schema;

use diesel::prelude::*;
use dotenv::dotenv;
use std::env;

pub fn create_connection() -> MysqlConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    MysqlConnection::establish(&database_url)
        .unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}

A continuación, necesitamos escribir un modelo para la tabla de estudiantes. Los modelos son donde tiene lugar el mapeo relacional de objetos. La plantilla generará el código necesario para convertir una o más filas de la tabla de Estudiantes a la estructura de Estudiantes en Rust.

cd ./src
touch model.rs

En el nuevo archivo model.rs que acabamos de crear, agregue lo siguiente:

use super::schema::students;

#[derive(Queryable)]
pub struct Student {
    pub id: i32,
    pub firstname: String,
    pub lastname: String,
    pub age: i32,
}

#[derive(Insertable)]
#[table_name = "students"]
pub struct NewStudent<'a> {
    pub firstname: &'a str,
    pub lastname: &'a str,
    pub age: &'a i32,
}

Con este patrón, la información de la tabla Students se asignará a la estructura Student correspondiente en Rust. La carpeta src ahora debería verse así:

src/
├── lib.rs
├── models.rs
└── schema.rs

Ahora podemos escribir un script para agregar un estudiante:

cd src
mkdir bin
cd bin
touch create_students.rs

En el archivo create_students.rs, podemos invocar los patrones y funciones escritos anteriormente para crear un nuevo estudiante:

extern crate classroom_diesel;
extern crate diesel;

use self::classroom_diesel::*;
fn main() {
    let connection = create_connection();
    let firstname = "John";
    let lastname = "Doe";
    let age: i32 = 64;

    let student = create_post(&connection, firstname, lastname, &age);
    println!(
        "Saved student {} with id {}",
        student.firstname, student.id
    );
}

La estructura del proyecto ahora se verá así:

./
│
├── migrations/
│   │
│   ├── 2022-07-04-062521_create_students/
│   │   ├── down.sql
│   │   └── up.sql
│   │
│   └── .gitkeep
│
├── src/
│   │
│   ├── bin/
│   │   └── create_students.rs
│   │
│   ├── lib.rs
│   ├── models.rs
│   └── schema.rs
│
├── .env
├── .gitignore
├── Cargo.lock
├── Cargo.toml
└── diesel.toml

Ejecute el nuevo script con el siguiente comando:

cargo run --bin create_students

Como puede ver en la imagen a continuación, el nuevo archivo de estudiante de John se guardó con una ID de 1. Podemos usar esta ID para consultar las bases de datos de Rust, que veremos en la siguiente sección.

Consultando la base de datos de Rust con Diesel ORM

En la sección anterior, vimos cómo escribir en la base de datos de Rust usando Diesel ORM. También es esencial comprender cómo funciona el sondeo o la lectura.

Escribamos un script para consultar a un estudiante cuya identificación es 1. Comience creando un archivo query_students.rs:

cd bin
touch query_students.rs

A continuación, en el archivo query_students.rs que acabamos de crear, agregue lo siguiente:

extern crate classroom_diesel;
extern crate diesel;

use self::models::*;
use classroom_diesel::*;
use diesel::prelude::*;

fn main() {
    use self::schema::students::dsl::*;

    let connection = create_connection();
    let result = students
        .filter(id.eq(1))
        .load::<Student>(&connection)
        .expect("Error loading students");

    println!(
        "Student: {} {} {} years",
        result[0].firstname, result[0].lastname, result[0].age
    );
}

Ejecute el script:

cargo run --bin query_students

Como puede ver en la imagen a continuación, el resultado es una línea impresa que contiene el nombre, el apellido y la edad del registro del estudiante que consultamos en la base de datos:

Primeros pasos con SQLx

Ahora que sabemos cómo crear un proyecto que use Diesel ORM para interactuar con bases de datos en Rust, veamos cómo crear un proyecto que use SQLx en su lugar.

Inicializar un nuevo proyecto con SQLx

Comience ejecutando el siguiente comando:

cargo new classroom_sqlx --bin

A continuación, agregue las dependencias requeridas al archivo cargo.toml:

[dependencies]
sqlx = { version = "0.5", features = [  "runtime-async-std-native-tls", "mysql" ] }
async-std = { version = "1", features = [ "attributes" ] }

Eso es todo lo que necesita para la configuración. Sencillo, ¿verdad?

Para usar SQLx para interactuar con bases de datos en Rust, todo lo que tenemos que hacer es escribir consultas SQL y código Rust. En la sección Diesel ORM, creamos y leímos un archivo de estudiante; en esta sección escribiremos consultas para actualizar y eliminar un registro.

Use SQLx y Rust para actualizar o eliminar registros de la base de datos

Primero, necesitamos escribir código Rust para conectar SQLx al servidor MySQL:

//main.rs

use sqlx::mysql::MySqlPoolOptions;

#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = MySqlPoolOptions::new()
        .max_connections(7)
        .connect("mysql://root:@localhost/classroom_diesel")
        .await?;

    Ok(())
}

SQLx admite consultas SQL preparadas y no preparadas. Las declaraciones SQL preparadas se oponen a la inyección SQL.

Veamos cómo actualizar el nombre y apellido de un registro con una clave principal de 1:

use sqlx::mysql::MySqlPoolOptions;

#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = MySqlPoolOptions::new()
        .max_connections(5)
        .connect("mysql://root:@localhost/classroom_diesel")
        .await?;

    sqlx::query("UPDATE students SET firstname=?, lastname=? WHERE id=?")
        .bind("Richard")
        .bind("Roe")
        .bind(1)
        .execute(&pool)
        .await?;
    Ok(())
}

Ejecute el script con el siguiente comando:

cargo run

Eliminar el registro también sigue un patrón similar; la única diferencia es la consulta SQL:

use sqlx::mysql::MySqlPoolOptions;

#[async_std::main]
async fn main() -> Result<(), sqlx::Error> {
    let pool = MySqlPoolOptions::new()
        .max_connections(5)
        .connect("mysql://root:@localhost/classroom_diesel")
        .await?;

    sqlx::query("DELETE FROM students WHERE id=?")
        .bind(1)
        .execute(&pool)
        .await?;
    Ok(())
}

Ejecute el script con el siguiente comando:

cargo run

Ahora puede interactuar con bases de datos en Rust usando Diesel o SQLx.

Conclusión

Los ORM como Diesel son adecuados; lo ayudan a generar parte del SQL que necesita. La mayoría de las veces, solo necesita lo adecuado en sus aplicaciones.

Sin embargo, puede requerir más «magia», en otras palabras, su tiempo y esfuerzo, en aplicaciones más grandes para que los ORM funcionen correctamente y generen consultas SQL.

Si es necesario crear consultas más complejas con requisitos de alto volumen y baja latencia, puede ser mejor usar bibliotecas como SQLx para ejecutar consultas SQL sin formato.

LogRocket: visibilidad completa de las aplicaciones Rust de producción

La depuración de aplicaciones de Rust puede ser un desafío, especialmente cuando los usuarios encuentran problemas que son difíciles de reproducir. Si está interesado en monitorear y rastrear el rendimiento de sus aplicaciones Rust, detectar errores automáticamente y rastrear solicitudes de red lentas y tiempos de carga, pruebe LogRocket.

LogRocket es como un DVR para aplicaciones web y móviles, literalmente graba todo lo que sucede en su aplicación Rust. En lugar de adivinar por qué ocurren los problemas, puede agregar e informar el estado en el que se encontraba su aplicación cuando ocurrió un problema. LogRocket también supervisa el rendimiento de su aplicación, informando métricas como la carga de la CPU del cliente, el uso de la memoria del cliente y más.

Modernice la forma en que depura sus aplicaciones de Rust: comience a monitorear de forma gratuita.

Fuente

WP Dev JaGonzalez

Hijo, esposo y padre de un hermoso niño. Amante de los animales, la tecnología, informática y programación. Si tienes alguna duda, inquietud, comentario o deseas comunicarte directamente conmigo, puedes enviarme un correo electrónico a admin@jagonzalez.org

Compartir
Publicado por
WP Dev JaGonzalez

Entradas recientes

iPhone Hackeado: Qué Hacer para Proteger tu Dispositivo y Asegurar tu Seguridad

¿Has notado aplicaciones desconocidas o un drenaje inesperado de la batería? Estos podrían ser indicios…

2 meses hace

Cómo Restablecer un iPhone a su Estado de Fábrica

Saber cómo Restablecer un iPhone a su Estado de Fábrica es clave para solucionar problemas…

2 meses hace

Motorola planea lanzar al menos dos nuevos teléfonos Moto G en septiembre

Motorola ha confirmado el lanzamiento de Moto G84 5G y Moto G54 5G en India,…

1 año hace

El equipo de WizardLM afirma que un modelo de IA de terceros les robó el trabajo

Recuerde WizardCoder, ¿el codificador de IA que cubrimos recientemente aquí en Windows Report? Nos jactamos…

1 año hace

Las fallas del complemento Jupiter X Core amenazaron a 172.000 sitios web con apropiaciones de cuentas

Los investigadores han descubierto numerosos fallos de seguridad en el complemento WordPress Jupiter X Core…

1 año hace

Consola portátil Xbox: aquí tienes todo lo que necesitas saber al respecto

Para solucionar problemas del sistema de PC con Windows, necesitará una herramienta dedicada Fortect es…

1 año hace