iOS学习笔记-数据库操作(使用FMDB)
iOS中原生的SQLite API在使用时,非常不便。于是,就出现了一系列将SQLite API进行封装的库,例如FMDB、
PlausibleDatabase、sqlitepersistentobjects等,FMDB (https://github.com/ccgus/fmdb) 是一款简洁、易用的封装库,这一篇文章
简单介绍下FMDB的使用。
在FMDB下载文件后,工程中必须导入如下文件,并使用 libsqlite3.dylib 依赖包。
FMDB同时兼容ARC和非ARC工程,会自动根据工程配置来调整相关的内存管理代码。
FMDB常用类:
FMDatabase : 一个单一的SQLite数据库,用于执行SQL语句。
FMResultSet :执行查询一个FMDatabase结果集,这个和android的Cursor类似。
FMDatabaseQueue :在多个线程来执行查询和更新时会使用这个类。
创建数据库:
db = [FMDatabase databaseWithPath:database_path];
1、当数据库文件不存在时,fmdb会自己创建一个。
2、 如果你传入的参数是空串:@"" ,则fmdb会在临时文件目录下创建这个数据库,数据库断开连接时,数据库文件被删除。
3、如果你传入的参数是 NULL,则它会建立一个在内存中的数据库,数据库断开连接时,数据库文件被删除。
打开数据库:
[db open]
返回BOOL型。
关闭数据库:
[db close]
数据库增删改等操作:
除了查询操作,FMDB数据库操作都执行executeUpdate方法,这个方法返回BOOL型。
看一下例子:
创建表:
if ([db open]) {
NSString *sqlCreateTable = [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS '%@' ('%@' INTEGER PRIMARY
KEY AUTOINCREMENT, '%@' TEXT, '%@' INTEGER, '%@' TEXT)",TABLENAME,ID,NAME,AGE,ADDRESS];
BOOL res = [db executeUpdate:sqlCreateTable];
if (!res) {
NSLog(@"error when creating db table");
} else {
NSLog(@"success to creating db table");
}
[db close];
}
添加数据:
if ([db open]) {
NSString *insertSql1= [NSString stringWithFormat:
@"INSERT INTO '%@' ('%@', '%@', '%@') VALUES ('%@', '%@', '%@')",
TABLENAME, NAME, AGE, ADDRESS, @"张三", @"13", @"济南"];
BOOL res = [db executeUpdate:insertSql1];
NSString *insertSql2 = [NSString stringWithFormat:
@"INSERT INTO '%@' ('%@', '%@', '%@') VALUES ('%@', '%@', '%@')",
TABLENAME, NAME, AGE, ADDRESS, @"李四", @"12", @"济南"];
BOOL res2 = [db executeUpdate:insertSql2];
if (!res) {
NSLog(@"error when insert db table");
} else {
NSLog(@"success to insert db table");
}
[db close];
}
修改数据:
if ([db open]) {
NSString *updateSql = [NSString stringWithFormat:
@"UPDATE '%@' SET '%@' = '%@' WHERE '%@' = '%@'",
TABLENAME, AGE, @"15" ,AGE, @"13"];
BOOL res = [db executeUpdate:updateSql];
if (!res) {
NSLog(@"error when update db table");
} else {
NSLog(@"success to update db table");
}
[db close];
}
删除数据:
if ([db open]) {
NSString *deleteSql = [NSString stringWithFormat:
@"delete from %@ where %@ = '%@'",
TABLENAME, NAME, @"张三"];
BOOL res = [db executeUpdate:deleteSql];
if (!res) {
NSLog(@"error when delete db table");
} else {
NSLog(@"success to delete db table");
}
[db close];
}
数据库查询操作:
查询操作使用了executeQuery,并涉及到FMResultSet。
if ([db open]) {
NSString * sql = [NSString stringWithFormat:
@"SELECT * FROM %@",TABLENAME];
FMResultSet * rs = [db executeQuery:sql];
while ([rs next]) {
int Id = [rs intForColumn:ID];
NSString * name = [rs stringForColumn:NAME];
NSString * age = [rs stringForColumn:AGE];
NSString * address = [rs stringForColumn:ADDRESS];
NSLog(@"id = %d, name = %@, age = %@ address = %@", Id, name, age, address);
}
[db close];
}
FMDB的FMResultSet提供了多个方法来获取不同类型的数据:
数据库多线程操作:
如果应用中使用了多线程操作数据库,那么就需要使用FMDatabaseQueue来保证线程安全了。 应用中不可在多个线程中共同使
用一个FMDatabase对象操作数据库,这样会引起数据库数据混乱。 为了多线程操作数据库安全,FMDB使用了FMDatabaseQueue,使用
FMDatabaseQueue很简单,首先用一个数据库文件地址来初使化FMDatabaseQueue,然后就可以将一个闭包(block)传入inDatabase方法中
。 在闭包中操作数据库,而不直接参与FMDatabase的管理。
FMDatabaseQueue * queue = [FMDatabaseQueue databaseQueueWithPath:database_path];
dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL);
dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL);
dispatch_async(q1, ^{
for (int i = 0; i < 50; ++i) {
[queue inDatabase:^(FMDatabase *db2) {
NSString *insertSql1= [NSString stringWithFormat:
@"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)",
TABLENAME, NAME, AGE, ADDRESS];
NSString * name = [NSString stringWithFormat:@"jack %d", i];
NSString * age = [NSString stringWithFormat:@"%d", 10+i];
BOOL res = [db2 executeUpdate:insertSql1, name, age,@"济南"];
if (!res) {
NSLog(@"error to inster data: %@", name);
} else {
NSLog(@"succ to inster data: %@", name);
}
}];
}
});
dispatch_async(q2, ^{
for (int i = 0; i < 50; ++i) {
[queue inDatabase:^(FMDatabase *db2) {
NSString *insertSql2= [NSString stringWithFormat:
@"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)",
TABLENAME, NAME, AGE, ADDRESS];
NSString * name = [NSString stringWithFormat:@"lilei %d", i];
NSString * age = [NSString stringWithFormat:@"%d", 10+i];
BOOL res = [db2 executeUpdate:insertSql2, name, age,@"北京"];
if (!res) {
NSLog(@"error to inster data: %@", name);
} else {
NSLog(@"succ to inster data: %@", name);
}
}];
}
});
demo下载
ios 上 sqlite的第三方封装库介绍
https://github.com/ccgus/fmdb ========
Delphi for iOS开发指南-在iOS客户端应用程序中连接企业级数据库
这篇教程描述了在iOS客户端应用程序中连接企业级数据库。(Delphi)
要连接到企业级数据库,你需要一个客户端的库。在多数情况下,客户端库由数据库供应商以DLL文件格式提供。这个方式不能在iOS
Device上运行,因为没有可用的客户端库。要解决这个问题,你可以开发一个中间层来连接到一个企业级数据库,你的iOS应用程序可以
与这个中间层会话。
RAD Studio提供DataSnap框架来让你不需要写代码就能够开发这个中间层(并且访问这个中间层)。这篇教程描述了开发这个中间层,
以及开发iOS客户端的步骤。
创建中间层,一个DataSnap Server
首先,创建一个DataSnap Server,从数据库数据器导出一个表。这篇教程使用一个DataSnap Server VCL Forms Application作为
DataSnap Server。
注:在这篇教程中,DataSnap Server(VCL应用程序)在多层数据库应用程序中作为中间层。你可以轻松创建并删除一个DataSnap
Server。在你理解这些基本步骤之后中,你可以将这个中间层转换成Windows服务程序。
创建一个DataSnap Server VCL Application
1. 创建一个新的工程。选择File>New>Other,然后从New Items对话框中选择Delphi Projects>Data Snap Server>DataSnap Server来
创建一个新的Delphi工程。
2.弹出New DataSnap Server向导,你需要按照这些步骤,不需要修改太多参数。
在New DataSnap Server向导中:
1. 第一步,选择VCL Forms Application作为应用程序类型。
2. 第二步,选择TCP/IP协议,Server Methods Class,和Sample Methods。
3.第三步,保持默认的TCP/IP通讯端口为211。这会保证客户端和服务端的通讯通过默认的DataSnap端口。
4. 最后一步(第四)选择TDSServerModule作为Server Methods的父类。
3. 保存窗体单元为DataSnapServerUnit.pas。
4. 切换到DataSnapServerUnit,更改窗体的Name属性为DSServerForm。
5. 保存Server Mothods单元(向导默认创建为:ServerMethodsUnit1)为ServerModuleUnit.pas。
6. 保存Server Containter单元(向导默认创建为:ServerContainerUnit1)为ServerContainerUnit.pas。
7. 保存新的工程为DataSnapServerProject.droj。
8. 在Project Manager中选择ProjectGroup1,然后保存这个工程为DataSnapTutorialProjectGroup.groupproj。
在DataSnap Server上定义一个数据集
1. 切换到ServerContainerUnit.pas文件,然后替换在Implementation中的Uses子句为:uses Winapi.Windows, ServerModuleUnit;
2.切换到ServerModuleUnit.pas文件。
3.在Form Designer中,更改Server Module的Name属性为DSServerModule_EMPLOYEE。
4.在Server Module上配置如下组件:
.拖放一个TSQLConnection组件在Server Module,然后设置如下属性:
TSQLConnection封装了一个数据库服务器的dbExpress连接。
.设置Name属性为SQLConnection_EMPLOYEE。
.设置LoginPrompt属性为False。
.设置Driver为InterBase。
.展开Drive节点,设置DataBase属性为C:\Users\Public\Documents\RAD Studio\11.0\Samples\Data\EMPLOYEE.GDB。
.更改Connected属性为True。如果你遇到一个错误,再次检查一下Driver属性。
.拖放一个TSQLDataSet组件到Server Module上,设置如下属性:
TSQLDataSet用来连接使用dbExpress接收到的数据。
.设置Name属性为SQLDataSet_EMPLOYEE。
.设置SQLConnection属性为SQLConnection_EMPLOYEE。
.设置CommandType属性为ctTable。
.设置CommandText属性为EMPLOYEE。
.更改Active属性为Ture。如果你遇到一个错误,再次查检你刚才配置过的属性。
.拖放一个TDataSetProvider组件到Server Module上,然后设置如下属性:
TDataSetProvider将一个数据集里面的数据打包,然后将这些可以传输的数据包到DataSnap客户端。
.设置Name属性为DataSetProvider_EMPLOYEE 。
.设置DataSet属性为SQLDataSet_EMPLOYEE :
注:这篇教程使用InterBase作为例子。然而,你可以连接到任意数据库服务器,使用相同的步骤。选择合适的Driver,以及其他属性指
向你的数据库。
从DataSnap Server导出DataSet
你刚刚创建一个新的Server Module,包含一个DataSet和DataSetProvider(将数据打包传递给下一层)。下一步是导出这个Server
Module到DataSnap Client。
1. 在Form Designer中,打开ServerContainerUnit。
2. 选择DSServerClass1,然后更新已经存在的OnGetClass事件的事件处理过程。添加如下代码到DSServerClass1的事件处理过程中:
[delphi]
procedure TServerContainer1.DSServerClass1GetClass(DSServerClass: TDSServerClass;
var PersistentClass: TPersistentClass);
begin
PersistentClass := TDSServerModule_EMPLOYEE;
end;
[delphi]
procedure TServerContainer1.DSServerClass1GetClass(DSServerClass: TDSServerClass;
var PersistentClass: TPersistentClass);
begin
PersistentClass := TDSServerModule_EMPLOYEE;
end;
使用这个事件处理过程,DataSnap Server会导出DataSetProvider,就像这个类中的公共方法一样给一个DataSnap Client。基于上一节
你所做的,现在你要导出这个DataSetProvider_EMPLOYEE到你的DataSnap Client。
运行DataSnap Server
DataSnap Server的实现已经完成。右击DataSnapServerProject.exe,然后选择Run Without Debugging。
现在你可以看到DataSnap Server运行在你的Windows电脑上。因为这个DataSnap Server没有任何界面元素,因此它是一个空窗体:
创建一个iOS应用程序连接到DataSnap Server
下一步,创建iOS客户端应用程序
1.在Project Manager中,右击DataSnapTutorialProjectGroup,然后选择Add New Project。
2.在Delphi Projects页选择FireMonkey Mobile Application:
3.保存新的单元为DataSnapClientUnit.pas。
4.保存新的工程为DataSnapClientProject.droj。
5.打开DataSnapClientUnit,然后更改窗体的Name属性为DSClientForm。
6.拖放以下组件到FireMonkey Mobile Form Designer上:
.TSQLConnection组件(SQLConenction1)
TSQLConnction封装了一个与数据库服务器的dbExpress连接。当然,它也支持DataSnap Server。
.设置Driver属性为DataSnap。
.展开Driver属性,然后设置HostName属性为DataSnap Server的主机名。
.设置LoginPrompt属性为False。
.设置Connected属性为True。
如果你遇到一个错误,请再次检查你刚设置的属性。
.TDSProviderConnection组件(DSProviderConnection1)
TDSProviderConnection组件使用dbExpress提供与DataSnap Server的连接。
.设置SQLConnection属性为SQLConnection1。
.设置ServerClassName为TDSServerModule_EMPLOYEE。这个名称需要与DataSnap Server里的Server Module的类名匹配。
.设置Connected属性为True。
.TClientDataSet组件(ClientDataSet1)
TClientDataSet实现了一个与数据库无关的数据集,它可以用作一个其他数据集的本地内存缓冲。
.设置RemoteServer属性为DSProviderConnection1。
.设置ProviderName属性为DataSetProvider_EMPLOYEE。这个名称需要与DataSnap Server里的DataSetProvider的名称匹配。
.设置Active属性为True。
.TListBox组件
.设置Align属性为alClient。
7.打开LiveBindings Designer,按照下图连接数据和用户界面:
1. 点击BindSourceDB1中的FULL_NAME,然后拖动鼠标指针到ListBox1的Item.Text上去:
2. 现在你已经创建并配置了一个在iOS上的DataSnap Client了。你应该能够在IDE中看到从DataSnap Server中的数据了:
布署MIDAS库到iOS Simulator
要在iOS Simulator中执行你的应用程序,你需要布置下列文件:
1.MIDAS库
2.通过选择Project>Deployment来打开Deployment Manager。
3.选择Add Featured Files():
4.选择如下模板,然后点击OK,关闭Deployment Manager:
.MIDAS库
在iOS模拟器,或iOS Device上运行你的应用程序
现在你的应用程序可以准备运行了。
在Project Manager中,选择iOS Simulator或iOS Device作为目标平台,然后运行你的应用程序。你应该可以浏览到数据。
========
iOS开发数据库篇—SQL代码应用示例
一、使用代码的方式批量添加(导入)数据到数据库中
1.执行SQL语句在数据库中添加一条信息
插入一条数据的sql语句:
点击run执行语句之后,刷新数据
2.在ios项目中使用代码批量添加多行数据示例
代码示例:
复制代码
//
// main.m
// 01-为数据库添加多行数据
//
// Created by apple on 14-7-26.
// Copyright (c) 2014年 wendingding. All rights reserved.
//
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[])
{
@autoreleasepool {
NSArray *names=@[@"张一",@"张二",@"张三",@"张四"];
NSMutableString *sql=[NSMutableString string];
for (int i=0; i<200; i++) {
int ID=i+1;
//这里的警告为无符号类型转换
NSString *name=names[arc4random_uniform(names.count)];
name=[name stringByAppendingFormat:@"-%d",arc4random_uniform(200)];
//生成随机数,范围以20为中心上下波动10
int age=arc4random_uniform(20)+10;
[sql appendFormat:@"INSERT INTO t_student (id,name,age) VALUES (%d,'%@',%d);\n",ID,name,age];
}
//把sql写入到文件中
[sql writeToFile:@"/Users/apple/Desk/students.sql" atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"\n%@",sql);
}
return 0;
}
作用:生成200条相应的sql插入语句
打印结果为:
使用文本编辑器,打开生成的sql文件。
可以把这些SQL语句拷贝到Navicat中进行执行,也可以直接执行整个文件。
在数据库中创建一张表:
选择执行SQL文件:
执行完毕后,点击cancel。
刷新数据库,查看插入的200条数据
二、分页简单演示
说明:
使用limit可以精确地控制查询结果的数量,比如每次只查询10条数据
格式 select * from 表名 limit 数值1, 数值2 ;
示例
select * from t_student limit 4, 8 ;
可以理解为:跳过最前面4条语句,然后取8条记录
limit常用来做分页查询,比如每页固定显示5条数据,那么应该这样取数据
第1页:limit 0, 5
第2页:limit 5, 5
第3页:limit 10, 5
…
第n页:limit 5*(n-1), 5
下面语句的作用
select * from t_student limit 7 ;
相当于select * from t_student limit 0, 7 ;表示取最前面的7条记录
三、补充
1.关于外键约束(建立起两张表之间的联系)
第一种做法:可以新建一张关系表,让之前两张表(班级表和学生表建立起对应的联系),但是这种做法很冗余,没有必要
第二种做法:使用外键约束
一对一,一对多,多对多关系。当两张表有联系时,如何设置外键(在哪张表中设置?)
2.关于表连接
子查询:要求查询所有类型为粤菜的菜名。
查询结果为:
连接查询:
查询结果为:
========
经典SQL语句大全
1.常用语句
***SQL语句的预编译:将语句转为数据流,执行语句前检查语句的语法,但不能知道语句是否能查出结果。此方法有返回值 预编译成功
则返回SQLITE_OK----0否则返回SQLITE_ERROR----1
int sqlite3_prepare_v2(
sqlite3 *db, //指向数据库的指针
const char *zSql, //SQL语句
int nByte, //SQL语句的长度 (一般用-1,系统可以自动算出字符串得长度)
sqlite3_stmt **ppStmt, //指向语句的指针
const char **pzTail //SQL中没有用到的一部分,一般为空
);
***SQL语句的值绑定。
*此类方法均有返回值 预编译成功则返回SQLITE_OK----0否则返回SQLITE_ERROR----1
//对整数值的绑定
int sqlite3_bind_int(
sqlite3_stmt*, //指向语句的指针
int, //占位符的序号(从1开始以此类推)
int //绑定的值
);
//对字符串值的绑定
int sqlite3_bind_text(
sqlite3_stmt*, //指向语句的指针
int, //占位符的序号(从1开始以此类推)
const char*, //要绑定的值(这里要c类型的字符串(CString),一般我们使用的是oc的字符串(NSString*)要通过-
(__strong const char *)UTF8String转一下就可以了)
int n, //该字符串的长度(一般用-1,系统可以自动算出字符串得长度)
void(*)(void*) //回调函数(这里博主还没用过所以不是很了解以后会慢慢更新)
);
//对二进制数据值的绑定
int sqlite3_bind_blob(
sqlite3_stmt*, //指向语句的指针
int, //占位符的序号(从1开始以此类推)
const void*, //要绑定的值(这里要的是c类型的比特(Byte),一般我们使用的是oc的NSDATA(NsData*)要通过-
(const void *)bytes转一下就可以了)
int n, //数据的长度(用- (NSUInteger)length得到 这里为什么不用-1呢?因为-1对于算字符串长度在行,其
他的不一定准所以这里我们手动算出数据的长度)
void(*)(void*) //回调函数(这里博主还没用过所以不是很了解以后会慢慢更新)
);
***SQL语句的取值(从数据库中对每一列取值)
//对整数字段的取值
int sqlite3_column_int(
sqlite3_stmt*, //指向语句的指针
int iCol //数据库中表中列的序号(从0开始以此类推)
);
//对字符串字段的取值
*注意这里我们取得是无符号的c字符串我们要先转为有符号C字符串再转为OC字符串
例sname为oc字符串
//对二进制字段的取值
NSString *sname=[NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];
const unsigned char *sqlite3_column_text(
sqlite3_stmt*, //指向语句的指针
int iCol //数据库中表中列的序号(从0开始以此类推)
);
NSString *sname=[NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];
const unsigned char *sqlite3_column_text(
sqlite3_stmt*, //指向语句的指针
int iCol //数据库中表中列的序号(从0开始以此类推)
);
*注意这里我们取得是c二进制我们要转为oc二进制(我们要将byte(c)转为nsdata(oc)通过+ (id)dataWithBytes:(const void *)
bytes length:(NSUInteger)length;因为这里需要数据的长度所以需要调用int sqlite3_column_bytes(sqlite3_stmt*, int iCol);获
取数据的长度)
例
int length=sqlite3_column_bytes(stmt,3);//获取二进制数据的长度
NSData *img=[NSData dataWithBytes:sqlite3_column_blob(stmt, 3) length:length]; //将二进制数据转换位NSData对象
const void *sqlite3_column_blob(
sqlite3_stmt*, //指向语句的指针
int iCol //数据库中表中列的序号(从0开始以此类推)
);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
******************************************新建一个类(Student*)
包含以下属性
@property(retain,nonatomic)NSString*sname;
@property(assign,nonatomic)int sage;
@property(retain,nonatomic)NSData*simage;
@property(assign,nonatomic)int sid;
1.数据库的全表查询
+(NSMutableArray*) findall//返回表中所有记录
{
NSMutableArray* stuArray;//存储查询结果 在这里只声明不开辟空间 等用的时候再开辟(节省空间)
sqlite3 *sqlite=[DataBase OpenDB];//打开数据库 并且定义了一个指针sqlite指向dbpoint所指向的区域(数据库)
sqlite3_stmt *stmt=nil;//定义一个指向sql语句的指针对象
int flag=sqlite3_prepare_v2(sqlite, "select * from tblstudent", -1, &stmt, nil);//调用预处理函数将sql语句赋值给stmt
对象 sqlite:数据库 -1:系统自动算出要存的sql语句的长度(也可以自己给出) &stmt:一个指向sql语句的内存的地址 nil:sql
语句中没有用到的一部分(一般为空) 返回值为一个int(宏)SQLITE_OK 0 SQLITE_ERROR 1
i
当前行第二列的值
f (flag==SQLITE_OK) //预编译成功
{
stuArray=[[NSMutableArray alloc]init];//为数组开辟空间
while (sqlite3_step(stmt)==SQLITE_ROW) //开始指向第一行的上面 判断下一行是否存在(存在:做准备指针移到下一行)
(不存在:跳出循环)
{
int sid1=sqlite3_column_int(stmt, 0);//获取表中当前行第一列的值
int sage=sqlite3_column_int(stmt, 2);
NSString *sname1=[NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];//获取当前表中
//sqlite3_column_text(stmt, 1):返回的是一个无符号c字符串 (const char *)进行强转为有符号c字符串
stringWithUTF8String:将一个c字符串转为nsstring类型
int length=sqlite3_column_bytes(stmt,3);//获取二进制数据的长度 sqlite3_column_bytes():获取当前行某一列的
所占内存的字节数
NSData *img1=[NSData dataWithBytes:sqlite3_column_blob(stmt, 3) length:length]; //将二进制数据转换位NSData
对象
//sqlite3_column_blob(stmt, 2):返回的是byte型(c) dataWithBytes:length:将(c)二进制转为nsdata(oc)
//定义一个(Student*)对象并将从数据库取来的数据赋值给对象的相应属性
Student *stu=[[Student alloc]init];//实例化
stu.sid=sid1;
stu.sname=sname1;
stu.simage=img1;
stu.sage=sage;
[stuArray addObject:stu];//将一个对象存入数组
[stu release];//释放对象
}
}
sqlite3_finalize(stmt);//回收stmt对象
return [stuArray autorelease];//返回包含学生信息的数组 并设为自动释放
}
2.通过姓名或者学号查询
*注意这里学号是主键所以肯定查回来是有唯一确定的(Student*)对象
+(Student*) findbysid:(int) sid
{
Student *stu;//用于返回的学生对象
sqlite3 *sqlite=[DataBase OpenDB];//打开数据库
sqlite3_stmt *stmt=nil;//定义sql语句对象
int flag=sqlite3_prepare_v2(sqlite, "select * from tblstudent where stuid=?", -1, &stmt, nil);//调用预处理函数将sql
语句赋值给stmt对象
if (flag==SQLITE_OK)
{
sqlite3_bind_int(stmt, 1, sid);//给问号占位符赋值
while (sqlite3_step(stmt)==SQLITE_ROW)//因为一个学号肯定对应一个学生所以这里其实只执行一次
{
//根据列顺序(从零开始)
int sid=sqlite3_column_int(stmt, 0);//取整型数据
int sage=sqlite3_column_int(stmt, 2);
NSString *sname=[NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];//取nsstring数据
int length=sqlite3_column_bytes(stmt,3);//获取二进制数据的长度
NSData *img=[NSData dataWithBytes:sqlite3_column_blob(stmt, 3) length:length]; //将二进制数据转换位NSData对
象
//将从数据库中查询来的数据存到(Student*)对象中,用于返回
stu=[[Student alloc]init];//此时开辟空间节省内存
stu.sid=sid;
stu.sname=sname;
stu.simage=img;
stu.sage=sage;
}
}
sqlite3_finalize(stmt);//回收stmt对象
return [stu autorelease];//返回包含学生信息的对象
}
*注意这里一般学生姓名并不是唯一标示所以可能会查到多条记录符合条件所以要返回一个数组来存储符合条件的(Student*)对象 但
现在我们的数据库不涉及重名的事例所以这里只返回学生对象
+(Student*) findbysname:(NSString*)sname
{
Student *stu;//用于返回的学生对象
sqlite3 *sqlite=[DataBase OpenDB];//打开数据库
sqlite3_stmt *stmt=nil;//定义sql语句对象
int flag=sqlite3_prepare_v2(sqlite, "select * from tblstudent where stuname=?", -1, &stmt, nil);//调用预处理函数将
sql语句赋值给stmt对象
if (flag==SQLITE_OK)
{
sqlite3_bind_text(stmt, 1, [sname UTF8String], -1, nil);//给问号占位符赋值
while (sqlite3_step(stmt)==SQLITE_ROW)
{
//根据列顺序(从零开始)
int sid=sqlite3_column_int(stmt, 0);//取整型数据
int sage=sqlite3_column_int(stmt, 2);
NSString *sname=[NSString stringWithUTF8String:(const char *)sqlite3_column_text(stmt, 1)];//取nsstring数据
int length=sqlite3_column_bytes(stmt,3);//获取二进制数据的长度
NSData *img=[NSData dataWithBytes:sqlite3_column_blob(stmt, 3) length:length]; //将二进制数据转换位NSData对
象
stu=[[Student alloc]init];//此时开辟空间节省内存
stu.sid=sid;
stu.sname=sname;
stu.simage=img;
stu.sage=sage;
}
}
sqlite3_finalize(stmt);//回收stmt对象
return [stu autorelease];//返回包含学生信息的(Student*)对象
}
3.向数据库增加一条记录
+(void) insertstudent:(NSString*) name age:(int)age image:(NSData*)image
{
sqlite3 *sqlite=[DataBase OpenDB];//打开数据库
sqlite3_stmt *stmt=nil;//定义sql语句对象
int flag=sqlite3_prepare_v2(sqlite, "insert into tblstudent(stuname,stuage,stuimage) values(?,?,?)", -1, &stmt,
nil);//调用预处理函数将sql语句赋值给stmt对象 多值绑定逗号隔开
if (flag==SQLITE_OK)
{
sqlite3_bind_text(stmt, 1, [name UTF8String],-1,nil);//给问号占位符赋值
sqlite3_bind_int(stmt, 2, age);//对整型数据的绑定
sqlite3_bind_blob(stmt, 3, [image bytes], [image length], nil);//对二进制类型数据的绑定 数据的字节数(-1 针对字
符串的长度比较准)
if(sqlite3_step(stmt)==SQLITE_ERROR)//执行insert动作
{
NSLog(@"insert error");
}
}
sqlite3_finalize(stmt);//回收stmt对象
}
4.修改更新数据库的数据
*注意更新数据库时要通过一定的限制条件来确定要更新的记录,可能是一条也可能是多条,当要更新的记录是多条时要注意所有符合条
件的记录都会改为你定义的值。
+(void) updatestudent:(Student*) stu
{
sqlite3 *sqlite=[DataBase OpenDB];//打开数据库
sqlite3_stmt *stmt=nil;//定义sql语句对象
int flag=sqlite3_prepare_v2(sqlite, "update tblstudent set stuname=?,stuage=?,stuimage=? where stuid=?", -1, &stmt,
nil);//调用预处理函数将sql语句赋值给st
mt对象
5.删除数据库的记录
if (flag==SQLITE_OK)
{
sqlite3_bind_text(stmt, 1, [stu.sname UTF8String],-1,nil);//给问号占位符赋值
//stu.sname为nsstring UTF8String nsstring---->ctring
sqlite3_bind_int(stmt, 4, stu.sid);//给问号占位符赋值
sqlite3_bind_int(stmt, 2, stu.sage);//给年龄赋值
sqlite3_bind_blob(stmt, 3, [stu.simage bytes], [stu.simage length], nil);
if(sqlite3_step(stmt)==SQLITE_ERROR)//执行update动作
{
NSLog(@"update error");
}
sqlite3_finalize(stmt);//回收stmt对象
}
}
if (flag==SQLITE_OK)
{
sqlite3_bind_text(stmt, 1, [stu.sname UTF8String],-1,nil);//给问号占位符赋值
//stu.sname为nsstring UTF8String nsstring---->ctring
sqlite3_bind_int(stmt, 4, stu.sid);//给问号占位符赋值
sqlite3_bind_int(stmt, 2, stu.sage);//给年龄赋值
sqlite3_bind_blob(stmt, 3, [stu.simage bytes], [stu.simage length], nil);
if(sqlite3_step(stmt)==SQLITE_ERROR)//执行update动作
{
NSLog(@"update error");
}
sqlite3_finalize(stmt);//回收stmt对象
}
}
*注意删除数据库的记录时要通过一定的限制条件来确定要删除的记录,可能是一条也可能是多条,当要删除的记录是多条时要注意所有
符合条件的记录都会被删除。
+(void) deletebysid:(int) sid
{
sqlite3 *sqlite=[DataBase OpenDB];//打开数据库
sqlite3_stmt *stmt=nil;//定义sql语句对象
int flag=sqlite3_prepare_v2(sqlite, "delete from tblstudent where stuid=?", -1, &stmt, nil);//调用预处理函数将sql语
句赋值给stmt对象
if (flag==SQLITE_OK)
{
sqlite3_bind_int(stmt, 1, sid);//给问号占位符赋值 1.语句2.占位符的序号3.给占位符赋得值
//执行delete动作
if(sqlite3_step(stmt)==SQLITE_ERROR)//未执行成功
{
NSLog(@"delete error");
}
}
sqlite3_finalize(stmt);//回收stmt对象
NSLog(@"删除了第%d",sid);
}
========
IOS 四种保存数据的方式
在iOS开发过程中,不管是做什么应用,都会碰到数据保存的问题。将数据保存到本地,能够让程序的运行更加 流畅,不会出现让人厌恶的
菊花形状,使得用户体验更好。下面介绍⼀一下数据保存的方式:
1.NSKeyedArchiver:采用归档的形式来保存数据,该数据对象需要遵守NSCoding协议,并且该对象对应的类必须提 供encodeWithCoder:和
initWithCoder:方法。前⼀一个方法告诉系统怎么对对象进行编码,而后⼀一个方法则是告诉系统怎 么对对象进行解码。例如对
Possession对象归档保存。
定义Possession:
@interface Possession:NSObject{//遵守NSCoding协议
NSString *name;//待归档类型 }
@implementation Possession -(void)encodeWithCoder:(NSCoder *)aCoder{
[aCoder encodeObject:name forKey:@"name"]; }
-(void)initWithCoder:(NSCoder *)aDecoder{
name=[[aDeCoder decodeObjectforKey:@"name"] retain];
}
归档操作: 如果对Possession对象allPossession归档保存,只需要NSCoder子类NSKeyedArchiver的方法 archiveRootObject:toFile: 即
可。
NSString *path = [self possessionArchivePath];
[NSKeyedArchiver archiveRootObject:allPossessions toFile: path ]
解压操作: 同样调用NSCoder子类NSKeyedArchiver的方法unarchiveRootObject:toFile: 即可 allPossessions = [[NSKeyedUnarchiver
unarchiveObjectWithFile:path] retain];
缺点:归档的形式来保存数据,只能⼀一次性归档保存以及⼀一次性解压。所以只能针对小量数据,而且对数据操作比较 笨拙,即如果想改
动数据的某⼀一小部分,还是需要解压整个数据或者归档整个数据。 2.NSUserDefaults:用来保存应用程序设置和属性、用户保存的数据
。用户再次打开程序或开机后这些数据仍然存 在。NSUserDefaults可以存储的数据类型包括:NSData、NSString、NSNumber、NSDate、
NSArray、 NSDictionary。如果要存储其他类型,则需要转换为前面的类型,才能用NSUserDefaults存储。具体实现为:
保存数据:
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults]; NSString *name =@”default string“;
[defaults setObject:firstName forKey:@"name"];
//获得UIImage实例
UIImage *image=[[UIImage alloc]initWithContentsOfFile:@"photo.jpg"];
NSData *imageData = UIImageJPEGRepresentation(image, 100);//UIImage对象转换成NSData [defaults synchronize];//用
synchronize方法把数据持久化到standardUserDefaults数据库
读取数据:
NSUserDefaults *defaults =[NSUserDefaults standardUserDefaults];
NSString *name = [defaults objectForKey:@"name"];//根据键值取出name
NSData *imageData = [defaults dataForKey:@"image"];
UIImage *Image = [UIImage imageWithData:imageData];//NSData转换为UIImage
3. Write写入方式:永久保存在磁盘中。具体方法为:
第⼀一步:获得文件即将保存的路径:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask,YES);//使用C函数
NSSearchPathForDirectoriesInDomains来获得沙盒中目录的全路
径。该函数有三个参数,目录类型、he domain mask、布尔值。其中布尔值表示是否需要通过?~扩展路径。 而且第⼀一个参数是不变的
,即为NSSearchPathDirectory 。在IOS中后两个参数也是不变的,即为:
NSUserDomainMask 和 YES。
NSString *ourDocumentPath =[documentPaths objectAtIndex:0];
还有⼀一种方法是使用NSHomeDirectory函数获得sandbox的路径。具体的用法为:
NSString *sandboxPath = NSHomeDirectory();
// Once you have the full sandbox path, you can create a path from it,但是不能在sandbox的本文件层上写 文件也不能创建目
录,而应该是此基础上创建⼀一个新的可写的目录,例如Documents,Library或者temp。 NSString *documentPath = [sandboxPath
stringByAppendingPathComponent:@"Documents"];//将Documents添加到sandbox路径上,具体原 因前面分析了!
这两者的区别就是:使用NSSearchPathForDirectoriesInDomains比在NSHomeDirectory后面添加Document更加安全。因为该文件目 录可
能在未来发送的系统上发生改变。
第二步:生成在该路径下的文件:
NSString *FileName=[documentDirectory stringByAppendingPathComponent:fileName];//fileName就是保存文件的文件 名
第三步:往文件中写入数据:
[data writeToFile:FileName atomically:YES];//将NSData类型对象data写入文件,文件名为FileName
最后:从文件中读出数据:
NSData data=[NSData dataWithContentsOfFile:FileName options:0 error:NULL];//从FileName中读取出数据
4. SQLite:采用SQLite数据库来存储数据。SQLite作为⼀一中小型数据库,应用ios中,跟前三种保存方式相比,相对 比较复杂⼀一些。还
是⼀一步步来吧!
第⼀一步:需要添加SQLite相关的库以及头文件:在项目文件的Build Phases下,找到Link Binary Library(ies),添加
libsqlite3.0.dylib(libsqlite3.dylib与前者的区别暂时不知,两者应该差不多);在项目文件中头文件或者源文件中添加头文件
第二步:开始使用SQLite:
#import "/usr/include/sqlite3.h"
使用前注意:如果不往数据库里面添加任何的表,这个数据库等于没有建立,不会在硬盘上产生任何文件,如果数据库已经 存在,则会打开
这个数据库。
注意:写入数据库,字符串可以采用char方式,而从数据库中取出char类型,当char类型有表示中文字符 时,会出现乱码。这是因为数据库
NSArray *documentsPaths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask , YES);
NSString *databaseFilePath=[[documentsPaths objectAtIndex:0] stringByAppendingPathComponent:@"mydb"];
//上面两句已经比较熟悉了吧!
//打开数据库
if (sqlite3_open([databaseFilePath UTF8String], &database)==SQLITE_OK) {
NSLog(@"sqlite dadabase is opened."); }
else{ return;}//打开不成功就返回
在打开了数据库的前提下,如果数据库没有表,那就开始建表了哦!
char *error;
const char *createSql="create table(id integer primary key autoincrement, name text)"; if (sqlite3_exec(database,
createSql, NULL, NULL, &error)==SQLITE_OK) {
NSLog(@"create table is ok."); }
else {
sqlite3_free(error);//每次使用完毕清空error字符串,提供给下⼀一次使用 }
建表完成之后,就开始插入记录:
const char *insertSql="insert into a person (name) values(‘gg’)";
if (sqlite3_exec(database, insertSql, NULL, NULL, &error)==SQLITE_OK) {
NSLog(@"insert operation is ok."); }
else {
NSLog(@"error: %s",error);
NSLog(@"error: %s",error);
sqlite3_free(error);//每次使用完毕清空error字符串,提供给下⼀一次使用 }
下⼀一步,查询记录:
const char *selectSql="select id,name from a person";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(database,selectSql, -1, &statement, nil)==SQLITE_OK) {
NSLog(@"select operation is ok."); }
else {
sqlite3_free(error); }
while(sqlite3_step(statement)==SQLITE_ROW) {
int _id=sqlite3_column_int(statement, 0);
NSString *name=(char*)sqlite3_column_text(statement, 1); NSLog(@"row>>id %i, name %s",_id,name);
}
sqlite3_finalize(statement);
最后,关闭数据库:
sqlite3_close(database);
默认使用ascII编码方式。所以要想正确从数据库中取出中文,需要用 NSString来接收从数据库取出的字符串。
========