rust-shentong操作指南

数据库连接

普通连接

有两种方式进行数据库的连接:

第一种:时用Connection::connect方法

let conn = Connection::connect(user, password, connect_string);

User为用户名;

password为密码;

connect_string为连接字符串格式为:host:port/dbname,比如:localhost:2003/osrdb。

需要引入Connection:use shentong::{Connection}

第二种用Connector::new创建

let conn = Connector::new(user,password,connect_string);

需要引入Connector:use shentong::{Connector}

注解

conn的status方法可以获得连接的连接的状态,比如:
assert_eq!(conn.status()?, ConnStatus::Normal);
conn.close()?;
assert_eq!(conn.status()?, ConnStatus::Closed);

连接池

// 创建一个连接池
let pool = PoolBuilder::new(username, password, connect_string)
    .max_connections(20)
.build()?;

// 获得一个连接.
let conn1 = pool.get()?;
let conn2 = pool.get()?;

// 将连接归还连接池.
conn1.close()?;
conn2.close()?;

安全连接

rust-shentong依赖aci驱动,aci驱动支持了ssl安全通信,且可以通过连接字符串设置,因此rust-shentong也可以在连接字符串设置ssl通信相关配置,多个参数之间用“;”间隔,比如:

let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb?ssl_wallet_enable=1;ssl_wallet_path=/opt/wallet/;ssl_wallet_pwd=szoscar55$")?;

详细可以设置的参数,可参考 《ACI (C/C++)程序员开发手册-->ACI编程概述-->参数配置文件-->参数说明》 中的参数及参数功能描述。

执行有结果的语句

query方式执行并获得结果

query方法会查询获得查询的所有结果,需要在执行后用result对象接收结果集,在循环中获取结果集中的每一行的数据。示例如下:

use shentong::{Connection, Error};
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
let sql = "select ename, sal, comm from emp where deptno = :1";
println!("---------------|---------------|---------------|");
let rows = conn.query(sql, &[&30])?;
for row_result in rows {
    let row = row_result?;
    let ename: String = row.get(0)?;
    let sal: i32 = row.get("sal")?;
    let comm: Option<i32> = row.get(2)?;
    println!(" {:14}| {:>10}    | {:>10}    |",
             ename,
             sal,
             comm.map_or("".to_string(), |v| v.to_string()));
}

query_name方式执行并获得结果

query_named方法会查询获得查询的所有结果,需要在执行后用result对象接收结果集,在循环中获取结果集中的每一行的数据。同时,如果语句中有参数的情况下,通过参数名称的方式指定参数值。示例如下:

use shentong::{Connection, Error};
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
let sql = "select ename, sal, comm from emp where deptno = :icol";
println!("---------------|---------------|---------------|");
let rows = conn.query_named(sql, &[("icol", &30)])?;
for row_result in rows {
    let row = row_result?;
    let ename: String = row.get(0)?;
    let sal: i32 = row.get("sal")?;
    let comm: Option<i32> = row.get(2)?;
    println!(" {:14}| {:>10}    | {:>10}    |",
             ename,
             sal,
             comm.map_or("".to_string(), |v| v.to_string()));
}

query_as方式执行并获得结果

query_as方法可以在执行查询时设置每个列的返回类型,这样在获取结果时就不用指定类型了,示例如下:

use shentong::{Connection, Error};
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
let sql = "select ename, sal, comm from emp where deptno = :1";
println!("---------------|---------------|---------------|");
let rows = conn.query_as::<(String, i32, Option<i32>)>(sql, &[&10])?;
for row_result in rows {
    let (ename, sal, comm) = row_result?;
    println!(" {:14}| {:>10}    | {:>10}    |",
             ename,
             sal,
             comm.map_or("".to_string(), |v| v.to_string()));
}

query_as_named方式执行并获得结果

query_as_named方法可以在执行查询时设置每个列的返回类型,这样在获取结果时就不用指定类型了,同时,如果语句中有参数的情况下,通过参数名称的方式指定参数值。示例如下:

use shentong::{Connection, Error};
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
let sql = "select ename, sal, comm from emp where deptno = :icol";
println!("---------------|---------------|---------------|");
let rows = conn.query_as::<(String, i32, Option<i32>)>(sql, &[("icol", &10)])?;
for row_result in rows {
    let (ename, sal, comm) = row_result?;
    println!(" {:14}| {:>10}    | {:>10}    |",
             ename,
             sal,
             comm.map_or("".to_string(), |v| v.to_string()));
}

query_row方式执行并获得结果

query_row方法执行查询后只返回一条数据(第一条),示例如下:

