飞鸽传书项目中 sqlite数据库应用

Sqlite3数据库使用注释

    飞秋项目接近尾声,各位同学大多数都用的是链表来对上线用户及文件传输来进行数据保存,在开发中采用链表与数据库利弊参半,我们一起先来分析一下!如有不当请各位同学补充!
    首先采用链表不从理论上讲不用与外存进行交互,效率自然相对较高,而数据库在开启、关闭过程中肯定要与外存交互,自然存在着性能差距。
    其次链表存储不会产生锁,但是可能产生数据操作差异,就是同时对一个链表操作时可能产生数据差异,数据库的底层通常会对该机制进行实现,但是在多线程时会产生数据库锁。
    再次在大数据量时链表可能会产生性能瓶颈,而同等数据量下sqlite的数据管理则更为省心。
    在本项目中最重要的一点是采用数据库基本不会出现段错误,大大提高了开发效率,使数据操作过程变得更为便捷。
   
    数据库的操作从应用方面来总结最常用的主要有六种操作,数据库打开、关闭,及增删改查操作。

数据库打开
    数据库在使用过程中一定要注意,数据库中存储二维表,表中存储的是一条一条的记录。
    在我看来,sqlite中数据库是独立的文件,linux版本的没有扩展名限制,在本项目中我们采用的是feiq.db。这样做的原因是:第一次我建立的数据库叫feiq,生成的可执行文件也叫feiq,编译时文件产生了覆盖。所以最后改成了feiq.db,重现建立的库跟表。
    sqlite3 *db = NULL;
    pthread_mutex_t mutex;//防止数据库死锁
   
    void dbinit()//数据库初始化
    {
        pthread_mutex_init(&mutex,NULL);
        sqlite3_open("feiq.db",&db);
        if(result!=SQLITE_OK)
        {
            printf("open error\n");
   
        }
    }
    本函数中主要是数据库的初始化,这里我建立了一个锁,防止数据库死锁,但是不知道是不是真的好使,目前来看是没有什么问题。sqlite3_open("feiq.db",&db);语句打开了数据库,
Feiq.db是数据库文件名,要注意的是使用前先建立数据库 sqlite3 feiq.db,通过create语句建立相应的表:
CREATE TABLE userlist(id integer(4) primary key,name varchar(20),host varchar(30),addr integer(4)) 保存在线用户列表
CREATE TABLE filelist(id integer(4) primary key,name varchar(50),num integer,pkgnum integer(4),size integer(4),ltime integer(4),user varchar(10))保存发送文件列表
CREATE TABLE recvlist(id integer(4) primary key,name varchar(50),num integer,pkgnum integer(4),size integer(4),ltime integer(4),user varchar(10))保存接收文件列表
CREATE TABLE user(id integer(4) primary key,name char(20),pass char(20)) 保存登陆用户

关闭数据库
void dbclose()//关闭
{
    sqlite3_close(db);
}
在程序结束后关闭数据库,关闭连接,释放占用的系统资源。




void dboper(char * sql)//增删改操作
{
    pthread_mutex_lock(&mutex);
   
    sqlite3_exec(db,sql,NULL,NULL,&errmsg);
    if(errmsg)
    {   
        printf("errsql is %s\n",sql);
        printf("errmsg444=%s \n",errmsg);
    }
   
    pthread_mutex_unlock(&mutex);
}
该函数需要一个参数char * sql,这里传递的是字符指针类型的一个变量,说白了就是传递了一个字符串的首地址,这个字符串是一条sql语句。

在使用过程中实例如下:
在线程启动时初始化数据库,清空用户列表中的所有数据
dbinit();//数据库初始化
dboper("delete from userlist");//先清除数据库数据
注意 这里清除表里的数据一般不会删除表再创建表,用delete from 表名  语句就可以删除指定表中的所有数据。



另外如果sql语句中有变量,
比如:第一次插入 1,aa,bb
      第二次插入 2,cc,ff
这里三个位置的值都是改变的,就需要对sql语句进行拼接

char sql[200] = "";

bzero(&sendB, sizeof(sendB));
long seconds = time((time_t*)NULL);
int length = sprintf(sendB, "1:%ld:my:lovecc:%ld:mylovecc", seconds, (IPMSG_ANSENTRY));
sendto(sockfd, sendB, length, 0, (struct sockaddr *)&userAddr, sizeof(userAddr));//如有后上线用户广播后进行回复:我在线
   
bzero(&sql, sizeof(sql));//sql语句清零
sprintf(sql, "insert into userlist values(null,'%s','%s',%d)", username, mathinename, userAddr.sin_addr.s_addr);//sql语句拼接
dboper(sql);//sql语句执行

在sprintf中 insert into userlist values(null,部分是不变的,每次都相同;,'%s','%s',%d)"部分%s
%s %d是每次改变的值的位置,所以通过这种方式就可以完成sql语句的拼接。

