Rust Oracle数据库操作宏库oracle_procmacro的使用:简化Oracle数据库交互的proc-macro扩展

Rust Oracle数据库操作宏库oracle_procmacro的使用:简化Oracle数据库交互的proc-macro扩展

oracle_procmacro 是为 oracle crate 定义的过程宏扩展,它提供了以下宏:

  • RowValue 派生宏

安装

在项目目录中运行以下Cargo命令:

cargo add oracle_procmacro

或者在Cargo.toml中添加:

oracle_procmacro = "0.1.2"

使用示例

以下是一个使用 oracle_procmacrooracle crate 的完整示例:

use oracle::{Connection, RowValue};
use oracle_procmacro::RowValue;

// 定义一个结构体并派生RowValue
#[derive(Debug, RowValue)]
struct Employee {
    emp_id: i32,
    emp_name: String,
    salary: Option<f64>,
    hire_date: oracle::Date,
}

fn main() -> oracle::Result<()> {
    // 连接到Oracle数据库
    let conn = Connection::connect("username", "password", "//localhost:1521/ORCL")?;

    // 创建表
    conn.execute(
        "CREATE TABLE employees (
            emp_id NUMBER PRIMARY KEY,
            emp_name VARCHAR2(100),
            salary NUMBER(10,2),
            hire_date DATE
        )",
        &[],
    )?;

    // 插入数据
    let employees = vec![
        Employee {
            emp_id: 1,
            emp_name: "John Doe".to_string(),
            salary: Some(50000.0),
            hire_date: oracle::Date::from_string("2020-01-15", "YYYY-MM-DD")?,
        },
        Employee {
            emp_id: 2,
            emp_name: "Jane Smith".to_string(),
            salary: Some(60000.0),
            hire_date: oracle::Date::from_string("2019-05-20", "YYYY-MM-DD")?,
        },
    ];

    for emp in &employees {
        conn.execute(
            "INSERT INTO employees (emp_id, emp_name, salary, hire_date) VALUES (:1, :2, :3, :4)",
            &[&emp.emp_id, &emp.emp_name, &emp.salary, &emp.hire_date],
        )?;
    }

    // 查询数据并使用RowValue自动转换
    let stmt = conn.prepare("SELECT emp_id, emp_name, salary, hire_date FROM employees")?;
    let rows = stmt.query(&[])?;

    for row_result in rows {
        let row = row_result?;
        // 自动将行转换为Employee结构体
        let emp: Employee = row.to_struct()?;
        println!("{:?}", emp);
    }

    Ok(())
}

完整示例代码

// 引入必要的库和宏
use oracle::{Connection, RowValue};
use oracle_procmacro::RowValue;

// 定义员工结构体并派生RowValue trait
#[derive(Debug, RowValue)]
struct Employee {
    emp_id: i32,           // 员工ID
    emp_name: String,      // 员工姓名
    salary: Option<f64>,   // 工资(可能为NULL)
    hire_date: oracle::Date, // 雇佣日期
}

// 主函数返回oracle crate的Result类型
fn main() -> oracle::Result<()> {
    // 1. 建立数据库连接
    let conn = Connection::connect(
        "your_username", 
        "your_password", 
        "//localhost:1521/ORCL"
    )?;

    // 2. 创建员工表
    conn.execute(
        r#"CREATE TABLE employees (
            emp_id NUMBER PRIMARY KEY,
            emp_name VARCHAR2(100) NOT NULL,
            salary NUMBER(10,2),
            hire_date DATE NOT NULL
        )"#, 
        &[]
    )?;
    println!("表创建成功");

    // 3. 准备员工数据
    let employees = vec![
        Employee {
            emp_id: 101,
            emp_name: "张三".to_string(),
            salary: Some(8500.50),
            hire_date: oracle::Date::from_string("2021-03-15", "YYYY-MM-DD")?,
        },
        Employee {
            emp_id: 102,
            emp_name: "李四".to_string(),
            salary: None,  // 表示NULL值
            hire_date: oracle::Date::from_string("2020-11-22", "YYYY-MM-DD")?,
        },
    ];

    // 4. 批量插入数据
    for emp in &employees {
        conn.execute(
            r#"INSERT INTO employees 
               (emp_id, emp_name, salary, hire_date) 
               VALUES (:1, :2, :3, :4)"#,
            &[&emp.emp_id, &emp.emp_name, &emp.salary, &emp.hire_date],
        )?;
    }
    println!("数据插入成功");

    // 5. 查询数据并使用RowValue自动转换
    println!("\n查询结果:");
    let stmt = conn.prepare(
        "SELECT emp_id, emp_name, salary, hire_date FROM employees ORDER BY emp_id"
    )?;
    let rows = stmt.query(&[])?;

    // 6. 遍历结果并转换为Employee结构体
    for row_result in rows {
        let row = row_result?;
        let emp: Employee = row.to_struct()?;  // 自动转换
        println!("{:#?}", emp);
    }

    // 7. 清理表(演示用)
    conn.execute("DROP TABLE employees PURGE", &[])?;
    println!("\n表已删除");

    Ok(())
}

