标签:
关于基本数据的持久性,写过一篇文章来简述过(基本数据持久性(一) 使用plist保存和读取数据)。这篇文章将简述采用数据库sqlite的方式来保存数据,并根据查询结果读取数据。
一、工作原理
sqlite采用表存储的方式,表的第一行(也就是我们常说的表头)在sqilte中被称为“字段”。对于标的每一行(除了字段)的信息,都有一个独一无二的列内容可以将表的每一行内容独立区分开(例如本文所示的案例,存储一个学生的信息——学号、姓名、年龄、班级。那么,学号这一列就可以将表的每一行内容独立区分开,因为每个学生的学号都是不一样的),那么我们就把字段中的“学号”称之为“主键”。主键是具有唯一性的。
二、程序的主要功能
1.xib文件如图1所示:
图 1
2. 通过“保存”按钮将4个textField(ID、Name、Age、Class后面的输入框)的内容保存到sqlite文件中。
3.通过“读取”按钮将“查询”后的结果从sqlite中的内容分别读取到4个textField中。如果没有查询到,则弹出来一个alert提示查询失败。因为每一个字段只有一个供显示的窗口,所以为了避免误解,我们只查询主键的值。
三、实现步骤
1.先创建一个Single ViewController的视图,命名为“使用sqlite保存和读取数据”。然后对xib文件进行操作,分别放置上5个Label、5个textField和2个Button,如图1所示。
2.对5个textField分别设置IBOutlet属性,对2个Button分别设置IBAction方法,并将其与xib文件相对应的控件关联。
3.在“ViewController.h”文件中,添加一个UITextFieldDelegate,以调用该协议中的“- (BOOL)textFieldShouldReturn:(UITextField *)textField”方法通过点击键盘的返回键来隐藏键盘(注:因为这个程序比较简单,该步骤不是必须的,可根据需要添加。因为在程序中,还会通过一个“- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event”的方法,使得点击屏幕时隐藏键盘)。做完2、3步骤以后的程序应该与下面的程序代码类似:
01.#import <UIKit/UIKit.h> 02.@interface ViewController : UIViewController<UITextFieldDelegate> 03.@property (retain, nonatomic) IBOutlet UITextField *searchFeild; //查询文本框 04.@property (retain, nonatomic) IBOutlet UITextField *stuID; //学生学号文本框 05.@property (retain, nonatomic) IBOutlet UITextField *stuName; //学生姓名文本框 06.@property (retain, nonatomic) IBOutlet UITextField *stuAge; //学生年龄文本框 07.@property (retain, nonatomic) IBOutlet UITextField *stuClass; //学生班级文本框 08.- (IBAction)saveData:(id)sender; //保存按钮触发方法 09.- (IBAction)loadData:(id)sender; //读取按钮触发方法 10.@end4.因为有textField,所以一般接下来我是先在“ViewController.m”文件里面写隐藏键盘的方法,以免忘记而在测试程序时因为键盘未隐藏而耽误事。在这里有两个方法来隐藏键盘,上面已经叙述过。如果采用“- (BOOL)textFieldShouldReturn:(UITextField *)textField”的方法来隐藏键盘,需要先在“- (void)viewDidLoad”方法里面设置代理,程序代码如下:
01.- (void)viewDidLoad 02.{ 03.[super viewDidLoad]; 04._searchFeild.delegate = self; 05._stuID.delegate = self; 06._stuName.delegate = self; 07._stuAge.delegate = self; 08._stuClass.delegate = self; 09.}另外一种隐藏键盘的方法是当触摸屏幕时(非textField区域和键盘区域)键盘隐藏,因此可以使用“- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event”方法,代码如下:
01.//点击非textField和键盘的屏幕后隐藏键盘 02.- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event 03.{ 04.[_searchFeild resignFirstResponder]; 05.[_stuID resignFirstResponder]; 06.[_stuName resignFirstResponder]; 07.[_stuAge resignFirstResponder]; 08.[_stuClass resignFirstResponder]; 09.}5.接下来,就是对sqlite的操作了。未能够使用sqilte,需要先加载一个Framework——libsqlite3.dylib。加载完成后,在ViewController.h文件里面包含刚加载的Framework的头文件"#import <sqlite3.h>",并添加上一个NSCoding的协议,以完成保存和读取数据时编码和解码的工作。然后添加一个sqlite3的指针,以便操作数据。完成后的代码如下所示:
01.#import <UIKit/UIKit.h> 02.#import <sqlite3.h> 03.@interface ViewController : UIViewController<UITextFieldDelegate, NSCoding> 04.{ 05.sqlite3 *sqliteDB; //定义一个数据库指针,以便操作数据 06.} 07.@property (retain, nonatomic) IBOutlet UITextField *searchFeild; //查询文本框 08.@property (retain, nonatomic) IBOutlet UITextField *stuID; //学生学号文本框 09.@property (retain, nonatomic) IBOutlet UITextField *stuName; //学生姓名文本框 10.@property (retain, nonatomic) IBOutlet UITextField *stuAge; //学生年龄文本框 11.@property (retain, nonatomic) IBOutlet UITextField *stuClass; //学生班级文本框 12.- (IBAction)saveData:(id)sender; //保存按钮触发方法 13.- (IBAction)loadData:(id)sender; //读取按钮触发方法 14.@end接下来,我们就需要创建一个sqlite3的数据库文件,用来保存我们输入的信息;并且要得到该文件的位置,以便我们往这个文件里面保存数据和从这个文件里面读取数据。为此,我们创建一个方法,该方法返回一个NSString类型的值(文件的路径是一串字符串,这个很好理解吧?),当我们点击“保存”或者“读取”按钮时,能够读到这个路径下的数据。我写的这个方法名叫“- (NSString*)dataFilePath”,详细代码如下所示:
01.//获取sqlite3文件的路径 02.- (NSString*)dataFilePath 03.{ 04.//获取文件路径数组 05.NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //注意:括号里面的第一个参数是NSDocumentDirectory,而不是NSDocumentationDirectory 06.//获取上述文件路径数组里面的第一个元素,我们只需要这个元素 07.NSString *documentPath = [paths objectAtIndex:0]; 08.//获取文件名称 09.NSString *fileName = [documentPath stringByAppendingPathComponent:@"data.sqlite3"]; 10.return fileName; 11.}6.可以把数据库文件(sqlite3)理解成一个“第三方平台”。那么,如果要想使我们的程序运行后能够关联到这个“第三方平台上”,就需要在我们对程序做任何操作之前能够加载成功这个“这个第三方平台”。于是,答案呼之欲出——我们需要在“- (void)viewDidLoad”方法中创建并加载数据库文件。我们创建一个名称为“student”的表来保存我们输入的数据,因此,完成后的“- (void)viewDidLoad”方法的代码应如下所示:
01.- (void)viewDidLoad 02.{ 03.[super viewDidLoad]; 04. 05._searchFeild.delegate = self; 06._stuID.delegate = self; 07._stuName.delegate = self; 08._stuAge.delegate = self; 09._stuClass.delegate = self; 10. 11.//加载创建的数据库文件(data.sqlite3) 12. 13.//1.获取保存文件的路径 14.NSString *path = [self dataFilePath]; 15. 16.//2.容错测试,判断文件是否存在,如果不存在,弹出警告并关闭文件;否则,继续执行 17.if(sqlite3_open([path UTF8String], &sqliteDB) != SQLITE_OK) 18.{ 19.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"文件路径不存在" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 20. 21.[alert show]; 22. 23.sqlite3_close(sqliteDB); 24. 25.[alert release]; //我没有使用ARC,所以alert必须要释放 26.} 27. 28.else //文件存在并打开成功 29.{ 30.//2.1定义一个char*变量接收错误信息 31.char *error; 32. 33.//2.2sqlite创建表的语句,创建一个名为student的表 34.//字段名是stuID、stuName、stuAge、stuClass 35.//类型为text表示为字符串类型 36.//并将stuID做为主键(PRIMARY KEY) 37. 38.char *createSQL = "CREATE TABLE IF NOT EXISTS student (stuID TEXT PRIMARY KEY, stuName TEXT, stuAge TEXT, stuClass TEXT);"; //如果名称为student的表不存在,则创建一个表,且把stuID做为主键(PRIMARY KEY)以区分行的唯一性。注意双引号里面有个分号。 39. 40.//2.3执行creatSQL语句(数据库指针,将要执行的语句) 41.if(sqlite3_exec(sqliteDB, createSQL, NULL, NULL, &error) != SQLITE_OK) 42.{ 43.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"创建SQL失败" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 44. 45.[alert show]; 46. 47.[alert release]; //我没有使用ARC,所以alert必须要释放 48.} 49. 50.sqlite3_close(sqliteDB); 51. 52.} 53.}7.接下来,我们就来完善“保存”按钮的方法,使得我们能够将我们输入的数据保存到数据库中以供读取。详细的代码如下:
01.//“保存”按钮的实现 02.- (IBAction)saveData:(id)sender 03.{ 04.//1.先打开文件路径 05.NSString *path = [self dataFilePath]; 06. 07.//2.容错测试,判断文件是否存在,如果不存在,弹出警告并关闭文件;否则,继续执行 08.if(sqlite3_open([path UTF8String], &sqliteDB) != SQLITE_OK) 09.{ 10.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"文件路径不存在" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 11. 12.[alert show]; 13. 14.sqlite3_close(sqliteDB); 15. 16.[alert release]; //我没有使用ARC,所以alert必须要释放 17.} 18. 19.else //文件存在并打开成功 20.{ 21.//2.1定义一个char*类型的变量来描述往在viewDidload中创建的student表中插入或者替换数据的语句 22. 23.char *sqlStr = "INSERT OR REPLACE INTO student(stuID, stuName, stuAge, stuClass) VALUES (?,?,?,?)"; 24. 25.//2.2定义一个对sql语句操作的指针 26.sqlite3_stmt *statement; 27. 28.//2.3容错检查,若数据保存成功,则执行插入数据的操作 29.if(sqlite3_prepare_v2(sqliteDB, sqlStr, -1, &statement, NULL) == SQLITE_OK) 30.{ 31.//插入数据的位置只能从1开始 32.sqlite3_bind_text(statement, -1, [_stuID.text UTF8String], 1, NULL); //在1的位置插入学号 33. 34.sqlite3_bind_text(statement, -1, [_stuName.text UTF8String], 2, NULL); //在2的位置插入姓名 35. 36.sqlite3_bind_text(statement, -1, [_stuAge.text UTF8String], 3, NULL); //在3的位置插入年龄 37. 38.sqlite3_bind_text(statement, -1, [_stuClass.text UTF8String], 4, NULL); //在4的位置插入年级 39. 40. 41.//判断数据是否插入成功,若不成功,则弹出一个alert 42.if(sqlite3_step(statement) != SQLITE_DONE) 43.{ 44.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"数据插入失败!"delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 45. 46.[alert show]; 47. 48.[alert release]; //我没有使用ARC,所以必须手动释放alert 49.} 50.} 51. 52.else53.{ 54.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"数据保存失败!"delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 55. 56.[alert show]; 57. 58.[alert release]; //我没有使用ARC,所以必须手动释放alert 59. 60.} 61. 62.//2.4结束指令指针,释放预编译内存 www.it165.net 63.sqlite3_finalize(statement); 64. 65.//2.5关闭数据库 66.sqlite3_close(sqliteDB); 67.} 68. 69.i++; //每保存一条信息,则i的值就加1 70.}8.数据已经保存了,那么是否可以读取呢?为此,我们先编写完成“读取”按钮的方法。我在程序里面给它命名为“- (IBAction)loadData:(id)sender”。首先,还是需要先把保存数据的路径给找出来,同时,我们还需要知道数据库里面一共保存了多少行的数据,这样才能使得我们对查询的数据与数据库里面的数据进行比较。如果查询到数据,就把把该数据里面的内容分别赋值给4个表示信息的textField;如果没有查询到信息,就弹出来一个alert。详细代码如下所示:
001.//“保存”按钮的实现 002.- (IBAction)saveData:(id)sender 003.{ 004.//1.先打开文件路径 005.NSString *path = [self dataFilePath]; 006. 007.//2.容错测试,判断文件是否存在,如果不存在,弹出警告并关闭文件;否则,继续执行 008.if(sqlite3_open([path UTF8String], &sqliteDB) != SQLITE_OK) 009.{ 010.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"文件路径不存在" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 011. 012.[alert show]; 013. 014.sqlite3_close(sqliteDB); 015. 016.[alert release]; //我没有使用ARC,所以alert必须要释放 017.} 018. 019.else //文件存在并打开成功 020.{ 021.//2.1定义一个char*类型的变量来描述往在viewDidload中创建的student表中插入或者替换数据的语句 022. 023.char *sqlStr = "INSERT OR REPLACE INTO student(stuID, stuName, stuAge, stuClass) VALUES (?,?,?,?);"; //注意双引号里面有个分号 024. 025.//2.2定义一个对sql语句操作的指针 026.sqlite3_stmt *statement; 027. 028.//2.3容错检查,若数据保存成功,则执行插入数据的操作 029.if(sqlite3_prepare_v2(sqliteDB, sqlStr, -1, &statement, NULL) == SQLITE_OK) 030.{ 031.//插入数据的位置只能从1开始 032.sqlite3_bind_text(statement, 1, [_stuID.text UTF8String], -1, NULL); //在1的位置插入学号 033. 034.sqlite3_bind_text(statement, 2, [_stuName.text UTF8String], -1, NULL); //在2的位置插入姓名 035. 036.sqlite3_bind_text(statement, 3, [_stuAge.text UTF8String], -1, NULL); //在3的位置插入年龄 037. 038.sqlite3_bind_text(statement, 4, [_stuClass.text UTF8String], -1, NULL); //在4的位置插入年级 039. 040.//判断数据是否插入成功,若不成功,则弹出一个alert 041.if(sqlite3_step(statement) != SQLITE_DONE) 042.{ 043.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"数据插入失败!"delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 044. 045.[alert show]; 046. 047.[alert release]; //我没有使用ARC,所以必须手动释放alert 048.} 049.} 050. 051.else052.{ 053.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"数据保存失败!"delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 054. 055.[alert show]; 056. 057.[alert release]; //我没有使用ARC,所以必须手动释放alert 058. 059.} 060. 061.//2.4结束指令指针,释放预编译内存 062.sqlite3_finalize(statement); 063. 064.//2.5关闭数据库 065.sqlite3_close(sqliteDB); 066. 067.} 068. 069.} 070.//“读取”按钮的实现 071.- (IBAction)loadData:(id)sender 072.{ 073.int j = 0; //对查询的数据,每和数据库中的主键比较一次就加1 074. 075.int result; //保存数据库的总结果 076. 077.char * errmsg = NULL; //接收错误信息的变量 078.char ** dbResult; //是 char ** 类型,两个*号 079.int nRow, nColumn; //数据库中的表格一共有多少行和多少列 080. 081.//1.先打开文件保存路径 082. 083.NSString *path = [self dataFilePath]; 084. 085.//2.将result的结果保存为数据库打开与否的状态 086.result = sqlite3_open([path UTF8String], &sqliteDB); 087. 088. 089.//3.打开数据库并进行容错检查 090. 091.if(sqlite3_open([path UTF8String], &sqliteDB) != SQLITE_OK) 092.{ 093.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"警告" message:@"文件路径不存在" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles: nil]; 094. 095.[alert show]; 096. 097.sqlite3_close(sqliteDB); 098. 099.[alert release]; //我没有使用ARC,所以alert必须要释放 100.} 101. 102.else103.{ 104.//3.1定义查询语句,查询名为student的表 105.char *querySQL = "SELECT * FROM student"; 106. 107.//3.2定义语句指令的指针,用这个指针来向SQL语句发出指令 108.sqlite3_stmt *statement; 109. 110.//3.3得到数据库的table的结果,其中的nRow的结果就是表格有多少行 111.result = sqlite3_get_table( sqliteDB, querySQL, &dbResult, &nRow, &nColumn, &errmsg ); 112. 113.//3.3预处理、预编译 114.if(sqlite3_prepare_v2(sqliteDB, querySQL, -1, &statement, NULL) == SQLITE_OK) 115.{ 116.//执行查找语句,遍历出所有的查询结果 117.while(sqlite3_step(statement) == SQLITE_ROW) 118.{ 119.j++; 120. 121.//NSLog(@"nRow = %i", nRow); //测试表格的行数是否正确 122. 123. 124.//从数据库中取出第0个字段的内容 125.//注意,取的时候一定是从0开始的,而不是1 126.char *field1 = (char*)sqlite3_column_text(statement, 0); 127. 128.//定义一个变量用于保存从第0个字段读出来的数据 129.//并转换成字符串的形式 130.NSString *field1String = [[NSString alloc]initWithUTF8String:field1]; 131. 132. 133.//从数据库中取出第1个字段的内容 134.char *field2 = (char*)sqlite3_column_text(statement, 1); 135. 136.//定义一个变量用于保存从第1个字段读出来的数据 137.//并转换成字符串的形式 138.NSString *field2String = [[NSString alloc]initWithUTF8String:field2]; 139. 140. 141.//从数据库中取出第2个字段的内容 142.char *field3 = (char*)sqlite3_column_text(statement, 2); 143. 144.//定义一个变量用于保存从第2个字段读出来的数据 145.//并转换成字符串的形式 146.NSString *field3String = [[NSString alloc]initWithUTF8String:field3]; 147. 148. 149.//从数据库中取出第3个字段的内容 150.char *field4 = (char*)sqlite3_column_text(statement, 3); 151. 152.//定义一个变量用于保存从第3个字段读出来的数据 153.//并转换成字符串的形式 154.NSString *field4String = [[NSString alloc]initWithUTF8String:field4]; 155. 156. 157.//将需要查询的结果与第0个字段转化为字符串以后的结果比较 158.//如果在nRow(包含nRow)之前查询到数据,就将结果显示,并退出不再继续比较 159.if([_searchFeild.text isEqualToString:field1String]) 160.{ 161.//如果比较的结果为相等,则把数据读入显示的4个textField中 162. 163._stuID.text = field1String; 164._stuName.text = field2String; 165._stuAge.text = field3String; 166._stuClass.text = field4String; 167. 168.return; 169.} 170. 171.//如果上面的那个if一直没有被触发,那么就执行下面这条if语句,弹出查询错误的警告 172.if((j == nRow) && ![_searchFeild.text isEqualToString:field1String]) 173.{ 174.NSString *alertStr = [[NSString alloc]initWithFormat:@"未找到编号为%@的用户", _searchFeild.text]; 175. 176.UIAlertView *alert = [[UIAlertView alloc]initWithTitle:@"查询错误" message:alertStr delegate:self cancelButtonTitle:@"取消" otherButtonTitles: nil]; 177. 178._stuID.text = @""; 179._stuName.text = @""; 180._stuAge.text = @""; 181._stuClass.text = @""; 182. 183.[alert show]; 184. 185.[alertStr release]; //没有使用ARC,必须手动释放alertStr 186. 187.[alert release]; //没有使用ARC,必须手动释放alert 188. 189.} 190. 191.[field1String release]; 192.[field2String release]; 193.[field3String release]; 194.[field4String release]; 195. 196.} 197.} 198. 199.//3.4结束指令指针,释放预编译内存 200.sqlite3_finalize(statement); 201. 202.//3.5关闭数据库 203.sqlite3_close(sqliteDB); 204.} 205. 206.}然后,通过Xcode结束程序,再Run一下程序,在“查询”输入框输入要查询的内容(只能是主键的值),点击“读取”按钮,看看效果吧。
9.至此,我们的程序就算是完成了。如果你没有采用ARC来写这个程序,那么,请记得在“- (void)dealloc”方法里面释放你创建的内存——如果你采用的是直接从xib拖动控件到ViewController.h文件里面生成相应的属性和方法,那么Xcode已经为你写好了“- (void)dealloc”方法了,你无须理会。
10.如果你想彻底清除data.sqlite3里面保存的数据,直接将iOS模拟器“还原内容和设置”即可。
四、扩展练习
在该例子中,我们完成的是数据的保存和读取操作。那么,请你想一想,如何完成对数据库里面的数据进行删除操作呢?是否可以对数据库里面的文件进行排序操作呢?
期待你的完美答案!
标签:
原文地址:http://www.cnblogs.com/fengmin/p/4261179.html