uorm 0.8.0

Rust 下的轻量级 ORM 框架,借鉴了 Java MyBatis 的设计理念,强调 SQL 与业务逻辑分离。它结合 Rust 的类型系统与宏机制,支持编写原生 SQL 并自动映射结果,兼容 async/await,兼顾性能与可控性。
Documentation
use async_trait::async_trait;
use mysql_async::prelude::Queryable;
use mysql_async::{Conn, Row as MyRow};
use std::collections::HashMap;

use crate::Result;
use crate::error::DbError;
use crate::udbc::connection::Connection;
use crate::udbc::mysql::value_codec::{from_mysql_value, to_mysql_value};
use crate::udbc::value::Value;

pub struct MysqlConnection {
    conn: Conn,
}

impl MysqlConnection {
    pub fn new(conn: Conn) -> Self {
        Self { conn }
    }

    // Optimize: consume row to avoid cloning values, use columns() to avoid intermediate Vec allocation
    fn map_row(row: MyRow) -> HashMap<String, Value> {
        // Access column metadata via Arc (cheap)
        let columns = row.columns();
        // Consume row to get values (moves ownership, efficient)
        let values = row.unwrap();

        let mut out_row = HashMap::with_capacity(values.len());
        // Zip values with columns. We rely on the driver ensuring lengths match.
        for (v, col) in values.into_iter().zip(columns.iter()) {
            out_row.insert(col.name_str().to_string(), from_mysql_value(v));
        }
        out_row
    }
}

#[async_trait]
impl Connection for MysqlConnection {
    async fn query(
        &mut self,
        sql: &str,
        args: &[(String, Value)],
    ) -> Result<Vec<HashMap<String, Value>>> {
        // 将参数映射为位置参数。注意:mysql_async 对 '?' 占位符只接受位置参数,因此忽略 args 中的 key。
        let params =
            mysql_async::Params::Positional(args.iter().map(|(_, v)| to_mysql_value(v)).collect());

        let rows: Vec<MyRow> = self
            .conn
            .exec(sql, params)
            .await
            .map_err(|e| DbError::DbError(e.to_string()))?;
        Ok(rows.into_iter().map(Self::map_row).collect())
    }

    async fn execute(&mut self, sql: &str, args: &[(String, Value)]) -> Result<u64> {
        let params =
            mysql_async::Params::Positional(args.iter().map(|(_, v)| to_mysql_value(v)).collect());

        self.conn
            .exec_drop(sql, params)
            .await
            .map_err(|e| DbError::DbError(e.to_string()))?;
        Ok(self.conn.affected_rows())
    }

    async fn last_insert_id(&mut self) -> Result<u64> {
        // unwrap_or(0) 用于处理未发生插入或无法获取 ID 的情况
        Ok(self.conn.last_insert_id().unwrap_or(0))
    }

    async fn begin(&mut self) -> Result<()> {
        self.conn
            .query_drop("BEGIN")
            .await
            .map_err(|e| DbError::DbError(e.to_string()))?;
        Ok(())
    }

    async fn commit(&mut self) -> Result<()> {
        self.conn
            .query_drop("COMMIT")
            .await
            .map_err(|e| DbError::DbError(e.to_string()))?;
        Ok(())
    }

    async fn rollback(&mut self) -> Result<()> {
        self.conn
            .query_drop("ROLLBACK")
            .await
            .map_err(|e| DbError::DbError(e.to_string()))?;
        Ok(())
    }
}