功能说明

  1. RowValue 派生宏允许将查询结果自动转换为Rust结构体
  2. 支持Oracle的基本数据类型到Rust类型的转换
  3. 处理NULL值通过Option类型
  4. 支持日期时间等特殊类型的转换

文档

更多详细用法请参考官方文档。

许可证

该库采用UPL-1.0或Apache-2.0许可证。


1 回复

Rust Oracle数据库操作宏库oracle_procmacro使用指南

简介

oracle_procmacro是一个简化Rust与Oracle数据库交互的过程宏库,它通过提供声明式宏来减少样板代码,使Oracle数据库操作更加简洁直观。

主要特性

  • 自动处理连接池管理
  • 简化SQL查询执行
  • 类型安全的参数绑定
  • 自动结果集映射
  • 支持事务操作

安装

在Cargo.toml中添加依赖:

[dependencies]
oracle_procmacro = "0.1"
tokio = { version = "1.0", features = ["full"] }

基本使用方法

1. 建立连接

use oracle_procmacro::oracle_connect;

#[oracle_connect(
    url = "oracle://username:password@localhost:1521/ORCLCDB",
    pool_size = 5
)]
struct DbConnection;

2. 执行查询

use oracle_procmacro::oracle_query;

#[oracle_query(
    sql = "SELECT id, name, salary FROM employees WHERE department = :dept",
    fetch = "all"
)]
struct GetEmployeesByDept {
    dept: String,
}

3. 使用查询

#[tokio::main]
async fn main() {
    let conn = DbConnection::new().await;
    let employees = GetEmployeesByDept { dept: "IT".to_string() }
        .execute(&conn)
        .await
        .unwrap();
    
    for emp in employees {
        println!("ID: {}, Name: {}, Salary: {}", emp.id, emp.name, emp.salary);
    }
}

高级用法

事务支持

use oracle_procmacro::oracle_transaction;

#[oracle_transaction]
async fn update_employee_salary(conn: &DbConnection, emp_id: i32, new_salary: f64) -> Result<(), String> {
    #[oracle_query(sql = "UPDATE employees SET salary = :salary WHERE id = :id")]
    struct UpdateSalary {
        id: i32,
        salary: f64,
    }
    
    UpdateSalary { id: emp_id, salary: new_salary }
        .execute(conn)
        .await
        .map_err(|e| e.to_string())?;
    
    Ok(())
}

批量操作

use oracle_procmacro::oracle_batch;

#[oracle_batch(
    sql = "INSERT INTO employees (id, name, department) VALUES (:id, :name, :dept)",
    batch_size = 100
)]
struct BatchInsertEmployee {
    id: i32,
    name: String,
    dept: String,
}

#[tokio::main]
async fn main() {
    let conn = DbConnection::new().await;
    let employees = vec![
        BatchInsertEmployee { id: 1, name: "Alice".to_string(), dept: "IT".to_string() },
        BatchInsertEmployee { id: 2, name: "Bob".to_string(), dept: "HR".to_string() },
        // ...更多记录
    ];
    
    BatchInsertEmployee::execute_batch(&conn, employees).await.unwrap();
}

自定义类型映射

use oracle_procmacro::oracle_type_mapping;

#[oracle_type_mapping]
mod custom_types {
    pub struct MyCustomType(String);
    