use shentong::Connection;
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;

let sql = "select ename, sal, comm from emp where empno = :1";
let row = conn.query_row(sql, &[&7369])?;
let ename: String = row.get("empno")?;
let sal: i32 = row.get("sal")?;
let comm: Option<i32> = row.get("comm")?;
println!("---------------|---------------|---------------|");
println!(" {:14}| {:>10}    | {:>10}    |",
         ename,
         sal,
         comm.map_or("".to_string(), |v| v.to_string()));

query_row_named方式执行并获得结果

query_row_named方法执行查询后只返回一条数据(第一条),同时如果语句中有参数的情况下,通过参数名称的方式指定参数值。示例如下:

use shentong::Connection;
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;

let sql = "select ename, sal, comm from emp where empno = :icol";
let row = conn.query_row_named(sql, &[("icol", &7369)])?;
let ename: String = row.get("empno")?;
let sal: i32 = row.get("sal")?;
let comm: Option<i32> = row.get("comm")?;
println!("---------------|---------------|---------------|");
println!(" {:14}| {:>10}    | {:>10}    |",
         ename,
         sal,
         comm.map_or("".to_string(), |v| v.to_string()));

query_row_as方式执行并获得结果

query_row_as方法执行查询后只返回一条数据(第一条),且在执行时指定各个列的返回类型,示例如下:

use shentong::Connection;
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
let sql = "select ename, sal, comm from emp where empno = :icol";
let row = conn.query_row_as::<(String, i32, Option<i32>)>(sql, &[&7566])?;
println!("---------------|---------------|---------------|");
println!(" {:14}| {:>10}    | {:>10}    |",
         row.0,
         row.1,
         row.2.map_or("".to_string(), |v| v.to_string()));

query_row_as_named方式执行并获得结果

query_row_as_named方法执行查询后只返回一条数据(第一条),且在执行时指定各个列的返回类型,同时如果语句中有参数的情况下,通过参数名称的方式指定参数值.示例如下:

use shentong::Connection;
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
let sql = "select ename, sal, comm from emp where empno = :icol";
let row = conn.query_row_as_named::<(String, i32, Option<i32>)>(sql, &[("icol", &7369)])?;
println!("---------------|---------------|---------------|");
println!(" {:14}| {:>10}    | {:>10}    |",
         row.0,
         row.1,
         row.2.map_or("".to_string(), |v| v.to_string()));

语句准备执行

通过Connection的prepare方法准备一条SQL语句,然后再用Statement的execute方法进行执行。Prepare时如果有参数,则在执行时需要给定参数值;一个sql语句被一次prepare后,可多次执行execute方法进行执行。

let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
// 创建要给prepare语句
let mut stmt = conn.prepare("insert into person values (:1, :2)", &[])?;
// 给定参数并执行插入
stmt.execute(&[&1, &"John"])?;
// 给定参数并执行插入另外一条数据
stmt.execute(&[&2, &"Smith"])?;

注意:如果prepare是查询语句,Statement对象也有query、query_named等系列执行查询的方法,用法与connection下的这些方法类似。 Statement结构的execute_named方法用于执行通过参数名称给参数绑定值的方式,比如:

let conn = Connection::connect("scott", "tiger", "")?;
let mut stmt = conn.prepare("insert into emp(empno, ename) values (:id, :name)", &[])?;
stmt.execute_named(&[("id", &114), ("name", &"Smith")])?;
stmt.execute_named(&[("id", &115),("name", &"Paul")])?; //

获取语句类型

当我们用preapre准备一条sql语句后,语句对象的statement_type方法就可获得语句的类型,比如:

let stmt = conn.prepare("SELECT ...", &[])?;
let stmt_type = stmt.statement_type();
assert_eq!(stmt_type, StatementType::Select);
StatementType中有很多类型的枚举,比如:
StatementType::Select
StatementType::Insert
StatementType::Update
StatementType::Delete
StatementType::Merge
StatementType::Create
StatementType::Alter
StatementType::Drop
StatementType::Begin
StatementType::Declare
StatementType::Commit
StatementType::Rollback

开发这还可以用一些方法来判定是否是期望的类型,比如:

assert_eq!(stmt.is_query(), true);
assert_eq!(stmt.is_plsql(), false);
assert_eq!(stmt.is_ddl(), false);
assert_eq!(stmt.is_dml(), false);
assert_eq!(stmt.is_returning(), false);

获得查询结果的列信息

获得查询结果的列信息,有助于开发者在程序中通过代码逻辑实现类型的判定来获取数据,开发者在获取结果时不必很清楚每个列的类型等信息。

