zoukankan      html  css  js  c++  java
  • SQLift基础

    基于嵌入式的数据库

    • 基于嵌入式Linux的数据库主要有SQLift,Firebird,Berkeley DB,eXtremeDb
    • Friebird是关系型数据库,功能强大,支持存储过程,SQL兼容等
    • SQLite关系型数据库,体积小,支持ACID事务
    • Berkeley DB中并没有数据库服务器的概念,它的程序库直接连接到应用程序中
    • eXtremeDB是内存数据库,运行高效

    SQLite基础

    SQLite的源代码是C,其源代码完全开放,SQLite第一个Alpha版本诞生于2000年5月,他是一个轻量级的嵌入式数据库

    SQLite有以下特性

    • 零配置——无需安装和管理配置;
    • 存储在单一磁盘文件中的一个完整的数据库;
    • 数据库文件可以在不同字节顺序的机器间自由共享;
    • 支持数据库大小至2TB
    • 足够小,全部源代码大致3万行c代码,250k
    • 比目前流行的大多数数据库对数据的操作要快

    下载 

    other:

      https://www.sqlite.org/index.html

    Mac:

      brew install sqlift

    // 进入命令行交互

    $ sqlite3 version

    SQLite3基本命令

    系统命令(以`.`开头的命令)

    命令描述
    .backup ?DB? FILE 备份 DB 数据库(默认是 "main")到 FILE 文件。
    .bail ON|OFF 发生错误后停止。默认为 OFF。
    .databases 列出数据库的名称及其所依附的文件。
    .dump ?TABLE? 以 SQL 文本格式转储数据库。如果指定了 TABLE 表,则只转储匹配 LIKE 模式的 TABLE 表。
    .echo ON|OFF 开启或关闭 echo 命令。
    .exit 退出 SQLite 提示符。
    .explain ON|OFF 开启或关闭适合于 EXPLAIN 的输出模式。如果没有带参数,则为 EXPLAIN on,即开启 EXPLAIN。
    .header(s) ON|OFF 开启或关闭头部显示。
    .help 显示消息。
    .import FILE TABLE 导入来自 FILE 文件的数据到 TABLE 表中。
    .indices ?TABLE? 显示所有索引的名称。如果指定了 TABLE 表,则只显示匹配 LIKE 模式的 TABLE 表的索引。
    .load FILE ?ENTRY? 加载一个扩展库。
    .log FILE|off 开启或关闭日志。FILE 文件可以是 stderr(标准错误)/stdout(标准输出)。
    .mode MODE 设置输出模式,MODE 可以是下列之一:
    • csv 逗号分隔的值

    • column 左对齐的列

    • html HTML 的 <table> 代码

    • insert TABLE 表的 SQL 插入(insert)语句

    • line 每行一个值

    • list 由 .separator 字符串分隔的值

    • tabs 由 Tab 分隔的值

    • tcl TCL 列表元素

    .nullvalue STRING 在 NULL 值的地方输出 STRING 字符串。
    .output FILENAME 发送输出到 FILENAME 文件。
    .output stdout 发送输出到屏幕。
    .print STRING... 逐字地输出 STRING 字符串。
    .prompt MAIN CONTINUE 替换标准提示符。
    .quit 退出 SQLite 提示符。
    .read FILENAME 执行 FILENAME 文件中的 SQL。
    .schema ?TABLE? 显示 CREATE 语句。如果指定了 TABLE 表,则只显示匹配 LIKE 模式的 TABLE 表。
    .separator STRING 改变输出模式和 .import 所使用的分隔符。
    .show 显示各种设置的当前值。
    .stats ON|OFF 开启或关闭统计。
    .tables ?PATTERN? 列出匹配 LIKE 模式的表的名称。
    .timeout MS 尝试打开锁定的表 MS 毫秒。
    .width NUM NUM 为 "column" 模式设置列宽度。
    .timer ON|OFF 开启或关闭 CPU 定时器。

    sql命令

    所有的 SQLite 语句可以以任何关键字开始,如 SELECT、INSERT、UPDATE、DELETE、ALTER、DROP 等,所有的语句以分号(;)结束。

    SQLite ANALYZE 语句:

    ANALYZE;
    or
    ANALYZE database_name;
    or
    ANALYZE database_name.table_name;
    

      

    SQLite AND/OR 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  CONDITION-1 {AND|OR} CONDITION-2;
    

      

    SQLite ALTER TABLE 语句:

    ALTER TABLE table_name ADD COLUMN column_def...;
    

      

    SQLite ALTER TABLE 语句(Rename):

    ALTER TABLE table_name RENAME TO new_table_name;
    

      

    SQLite ATTACH DATABASE 语句:

    ATTACH DATABASE 'DatabaseName' As 'Alias-Name';
    

      

    SQLite BEGIN TRANSACTION 语句:

    BEGIN;
    or
    BEGIN EXCLUSIVE TRANSACTION;
    

      

    SQLite BETWEEN 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  column_name BETWEEN val-1 AND val-2;
    

      

    SQLite COMMIT 语句:

    COMMIT;
    

      

    SQLite CREATE INDEX 语句:

    CREATE INDEX index_name
    ON table_name ( column_name COLLATE NOCASE );
    

      

    SQLite CREATE UNIQUE INDEX 语句:

    CREATE UNIQUE INDEX index_name
    ON table_name ( column1, column2,...columnN);
    

      

    SQLite CREATE TABLE 语句:

    CREATE TABLE table_name(
       column1 datatype,
       column2 datatype,
       column3 datatype,
       .....
       columnN datatype,
       PRIMARY KEY( one or more columns )
    );
    

      

    SQLite CREATE TRIGGER 语句:

    CREATE TRIGGER database_name.trigger_name 
    BEFORE INSERT ON table_name FOR EACH ROW
    BEGIN 
       stmt1; 
       stmt2;
       ....
    END;
    

      

    SQLite CREATE VIEW 语句:

    CREATE VIEW database_name.view_name  AS
    SELECT statement....;
    

      

    SQLite CREATE VIRTUAL TABLE 语句:

    CREATE VIRTUAL TABLE database_name.table_name USING weblog( access.log );
    or
    CREATE VIRTUAL TABLE database_name.table_name USING fts3( );
    

      

    SQLite COMMIT TRANSACTION 语句:

    COMMIT;
    

      

    SQLite COUNT 子句:

    SELECT COUNT(column_name)
    FROM   table_name
    WHERE  CONDITION;
    

      

    SQLite DELETE 语句:

    DELETE FROM table_name
    WHERE  {CONDITION};
    

      

    SQLite DETACH DATABASE 语句:

    DETACH DATABASE 'Alias-Name';
    

      

    SQLite DISTINCT 子句:

    SELECT DISTINCT column1, column2....columnN
    FROM   table_name;
    

      

    SQLite DROP INDEX 语句:

    DROP INDEX database_name.index_name;
    

      

    SQLite DROP TABLE 语句:

    DROP TABLE database_name.table_name;
    

      

    SQLite DROP VIEW 语句:

    DROP VIEW view_name;
    

      

    SQLite DROP TRIGGER 语句:

    DROP TRIGGER trigger_name
    

      

    SQLite EXISTS 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  column_name EXISTS (SELECT * FROM   table_name );
    

      

    SQLite EXPLAIN 语句:

    EXPLAIN INSERT statement...;
    or 
    EXPLAIN QUERY PLAN SELECT statement...;
    

      

    SQLite GLOB 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  column_name GLOB { PATTERN };
    

      

    SQLite GROUP BY 子句:

    SELECT SUM(column_name)
    FROM   table_name
    WHERE  CONDITION
    GROUP BY column_name;
    

      

    SQLite HAVING 子句:

    SELECT SUM(column_name)
    FROM   table_name
    WHERE  CONDITION
    GROUP BY column_name
    HAVING (arithematic function condition);
    

      

    SQLite INSERT INTO 语句:

    INSERT INTO table_name( column1, column2....columnN)
    VALUES ( value1, value2....valueN);
    

      

    SQLite IN 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  column_name IN (val-1, val-2,...val-N);
    

      

    SQLite Like 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  column_name LIKE { PATTERN };
    

      

    SQLite NOT IN 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  column_name NOT IN (val-1, val-2,...val-N);
    

      

    SQLite ORDER BY 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  CONDITION
    ORDER BY column_name {ASC|DESC};
    

      

    SQLite PRAGMA 语句:

    PRAGMA pragma_name;
    
    For example:
    
    PRAGMA page_size;
    PRAGMA cache_size = 1024;
    PRAGMA table_info(table_name);
    

      

    SQLite RELEASE SAVEPOINT 语句:

    RELEASE savepoint_name;
    

      

    SQLite REINDEX 语句:

    REINDEX collation_name;
    REINDEX database_name.index_name;
    REINDEX database_name.table_name;
    

      

    SQLite ROLLBACK 语句:

    ROLLBACK;
    or
    ROLLBACK TO SAVEPOINT savepoint_name;
    

      

    SQLite SAVEPOINT 语句:

    SAVEPOINT savepoint_name;
    

      

    SQLite SELECT 语句:

    SELECT column1, column2....columnN
    FROM   table_name;
    

      

    SQLite UPDATE 语句:

    UPDATE table_name
    SET column1 = value1, column2 = value2....columnN=valueN
    [ WHERE  CONDITION ];
    

      

    SQLite VACUUM 语句:

    VACUUM;
    

      

    SQLite WHERE 子句:

    SELECT column1, column2....columnN
    FROM   table_name
    WHERE  CONDITION;
    

      

    SQLite C语言编程接口

    序号API & 描述
    1 sqlite3_open(const char *filename, sqlite3 **ppDb)

    该例程打开一个指向 SQLite 数据库文件的连接,返回一个用于其他 SQLite 程序的数据库连接对象。

    如果 filename 参数是 NULL 或 ':memory:',那么 sqlite3_open() 将会在 RAM 中创建一个内存数据库,这只会在 session 的有效时间内持续。

    如果文件名 filename 不为 NULL,那么 sqlite3_open() 将使用这个参数值尝试打开数据库文件。如果该名称的文件不存在,sqlite3_open() 将创建一个新的命名为该名称的数据库文件并打开。

    filename:数据库文件路径

    ppDB:指向sqlite句柄指针

    返回值:成功返回0,失败返回错误码(非零值)

    2 sqlite3_exec(sqlite3*db, const char *sql, sqlite_callback, void *data, char **errmsg)

    该例程提供了一个执行 SQL 命令的快捷方式,SQL 命令由 sql 参数提供,可以由多个 SQL 命令组成。

    在这里,第一个参数 sqlite3 是打开的数据库对象,sqlite_callback 是一个回调,data 作为其第一个参数,errmsg 将被返回用来获取程序生成的任何错误。

    sqlite3_exec() 程序解析并执行由 sql 参数所给的每个命令,直到字符串结束或者遇到错误为止。

    db:数据库操作句柄

    sql:sql语句

    sqlite_callback:回调函数,只有sql为查询函数才会执行此回调

    data:给回调函数传递参数

    errmsg:错误消息

    成功返回:SQLITE_OK

    3 sqlite3_close(sqlite3*)

    该例程关闭之前调用 sqlite3_open() 打开的数据库连接。所有与连接相关的语句都应在连接关闭之前完成。

    如果还有查询没有完成,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。

    成功返回0,失败返回错误码

    4

    const char *sqlite3_errmg(sqlite3 *db)

    返回错误信息

    5

    typedef int(*sqlite3_callback)(void *para,int f_num,char **f_value,char **fname)

    每找到一条记录自动执行一次回调函数

    -para:传递给回调函数的参数

    -f_num:记录中包含的字段数目

    -f_value:包含每个字段值的指针数组

    -f_name:包含每个字段名称的指针数组

    返回值:成功返回0,失败返回-1

    6

    不使用callback执行sql语句

    int sqlite3_get_table(sqlite3 *db,const cgar *sql,char ***resultp,int *nrow,int *ncolumn,char **errmsg)

    执行sql操作

    db:数据库句柄

    sql:sql语句

    resultp:用来指向sql执行结果的指针

    nrow:满足条件的记录的数目

    ncolum:每条记录包含的字段数目

    errmsg:错误信息指针的地址

    返回值:成功返回0,失败返回错误码

    连接数据库

    下面的 C 代码段显示了如何连接到一个现有的数据库。如果数据库不存在,那么它就会被创建,最后将返回一个数据库对象。

    #include <stdio.h>
    #include <sqlite3.h>
    
    int main(int argc, char* argv[])
    {
       sqlite3 *db;
       char *zErrMsg = 0;
       int rc;
    
       rc = sqlite3_open("test.db", &db);
    
       if( rc ){
          fprintf(stderr, "Can't open database: %s
    ", sqlite3_errmsg(db));
          exit(0);
       }else{
          fprintf(stderr, "Opened database successfully
    ");
       }
       sqlite3_close(db);
    }

    现在,让我们来编译和运行上面的程序,在当前目录中创建我们的数据库 test.db。您可以根据需要改变路径。

    $gcc test.c -l sqlite3
    $./a.out
    Opened database successfully

    在这里,把我们的程序链接上 sqlite3 库,以便向 C 程序提供必要的函数。这将在您的目录下创建一个数据库文件 test.db,您将得到如下结果:

    -rwxr-xr-x. 1 root root 7383 May  8 02:06 a.out
    -rw-r--r--. 1 root root  323 May  8 02:05 test.c
    -rw-r--r--. 1 root root    0 May  8 02:06 test.db

    创建表

    下面的 C 代码段将用于在先前创建的数据库中创建一个表:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sqlite3.h> 
    
    static int callback(void *NotUsed, int argc, char **argv, char **azColName){
       int i;
       for(i=0; i<argc; i++){
          printf("%s = %s
    ", azColName[i], argv[i] ? argv[i] : "NULL");
       }
       printf("
    ");
       return 0;
    }
    
    int main(int argc, char* argv[])
    {
       sqlite3 *db;
       char *zErrMsg = 0;
       int  rc;
       char *sql;
    
       /* Open database */
       rc = sqlite3_open("test.db", &db);
       if( rc ){
          fprintf(stderr, "Can't open database: %s
    ", sqlite3_errmsg(db));
          exit(0);
       }else{
          fprintf(stdout, "Opened database successfully
    ");
       }
    
       /* Create SQL statement */
       sql = "CREATE TABLE COMPANY("  
             "ID INT PRIMARY KEY     NOT NULL," 
             "NAME           TEXT    NOT NULL," 
             "AGE            INT     NOT NULL," 
             "ADDRESS        CHAR(50)," 
             "SALARY         REAL );";
    
       /* Execute SQL statement */
       rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
       if( rc != SQLITE_OK ){
       fprintf(stderr, "SQL error: %s
    ", zErrMsg);
          sqlite3_free(zErrMsg);
       }else{
          fprintf(stdout, "Table created successfully
    ");
       }
       sqlite3_close(db);
       return 0;
    }

    上述程序编译和执行时,它会在 test.db 文件中创建 COMPANY 表,最终文件列表如下所示:

    -rwxr-xr-x. 1 root root 9567 May  8 02:31 a.out
    -rw-r--r--. 1 root root 1207 May  8 02:31 test.c
    -rw-r--r--. 1 root root 3072 May  8 02:31 test.db

    INSERT 操作

    下面的 C 代码段显示了如何在上面创建的 COMPANY 表中创建记录:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sqlite3.h>
    
    static int callback(void *NotUsed, int argc, char **argv, char **azColName){
       int i;
       for(i=0; i<argc; i++){
          printf("%s = %s
    ", azColName[i], argv[i] ? argv[i] : "NULL");
       }
       printf("
    ");
       return 0;
    }
    
    int main(int argc, char* argv[])
    {
       sqlite3 *db;
       char *zErrMsg = 0;
       int rc;
       char *sql;
    
       /* Open database */
       rc = sqlite3_open("test.db", &db);
       if( rc ){
          fprintf(stderr, "Can't open database: %s
    ", sqlite3_errmsg(db));
          exit(0);
       }else{
          fprintf(stderr, "Opened database successfully
    ");
       }
    
       /* Create SQL statement */
       sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "  
             "VALUES (1, 'Paul', 32, 'California', 20000.00 ); " 
             "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) "  
             "VALUES (2, 'Allen', 25, 'Texas', 15000.00 ); "     
             "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" 
             "VALUES (3, 'Teddy', 23, 'Norway', 20000.00 );" 
             "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY)" 
             "VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00 );";
    
       /* Execute SQL statement */
       rc = sqlite3_exec(db, sql, callback, 0, &zErrMsg);
       if( rc != SQLITE_OK ){
          fprintf(stderr, "SQL error: %s
    ", zErrMsg);
          sqlite3_free(zErrMsg);
       }else{
          fprintf(stdout, "Records created successfully
    ");
       }
       sqlite3_close(db);
       return 0;
    }

    上述程序编译和执行时,它会在 COMPANY 表中创建给定记录,并会显示以下两行:

    Opened database successfully
    Records created successfully

    SELECT 操作

    在我们开始讲解获取记录的实例之前,让我们先了解下回调函数的一些细节,这将在我们的实例使用到。这个回调提供了一个从 SELECT 语句获得结果的方式。它声明如下:

    typedef int (*sqlite3_callback)(
    void*,    /* Data provided in the 4th argument of sqlite3_exec() */
    int,      /* The number of columns in row */
    char**,   /* An array of strings representing fields in the row */
    char**    /* An array of strings representing column names */
    );

    如果上面的回调在 sqlite_exec() 程序中作为第三个参数,那么 SQLite 将为 SQL 参数内执行的每个 SELECT 语句中处理的每个记录调用这个回调函数。

    下面的 C 代码段显示了如何从前面创建的 COMPANY 表中获取并显示记录:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sqlite3.h>
    
    static int callback(void *data, int argc, char **argv, char **azColName){
       int i;
       fprintf(stderr, "%s: ", (const char*)data);
       for(i=0; i<argc; i++){
          printf("%s = %s
    ", azColName[i], argv[i] ? argv[i] : "NULL");
       }
       printf("
    ");
       return 0;
    }
    
    int main(int argc, char* argv[])
    {
       sqlite3 *db;
       char *zErrMsg = 0;
       int rc;
       char *sql;
       const char* data = "Callback function called";
    
       /* Open database */
       rc = sqlite3_open("test.db", &db);
       if( rc ){
          fprintf(stderr, "Can't open database: %s
    ", sqlite3_errmsg(db));
          exit(0);
       }else{
          fprintf(stderr, "Opened database successfully
    ");
       }
    
       /* Create SQL statement */
       sql = "SELECT * from COMPANY";
    
       /* Execute SQL statement */
       rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
       if( rc != SQLITE_OK ){
          fprintf(stderr, "SQL error: %s
    ", zErrMsg);
          sqlite3_free(zErrMsg);
       }else{
          fprintf(stdout, "Operation done successfully
    ");
       }
       sqlite3_close(db);
       return 0;
    }

    上述程序编译和执行时,它会产生以下结果:

    Opened database successfully
    Callback function called: ID = 1
    NAME = Paul
    AGE = 32
    ADDRESS = California
    SALARY = 20000.0
    
    Callback function called: ID = 2
    NAME = Allen
    AGE = 25
    ADDRESS = Texas
    SALARY = 15000.0
    
    Callback function called: ID = 3
    NAME = Teddy
    AGE = 23
    ADDRESS = Norway
    SALARY = 20000.0
    
    Callback function called: ID = 4
    NAME = Mark
    AGE = 25
    ADDRESS = Rich-Mond
    SALARY = 65000.0
    
    Operation done successfully

    UPDATE 操作

    下面的 C 代码段显示了如何使用 UPDATE 语句来更新任何记录,然后从 COMPANY 表中获取并显示更新的记录:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sqlite3.h> 
    
    static int callback(void *data, int argc, char **argv, char **azColName){
       int i;
       fprintf(stderr, "%s: ", (const char*)data);
       for(i=0; i<argc; i++){
          printf("%s = %s
    ", azColName[i], argv[i] ? argv[i] : "NULL");
       }
       printf("
    ");
       return 0;
    }
    
    int main(int argc, char* argv[])
    {
       sqlite3 *db;
       char *zErrMsg = 0;
       int rc;
       char *sql;
       const char* data = "Callback function called";
    
       /* Open database */
       rc = sqlite3_open("test.db", &db);
       if( rc ){
          fprintf(stderr, "Can't open database: %s
    ", sqlite3_errmsg(db));
          exit(0);
       }else{
          fprintf(stderr, "Opened database successfully
    ");
       }
    
       /* Create merged SQL statement */
       sql = "UPDATE COMPANY set SALARY = 25000.00 where ID=1; " 
             "SELECT * from COMPANY";
    
       /* Execute SQL statement */
       rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
       if( rc != SQLITE_OK ){
          fprintf(stderr, "SQL error: %s
    ", zErrMsg);
          sqlite3_free(zErrMsg);
       }else{
          fprintf(stdout, "Operation done successfully
    ");
       }
       sqlite3_close(db);
       return 0;
    }

    上述程序编译和执行时,它会产生以下结果:

    Opened database successfully
    Callback function called: ID = 1
    NAME = Paul
    AGE = 32
    ADDRESS = California
    SALARY = 25000.0
    
    Callback function called: ID = 2
    NAME = Allen
    AGE = 25
    ADDRESS = Texas
    SALARY = 15000.0
    
    Callback function called: ID = 3
    NAME = Teddy
    AGE = 23
    ADDRESS = Norway
    SALARY = 20000.0
    
    Callback function called: ID = 4
    NAME = Mark
    AGE = 25
    ADDRESS = Rich-Mond
    SALARY = 65000.0
    
    Operation done successfully

    DELETE 操作

    下面的 C 代码段显示了如何使用 DELETE 语句删除任何记录,然后从 COMPANY 表中获取并显示剩余的记录:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sqlite3.h> 
    
    static int callback(void *data, int argc, char **argv, char **azColName){
       int i;
       fprintf(stderr, "%s: ", (const char*)data);
       for(i=0; i<argc; i++){
          printf("%s = %s
    ", azColName[i], argv[i] ? argv[i] : "NULL");
       }
       printf("
    ");
       return 0;
    }
    
    int main(int argc, char* argv[])
    {
       sqlite3 *db;
       char *zErrMsg = 0;
       int rc;
       char *sql;
       const char* data = "Callback function called";
    
       /* Open database */
       rc = sqlite3_open("test.db", &db);
       if( rc ){
          fprintf(stderr, "Can't open database: %s
    ", sqlite3_errmsg(db));
          exit(0);
       }else{
          fprintf(stderr, "Opened database successfully
    ");
       }
    
       /* Create merged SQL statement */
       sql = "DELETE from COMPANY where ID=2; " 
             "SELECT * from COMPANY";
    
       /* Execute SQL statement */
       rc = sqlite3_exec(db, sql, callback, (void*)data, &zErrMsg);
       if( rc != SQLITE_OK ){
          fprintf(stderr, "SQL error: %s
    ", zErrMsg);
          sqlite3_free(zErrMsg);
       }else{
          fprintf(stdout, "Operation done successfully
    ");
       }
       sqlite3_close(db);
       return 0;
    }

    上述程序编译和执行时,它会产生以下结果:

    Opened database successfully
    Callback function called: ID = 1
    NAME = Paul
    AGE = 32
    ADDRESS = California
    SALARY = 20000.0
    
    Callback function called: ID = 3
    NAME = Teddy
    AGE = 23
    ADDRESS = Norway
    SALARY = 20000.0
    
    Callback function called: ID = 4
    NAME = Mark
    AGE = 25
    ADDRESS = Rich-Mond
    SALARY = 65000.0
    
    Operation done successfully
    Songzhibin
  • 相关阅读:
    windows 10 安装可视化mycat
    YYModel 源码解读(二)之NSObject+YYModel.h (1)
    NSCharacter​Set 使用说明
    YYModel 源码解读(二)之YYClassInfo.h (3)
    Cocoa深入学习:NSOperationQueue、NSRunLoop和线程安全 (转)
    Swift 必备开发库 (高级篇) (转)
    YYModel 源码解读(二)之YYClassInfo.h (2)
    YYModel 源码解读(二)之YYClassInfo.h (1)
    Runtime应用防止按钮连续点击 (转)
    YYModel 源码解读(一)之YYModel.h
  • 原文地址:https://www.cnblogs.com/binHome/p/12872390.html
Copyright © 2011-2022 走看看