Insert是插入语句
Update是修改语句
Delete是查询语句 除了特殊应用,如清除表中所有数据,delete语句后必须加条件where,否则会清空表数据,在企业级开发中这会造成非常严重的后果,你就离被开除不远了!


在sql语句中查询语句是最为复杂的,在实际的java项目开发中一条查询写满一屏幕并不稀奇,所以查询语句极为重要。
int my_print(void *para, int n_column, char **column_value, char **column_name)//回调遍历
{
    int i=0;

    for(i=0;i<n_column;i++)
    {
        printf("%s=%s\t",column_name[i],column_value[i]);
    }
    printf("\n");
   
    return 0;
}



void dbquery(char *sql)//查询 显示所有记录
{
    pthread_mutex_lock(&mutex);
   
    result = sqlite3_exec(db,sql,my_print,arg,&errmsg);
   
    if(errmsg)
    {
        printf("errmsg11=%s \n",errmsg);
    }

    pthread_mutex_unlock(&mutex);
}
void dbquery(char *sql)//查询 显示所有记录,同样需要传递一个字符串常量,作为sql语句,使用方式与上边基本一致,所以不再多说。
在这里有一个回调函数
int my_print(void *para, int n_column, char **column_value, char **column_name)//回调遍历
其作用是每查出来一条记录则调用一次该函数来进行输出,
printf("%s=%s\t",column_name[i],column_value[i]);这里输出了字段名与字段值
同时该方法是通用的,任何一个表,如果只是想按上述格式显示所有的数据的话,直接调用
void dbquery(char *sql)函数,你需要做的就是传入sql语句:"select *from *** where ***=***"

char sql[200] = "";
sprintf(sql ,"select * from userlist where name='%s'",name);
dbquery(sql);


查询的另外一种常见用法是将查询到的值取出来,而不仅仅是简单的展示。
在这里我采用了一个相对变通的方式来取值,不是特别的标准,但是可以实现功能。


void dbquerygetuser(char *sql,IPMSG_USER *user)//根据条件查询用户
{
    pthread_mutex_lock(&mutex);

    result = sqlite3_exec(db,sql,my_getuser,user,&errmsg);
    if(errmsg)
    {
        printf("errmsg222=%s \n",errmsg);
    }
   
    pthread_mutex_unlock(&mutex);
}
这次的回调函数是my_getuser,在下边进行分析。
result = sqlite3_exec(db,sql,my_getuser,user,&errmsg);
本函数传递过来两个参数,一个是前面反复说的sql语句char *sql,这里需要的是一个查询语句,就是你需要一个什么条件的记录,那么就写sql语句进行查询。my_getuser是指定回调函数。user最为重要,需要传递过来一个结构体对象。

调用时可以:
IPMSG_USER *us = (IPMSG_USER *)malloc(sizeof(IPMSG_USER));
Char sql[200] = "select * from userlist where name='k29'";
dbquerygetuser(sql,us);
如果你想用查询到的值 那么printf("getuerbyname %s\n",(*us).name); 就可以了



int my_getuser(void *para, int n_column, char **column_value, char **column_name)//将结果放到user结构体中
{
   
    int i=0;
    char temp[4][100];
    IPMSG_USER *user = (IPMSG_USER *)para;

    for(i=0;i<n_column;i++)
    {
        printf("%s=%s\t",column_name[i],column_value[i]);
       
        if(column_value[i]!=NULL)
        {
            strcpy(temp[i],column_value[i]);
            temp[i][strlen(column_value[i])] = '\0';
        }
        else
        bzero(temp[i],sizeof(temp[i]));
    }

    printf("\n");

    strncpy((*user).name,temp[1],strlen(temp[1])+1);
    strncpy((*user).host,temp[2],strlen(temp[2])+1);
    (*user).s_addr= (unsigned int)atoi(temp[3]);
   
    return 0;
}

回调函数中被我标注成红色的部分是我自己想到的一个不标准做法,但是代码完全能够实现功能。
其目的是要讲查询到的数据放到一个结构体当中,但是c里面没有iterator对象,也没有其他的辅助手段,所以这里我用到了二维字符数组,相当于一个字符串数组,要注意的是不管你表中字段是什么类型,取出来的时候都可以是字符串,所以
for(i=0;i<n_column;i++)
    {
        printf("%s=%s\t",column_name[i],column_value[i]);
       
        if(column_value[i]!=NULL)
        {
            strcpy(temp[i],column_value[i]);
            temp[i][strlen(column_value[i])] = '\0';
        }
        else
        bzero(temp[i],sizeof(temp[i]));
    }
就取出来了,bzero(temp[i],sizeof(temp[i]));是将为null字段清空bzero
后边再将数组中的数据转换,保存进结构体
strncpy((*user).name,temp[1],strlen(temp[1])+1);
strncpy((*user).host,temp[2],strlen(temp[2])+1);
(*user).s_addr= (unsigned int)atoi(temp[3]);

祝各位好运 呵呵,有问题可以互相讨论。

猜你喜欢

转载自yan0063.iteye.com/blog/1664413
今日推荐