use shentong::Connection;

// 创建数据库连接.
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;

let sql = "select ename, sal, comm from emp where 1 = 2";
let rows = conn.query(sql, &[])?;

// 打印列名
for info in rows.column_info() {
    print!(" {:14}|", info.name());
}
println!("");

// 打印列类型
for info in rows.column_info() {
    print!(" {:14}|", info.oracle_type().to_string());
}
println!("");

参数信息获取

用语句对象的bind_count的方法获得参数个数,用bind_names方法获得参数的集合。、

      let stmt = conn.prepare("select * from test where id = :idval", &[])?;
assert_eq!(stmt.bind_count(), 1);
let bind_names = stmt.bind_names();
assert_eq!(bind_names.len(), 1);
      assert_eq!(bind_names[0], "idval");

执行无结果语句

执行insert、update以及DDL语句时,时没有结果集返回的,因此可以直接调用Connection的execute方法执行语句。

use shentong::Connection;

// 创建数据库连接.
let conn = Connection::connect("sysdba", "szoscar55", "localhost:2003/osrdb")?;
conn.execute("create table person (id number(38), name varchar2(40))", &[])?;
//按位置绑定参数
conn.execute("insert into person values (:1, :2)",&[&1, &"John"])?;
//按名称绑定参数
conn.execute_named("insert into person values (:id, :name)",&[("id", &2),  ("name", &"Smith"), ])?;

// 提交事务
conn.commit()?;

// 删除数据
conn.execute("delete from person", &[])?;

//回滚事务
conn.rollback()?;

大对象的处理

小大对象可以用普通数据类型方式进行获取,也可以用专属的类型处理结构来操作,比如数据库中的BLOB类型,可以用Blob结构来处理:

use shentong::sql_type::Blob;
use shentong::sql_type::Lob;
use std::io::BufReader;
use std::io::Read;
let sql = "select BLOBCol from TestBLOBS where IntCol = 1";
let mut stmt = conn.statement(sql).lob_locator().build()?;
let blob = stmt.query_row_as::<Blob>(&[])?;
let mut buf_reader = BufReader::with_capacity(blob.chunk_size()? * 16, blob);
let mut buf = [0u8; 4];
assert_eq!(buf_reader.read(&mut buf)?, 4); // read the first four bytes
assert_eq!(&buf, b"BLOB");
assert_eq!(buf_reader.read(&mut buf)?, 4); // read the next four bytes
assert_eq!(&buf, b" DAT");
assert_eq!(buf_reader.read(&mut buf)?, 1); // read the last one byte
assert_eq!(&buf[0..1], b"A");
assert_eq!(buf_reader.read(&mut buf)?, 0); // end of blob

事务处理

conn对象上有commit和rollback方法进行事务的提交和回滚;默认情况下,事务是是需要手动提交的,如果想设置为自动提交,需要执行:

conn.set_autocommit(true);

Returning语句执行

支持returning into语法的执行,同时支持将into的值返回,通过语句对象的returned_values方法返回。

 let sql = "update TestStrings set StringCol = StringCol where IntCol >= :1 returning IntCol into :icol";
    let mut stmt = conn.prepare(sql, &[])?;
assert_eq!(stmt.is_returning(), true);
//用bind方法将参数2设置为out参数绑定
stmt.bind(2, &None::<i32>)?;

    // 更新一行
    stmt.execute(&[&10])?;
    let updated_int_col: Vec<i32> = stmt.returned_values(2)?;
assert_eq!(updated_int_col, vec![10]);

NULL值绑定

如果我们执行语句时,参数绑定如果想传入一个NULL,则可以在执行语句时,参数的值给None,比如:&None::<i32>:

conn.execute(
        "insert into TestNumbers values (:1, :2, :3, :4, :5)",
        &[&100, &9.2, &10.14, &7.14, &None::<i32>],
    )?;

获得影响行数

对于update、insert等语句,执行成功后会有影响行数,通过语句对象的row_count方法可以获得影响行数;如果时select语句,获得时已经获取到的行数。

let stmt = conn.execute(
    "update TestStrings set StringCol = StringCol where IntCol >= :1",
    &[&6],
)?;
assert_eq!(stmt.row_count()?, 5); //update语句更新的行数

//获取到的行数
let mut stmt = conn.prepare("select * from TestStrings where IntCol >= :1", &[])?;
assert_eq!(stmt.row_count()?, 0); //未获取前
for _row in stmt.query(&[&6])? {}
assert_eq!(stmt.row_count()?, 5); //获取后