由于在iPhone3.0上已经支持了Core Data,是苹果一个新的API,并且是基于SQlite的。速度也是非常快吧。所以我们对SQLite仅需要懂一些即可,以下是一些基础信息
一、:常用接口(2个重要结构体和5个主要函数)
sqlite3
*pdb, 数据库句柄,跟文件句柄 FILE 很类似
sqlite3_stmt
*stmt, 这个相当于 ODBC 的 Command 对象,用于保存编译好的 SQL
语句
sqlite3_open(), 打开数据库
sqlite3_exec(), 执行非查询的 sql 语句
sqlite3_prepare(), 准备 sql 语句,执行 select 语句或者要使用 parameter bind
时,用这个函数(封装了 sqlite3_exec ) .
Sqlite3_step(), 在调用 sqlite3_prepare 后,使用这个函数在记录集中移动。
Sqlite3_close(), 关闭数据库文件
还有一系列的函数,用于从记录集字段中获取数据,如
sqlite3_column_text(), 取 text 类型的数据。
sqlite3_column_blob (),取 blob 类型的数据
sqlite3_column_int(), 取 int 类型的数据
其他工具函数
1. 得到结果总共的行数
int sqlite3_data_count(sqlite3_stmt *pStmt);
如果过程没有返回值,如update,将返回0
2. 得到当前行中包含的数据个数
int sqlite3_column_count(sqlite3_stmt *pStmt);
如果sqlite3_step返回SQLITE_ROW,可以得到列数,否则为零。
3. 得到数据行中某个列的数据
sqlite3_column_xxx(sqlite3_stmt*, int iCol);
在sqlite3_step返回SQLITE_ROW后,使用它得到第iCol列的数据。
其中的xxx代表:
blob:指向保存数据内存的指针
bytes, bytes16: 得到该blob类型数据的大小,或者text转换为UTF8/UTF16的字符串长度。
double, int, int64: 数值
text,text16:字符串指针
type:该列的数据类型(SQLITE_INTEGER,SQLITE_FLOAT,SQLITE_TEXT,SQLITE_BLOB,SQLITE_NULL)
注意:如果对该列使用了不同与该列本身类型适合的数据读取方法,得到的数值将是转换过的结果。
4. 得到数据行中某个列的数据的类型
int sqlite3_column_type(sqlite3_stmt*, int iCol);
返回值:SQLITE_INTEGER,SQLITE_FLOAT,SQLITE_TEXT,SQLITE_BLOB,SQLITE_NULL
使用的方法和sqlite3_column_xxx()函数类似。
二、:sqlite数据类型介绍
在进行数据库Sql操作之前,首先有个问题需要说明,就是Sqlite的数据类型,和其他的数据库不同,Sqlite支持的数据类型有他自己的特色,这个特色有时会被认为是一个潜在的缺点,但是这个问题并不在我们的讨论范围之内。
大多数的数据库在数据类型上都有严格的限制,在建立表的时候,每一列都必须制定一个数据类型,只有符合该数据类型的数据可以被保存在这一列当中。而在
Sqlite
2.X中,数据类型这个属性只属于数据本生,而不和数据被存在哪一列有关,也就是说数据的类型并不受数据列限制(有一个例外:INTEGER
PRIMARY KEY,该列只能存整型数据)。
但是当Sqlite进入到3.0版本的时候,这个问题似乎又有了新的答案,Sqlite的开发者开始限制这种无类型的使用,在3.0版本当中,每一列开始
拥有自己的类型,并且在数据存入该列的时候,数据库会试图把数据的类型向该类型转换,然后以转换之后的类型存储。当然,如果转换被认为是不可行
的,Sqlite仍然会存储这个数据,就像他的前任版本一样。
举个例子,如果你企图向一个INTEGER类型的列中插入一个字符串,Sqlite会检查这个字符串是否有整型数据的特征,
如果有而且可以被数据库所识别,那么该字符串会被转换成整型再保存,如果不行,则还是作为整型存储。
总的来说,所有存在Sqlite 3.0版本当中的数据都拥有以下之一的数据类型:
空(NULL):该值为空
整型(INTEGEER):有符号整数,按大小被存储成1,2,3,4,6或8字节。
实数(REAL):浮点数,以8字节指数形式存储。
文本(TEXT):字符串,以数据库编码方式存储(UTF-8, UTF-16BE 或者 UTF-16-LE)。
BLOB:BLOB数据不做任何转换,以输入形式存储。
ps:
在关系数据库中,CLOB和BLOB类型被用来存放大对象。BOLB表示二进制大对象,这种数据类型通过用来保存图片,图象,视频等。CLOB表示字符大对象,能够存放大量基于字符的数据。
对应的,对于数据列,同样有以下的数据类型:
TEXT
NUMERIC
INTEGER
REAL
NONE
数据列的属性的作用是确定对插入的数据的转换方向:
TEXT 将数据向文本进行转换,对应的数据类型为NULL,TEXT 或 BLOB
NUMERIC 将数据向数字进行转换,对应的数据类型可能为所有的五类数据,当试图存入文本
时将执行向整型或浮点类型的转换(视具体的数值而定),转换若不可行,则保留文本类型存储,NULL或BLOB不做变化
INTEGER 将数据向整型转换,类似于NUMERIC,不同的是没有浮点标志的浮点数将转换为整型保存
REAL 将数据向浮点数类型转换,类似于NUMERIC,不同的是整数将转换为浮点数保存
NULL 不做任何转换的数据列类型
内部类型 |
请求的类型 |
转换 |
NULL |
INTEGER |
结果是0 |
NULL |
FLOAT |
结果是0.0 |
NULL |
TEXT |
结果是NULL |
NULL |
BLOB |
结果是NULL |
INTEGER |
FLOAT |
从整形转换到浮点型 |
INTEGER |
TEXT |
整形的ASCII码显示 |
INTEGER |
BLOB |
同上 |
FLOAT |
INTEGER |
浮点型转换到整形 |
FLOAT |
TEXT |
浮点型的ASCII显示 |
FLOAT |
BLOB |
同上 |
TEXT |
INTEGER |
使用atoi() |
TEXT |
FLOAT |
使用atof() |
TEXT |
BLOB |
没有转换 |
BLOB |
INTEGER |
先到TEXT,然后使用atoi |
BLOB |
FLOAT |
先到TEXT,然后使用atof |
BLOB |
TEXT |
如果需要的话添加0终止符 |
在iPhone中使用Sqlite 3主要步骤如下:
1 首先获取iPhone上Sqlite 3的数据库文件的地址
2 打开Sqlite 3的数据库文件
3 定义SQL文
4 邦定执行SQL所需要的参数
5 执行SQL文,并获取结果
6 释放资源
7 关闭Sqlite 3数据库。
// 首先获取iPhone上Sqlite3的数据库文件的地址
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"database_name"];
// 打开Sqlite3的数据库文件
sqlite3 *database;
sqlite3_open([path UTF8String], &database);
// 定义SQL文
sqlite3_stmt *stmt;
const char *sql = "SELECT * FROM table_name WHERE pk=? and name=?";
sqlite3_prepare_v2(database, sql, -1, &stmt, NULL);
// 邦定第一个int参数
sqlite3_bind_int(stmt, 1, 1);
// 邦定第二个字符串参数
sqlite3_bind_text(stmt, 2, [title UTF8String], -1, SQLITE_TRANSIENT);
// 执行SQL文,并获取结果
sqlite3_step(stmt);
// 释放资源
sqlite3_finalize(stmt);
// 关闭Sqlite3数据库
sqlite3_close(database);
三、objective-c 与 Sqlite
1、SQLite的开发包
注意:要在工程中的Frameworks中导入相应的libsqlite3.dylib文件,也许在相应的目录下存在多个以libsqlite3开头的文件,务必选择libsqlite3.dylib,它始终指向最新版的SQLite3库的别名。
libsqlite3.0.dylib文件地址:
/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS3.0.sdk/usr/lib/libsqlite3.dylib
2、创建数据库
//数据库的位置
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//打开数据库 如果没有则会创建一个新的数据库
sqlite3 *sql;
if(sqlite3_open([[documentsDirectory stringByAppendingPathComponent:@"data.sqlite3"] UTF8String],&sql)!=SQLITE_OK) //data.sqlite3为自己定义数据库名称
{
NSLog(@"创建数据库失败!");
return;
}
3、创建数据表
//创建一个新表 如果没有则会创一个新表
char *errorMsg;
NSString *createSQL = @"CREATE TABLE IF NOT EXISTS FIELDS (ROW INTEGER PRIMARY KEY, FIELD_DATA TEXT);";
if (sqlite3_exec (sql, [createSQL UTF8String],NULL, NULL, &errorMsg) != SQLITE_OK)
{
sqlite3_close(sql);
NSLog(@"创建数据表失败:%s",errorMsg);
}
4、插入更改数据
for (int i = 1; i <= 4; i++)
{
NSString *fieldName = [[NSString alloc] initWithFormat:@"field%d", i];
char *errorMsg;
char *update = "INSERT OR REPLACE INTO FIELDS (ROW, FIELD_DATA) VALUES (?, ?);";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(sql, update, -1, &stmt, nil) == SQLITE_OK)
{
//列1 绑定数据
sqlite3_bind_int(stmt, 1, i);
//列2 绑定数据
sqlite3_bind_text(stmt, 2, [fieldName UTF8String], -1, NULL);
}
if (sqlite3_step(stmt) != SQLITE_DONE)
{
NSAssert1(0, @"插入数据产生错误: %s", errorMsg);
}
sqlite3_finalize(stmt);
}
5、查询数据
NSString *query = @"SELECT ROW, FIELD_DATA FROM FIELDS ORDER BY ROW";
sqlite3_stmt *statement;
if (sqlite3_prepare_v2( sql, [query UTF8String],-1, &statement, nil) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW)
{
//列1
int row = sqlite3_column_int(statement, 0);
//列2
char *rowData = (char *)sqlite3_column_text(statement, 1);
NSString *fieldName = [[NSString alloc]
initWithFormat:@"%d", row];
NSString *fieldValue = [[NSString alloc]
initWithUTF8String:rowData];
NSLog(@"Row:%@ Data:%@",fieldName,fieldValue);
}
sqlite3_finalize(statement);
//关闭数据库
sqlite3_close(sql);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
打开数据库 另外一种方法:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"database.sqlite"];//database.sqlite为自己定义数据库名称
NSFileManager *fileManager = [NSFileManager defaultManager];
databasePath_ = path;
BOOL find = [fileManager fileExistsAtPath:path];
if (!find) {
NSString *rePath = getBundleFilePath(@"database", @"sqlite");
NSData *dataFile = [NSData dataWithContentsOfFile:rePath];
[dataFile writeToFile:databasePath_ atomically:YES];
}
NSLog(@"Database file have already existed.");
if(sqlite3_open([path UTF8String], &database_) != SQLITE_OK) {
sqlite3_close(database_);
NSLog(@"Error: open database file.");
}
四、简要说明一下SQLite数据库执行SQL语句的过程
** 调用sqlite3_prepare()将SQL语句编译为sqlite内部一个结构体(sqlite3_stmt).该结构体中包含了将要执行的的SQL语句的信息.
** 如果需要传入参数,在SQL语句中用'?'作为占位符,再调用sqlite3_bind_XXX()函数将对应的参数传入.
** 调用sqlite3_step(),这时候SQL语句才真正执行.注意该函数的返回值,SQLITE_DONE和SQLITE_ROW都是表示执行成功, 不同的是SQLITE_DONE表示没有查询结果,象UPDATE,INSERT这些SQL语句都是返回SQLITE_DONE,SELECT查询语句在 查询结果不为空的时候返回SQLITE_ROW,在查询结果为空的时候返回SQLITE_DONE.
** 每次调用sqlite3_step()的时候,只返回一行数据,使用sqlite3_column_XXX()函数来取出这些数据.要取出全部的数据需要 反复调用sqlite3_step(). (注意, 在bind参数的时候,参数列表的index从1开始,而取出数据的时候,列的index是从0开始).
** 在SQL语句使用完了之后要调用sqlite3_finalize()来释放stmt占用的内存.该内存是在sqlite3_prepare()时分配的.
** 如果SQL语句要重复使用,可以调用sqlite3_reset()来清楚已经绑定的参数
五、构建一个SqliteHelper类及其方法(详细的类 见代码总结文件SQLiteHelper)
写一个SQLite的Helper文件,方便我们在其他的代码中使用,头文件(SqliteHelper.h)如下。
#import
#import “sqlite3.h“
#define kFileName @”mydatabase.sql”
@interface SqliteHelper : NSObject {
sqlite3 *database;
}
//创建表
- (BOOL)createTable;
//插入数据
- (BOOL)insertMainTable:(NSString*) username insertPassword:(NSString*) password;
//查询表
- (BOOL)checkIfHasUser;
@end
我们的代码文件如下。
#import “SqliteHelper.h“
@implementation SqliteHelper
- (BOOL)createTable
{
NSArray *path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *paths = [[path objectAtIndex:0] stringByAppendingPathComponent:kFileName];
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL fileFinded = [fileManager fileExistsAtPath:paths];
NSLog(@”Database file path is %@“,paths);
if(fileFinded)
{
NSLog(@”Database file existed“);
if(sqlite3_open([paths UTF8String],&database)!=SQLITE_OK)
{
sqlite3_close(database);
NSLog(@”Open Failed“);
return NO;
}
}
else
{
NSLog(@”Database file is not existed“);
if(sqlite3_open([paths UTF8String],&database)!=SQLITE_OK)
{
sqlite3_close(database);
NSLog(@”Open Failed“);
return NO;
}
}
char *errorMsg;
NSString *createSQL = @”create table if not exists fields (userid integer primary key,username text,password text)“;
if(sqlite3_exec(database,[createSQL UTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
{
sqlite3_close(database);
NSLog(@”Open failed or init filed“);
return NO;
}
return YES;
}
- (BOOL)insertMainTable:(NSString*) username insertPassword:(NSString*) password
{
char *errorMsg;
NSString *createSQL = @”create table if not exists fields (userid integer primary key,username text,password text)“;
if(sqlite3_exec(database,[createSQL UTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
{
sqlite3_close(database);
NSLog(@”Open failed or init filed“);
return NO;
}
NSString *insertData = [[NSString alloc] initWithFormat:@”insert or replace into fields (userid,username,password) values (%d,’%@’,'%@’)“,0,username,password];
if(sqlite3_exec(database,[insertData UTF8String],NULL,NULL,&errorMsg)!=SQLITE_OK)
{
sqlite3_close(database);
NSLog(@”Open failed or failed to insert“);
return NO;
}
return YES;
}
- (BOOL)checkIfHasUser
{
NSString *getUserCountSQL = @”select * from fields“;
sqlite3_stmt *statement;
NSLog(@”checkIfHasUser“);
if(sqlite3_prepare_v2(database,[getUserCountSQL UTF8String],-1,&statement,nil)==SQLITE_OK)
{
//while(sqlite3_step(statement) == SQLITE_ROW)
//{
// int row = sqlite3_column_int(statement,0);
// char* rowData = (char*)sqlite3_column_text(statement,2);
// NSString *fieldName = [[NSString alloc] initWithFormat:@”show%d”,row];
// NSString *fieldValue = [[NSString alloc] initWithUTF8String:rowData];
//
// NSLog(@”fieldName is :%@,fieldValue is :%@”,fieldName,fieldValue);
// return [[NSString alloc] initWithFormat:@”fieldName is :%@,fieldValue is :%@”,fieldName,fieldValue];
//
// [fieldName release];
// [fieldValue release];
//}
//sqlite3_finalize(statement);
if(sqlite3_step(statement) == SQLITE_ROW)
{
NSLog(@”Have user“);
return YES;
}
}
NSLog(@”No user“);
return NO;
}
@end
其中checkIfHasUser是检查数据,这个方法中我注释的是得到数据,因为我们这里只是check,所以不需要得到数据,直接看是否存在数据即可