    impl From<oracle::SqlValue> for MyCustomType {
        fn from(value: oracle::SqlValue) -> Self {
            MyCustomType(value.get_string().unwrap())
        }
    }
    
    impl Into<oracle::SqlValue> for MyCustomType {
        fn into(self) -> oracle::SqlValue {
            oracle::SqlValue::String(self.0)
        }
    }
}

错误处理

所有操作都返回Result类型,可以方便地进行错误处理:

#[tokio::main]
async fn main() {
    let conn = DbConnection::new().await;
    let result = GetEmployeesByDept { dept: "IT".to_string() }
        .execute(&conn)
        .await;
    
    match result {
        Ok(employees) => {
            // 处理成功结果
        },
        Err(e) => {
            eprintln!("查询失败: {}", e);
            // 错误处理逻辑
        }
    }
}

性能提示

  1. 连接池大小应根据应用负载调整
  2. 批量操作比单条操作效率更高
  3. 复杂查询考虑使用存储过程

完整示例

以下是一个完整的Rust程序示例,展示了如何使用oracle_procmacro库进行Oracle数据库操作:

use oracle_procmacro::{oracle_connect, oracle_query, oracle_batch, oracle_transaction};

// 1. 建立数据库连接
#[oracle_connect(
    url = "oracle://username:password@localhost:1521/ORCLCDB",
    pool_size = 5
)]
struct DbConnection;

// 2. 定义查询结构体
#[oracle_query(
    sql = "SELECT id, name, salary FROM employees WHERE department = :dept",
    fetch = "all"
)]
struct GetEmployeesByDept {
    dept: String,
}

// 3. 定义批量插入结构体
#[oracle_batch(
    sql = "INSERT INTO employees (id, name, department) VALUES (:id, :name, :dept)",
    batch_size = 100
)]
struct BatchInsertEmployee {
    id: i32,
    name: String,
    dept: String,
}

// 4. 定义事务操作函数
#[oracle_transaction]
async fn transfer_employee(
    conn: &DbConnection,
    emp_id: i32,
    new_dept: String,
    new_salary: f64
) -> Result<(), String> {
    #[oracle_query(sql = "UPDATE employees SET department = :dept WHERE id = :id")]
    struct UpdateDept {
        id: i32,
        dept: String,
    }
    
    #[oracle_query(sql = "UPDATE employees SET salary = :salary WHERE id = :id")]
    struct UpdateSalary {
        id: i32,
        salary: f64,
    }
    
    UpdateDept { id: emp_id, dept: new_dept }
        .execute(conn)
        .await
        .map_err(|e| e.to_string())?;
    
    UpdateSalary { id: emp_id, salary: new_salary }
        .execute(conn)
        .await
        .map_err(|e| e.to_string())?;
    
    Ok(())
}

#[tokio::main]
async fn main() {
    // 初始化连接
    let conn = DbConnection::new().await;
    
    // 示例1: 查询操作
    let it_employees = GetEmployeesByDept { dept: "IT".to_string() }
        .execute(&conn)
        .await
        .unwrap();
    
    println!("IT部门员工:");
    for emp in it_employees {
        println!("ID: {}, Name: {}, Salary: {}", emp.id, emp.name, emp.salary);
    }
    
    // 示例2: 批量插入
    let new_employees = vec![
        BatchInsertEmployee { id: 101, name: "Charlie".to_string(), dept: "Finance".to_string() },
        BatchInsertEmployee { id: 102, name: "David".to_string(), dept: "Marketing".to_string() },
        BatchInsertEmployee { id: 103, name: "Eve".to_string(), dept: "Engineering".to_string() },
    ];
    
    BatchInsertEmployee::execute_batch(&conn, new_employees).await.unwrap();
    println!("成功批量插入员工记录");
    
    // 示例3: 事务操作
    match transfer_employee(&conn, 101, "Engineering".to_string(), 85000.0).await {
        Ok(_) => println!("员工部门转移成功"),
        Err(e) => eprintln!("事务失败: {}", e),
    }
}

这个完整示例展示了:

  1. 数据库连接池的建立
  2. 基本查询操作
  3. 批量数据插入
  4. 事务处理
  5. 错误处理

您可以根据实际需求调整SQL语句和参数,这个宏库可以大大简化Oracle数据库操作代码。

回到顶部