将项添加到sqlite3数据库不起作用

时间:2014-01-01 22:21:02

标签: objective-c sqlite xcode5.0.1

我有以下代码,它基于youtube上的教程实现,但我更改了一些以满足我的目的,即插入硬编码的项目..数据库已创建,而且表格但我发现addItems没有添加。 我不认为addItemToTable方法有问题, 我认为在这里列出的第一部分是一个逻辑错误,但找不到它。任何帮助都会很棒

提前致谢

这些是项目:

    table_ok = YES;
    if (table_ok) {
            if (!db_open_status) {
                [self openDBWithSQLName:dataBaseName];
                NSLog(@"DB opened");
            }

            NSMutableDictionary *objectColsVals = [[NSMutableDictionary alloc]init];
            NSString *this_id = @"12";
            NSString *this_name = @"and";
            NSString *this_email = @"123@hotmail.com";
            NSString *this_password = @"aa11111";
            NSString *this_role = @"Marketing";
            [objectColsVals setValue:this_id forKey:[my_columns_names objectAtIndex:0]];
            [objectColsVals setValue:this_name forKey:[my_columns_names objectAtIndex:1]];
            [objectColsVals setValue:this_email forKey:[my_columns_names objectAtIndex:2]];
            [objectColsVals setValue:this_password forKey:[my_columns_names objectAtIndex:3]];
            [objectColsVals setValue:this_role forKey:[my_columns_names objectAtIndex:4]];

            if ([[objectColsVals allKeys] count] > 0) {
                if ([self addItemToTable:tableName WithColumnValues:objectColsVals]) {
                    NSLog(@"inserted");
                    [self closeDB];
                }
              }  

这个方法:

-(BOOL)addItemToTable:(NSString *)usetable WithColumnValues:(NSDictionary *)valueObject{
    BOOL has_beenAdded = NO;
    NSString *mycolumns = @"";
    NSString *myvalues = @"";
    //loop through all the value keys 
    for (int r=0; r<[[valueObject allKeys] count]; r++) {
        NSString *this_keyname = [[valueObject allKeys]objectAtIndex:r];
        mycolumns = [mycolumns stringByAppendingString:this_keyname];
        NSString *thisval = [NSString stringWithFormat:@"'%@'",[valueObject objectForKey:this_keyname]];
        myvalues = [myvalues stringByAppendingString:thisval];
        //add commas to seperate the col and val lists before last item
        if (r<(([[valueObject allKeys] count])-1)) {
            mycolumns = [mycolumns stringByAppendingString:@","];
            myvalues = [myvalues stringByAppendingString:@","];
        }
    }

    NSString *myinsert = [NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES(%@)",usetable,mycolumns,myvalues];
    char *err;
    if (sqlite3_exec(estate_db, [myinsert UTF8String], NULL, NULL, &err) != SQLITE_OK) {
        sqlite3_close(estate_db);
    }else{
        has_beenAdded = YES;
    }

    return has_beenAdded;
}

1 个答案:

答案 0 :(得分:1)

就你所拥有的而言,很难说问题出在哪里。似乎没有什么是错的。应该至少应该:

  • 检查以编程方式生成的INSERT语句的内容,以确保没有一些微妙的问题无法粗略检查代码;

  • 如果任何sqlite3_xxx()调用失败(特别是如果sqlite3_exec返回除SQLITE_OK之外的任何内容),则记录错误消息(err变量,或者通过致电sqlite3_errmsg());如果你不看这些错误信息,你只是盲目飞行;以及

  • 在模拟器上运行应用程序,然后在Mac上打开模拟器的数据库副本(在~/Library/Application Support/iPhone Simulator目录中;如果隐藏了~/Library文件夹,请通过运行在终端命令行工具中使用chflags -nohidden ~/Library命令)并直接检查数据库的内容。验证列名,表名等。

同样,目前还不清楚问题出在哪里,但它很可能只是在打开数据库或创建相关表格时会出现一些混乱。在我们确认错误消息和实际SQL之前,很难说。它可以是尝试打开捆绑包中数据库的只读副本,错误地调用sqlite3_open并无意中创建新的空白数据库。您真的应该更新问题并共享创建数据库的代码(或从包中复制它),以及执行上面概述的一些诊断步骤。


说完这些之后,我真的不鼓励你用stringWithFormat将值添加到SQL中。 SQL的动态构建很好,但你真的不应该使用stringWithFormat将值插入SQL本身。鉴于您使用单引号引用文本值,如果该人的姓氏为O'Brian,该怎么办?或者,如果您更改了例程以使用双引号,那么该人的名字是Dwayne "The Rock" Johnson怎么办?如果字符串定界符出现在数据值中,则当前代码可能会失败。更糟糕的是,你在技术上暴露自己的SQL注入攻击。

您通常应该使用?占位符。例如,考虑如下定义的字典:

NSDictionary *dataToInsert = @{@"name"        : @"Jack",
                               @"id"          : @37,
                               @"password"    : @"feefifofum",
                               @"role"        : [NSNull null],
                               @"email"       : @"jack@magicbeans.fairyland.com",
                               @"hourly_wage" : @12.85};

您要做的是构建一个如下所示的SQL语句:

INSERT INTO test (name,id,password,role,email,hourly_wage) VALUES (?,?,?,?,?,?)

然后,您希望使用?函数将值绑定到这些sqlite3_bind_xxx()占位符。

因此,您可以创建和准备SQL语句(构建values数组和placeholders数组),如下所示:

NSArray *keys = [dataToInsert allKeys];
NSMutableArray *values = [NSMutableArray arrayWithCapacity:[keys count]];
NSMutableArray *placeholders = [NSMutableArray arrayWithCapacity:[keys count]];

// build array of values and array of question mark placeholders

for (NSString *key in keys) {
    [values addObject:[dataToInsert objectForKey:key]];
    [placeholders addObject:@"?"];
}

// use the `keys` and `placeholders` arrays to build the SQL

NSString *insertSql = [NSString stringWithFormat:@"INSERT INTO %@ (%@) VALUES (%@)",
                       tableName,
                       [keys componentsJoinedByString:@","],
                       [placeholders componentsJoinedByString:@","]];

if (sqlite3_prepare_v2(db, [insertSql UTF8String], -1, &statement, NULL) != SQLITE_OK) {
    NSLog(@"prepare failed: %s", sqlite3_errmsg(db));
    sqlite3_close(db);
    return;
}

// statement is prepared, but we still have to bind the values...

然后,您可以使用以下内容绑定值。这是动态检查values数组中对象的类(如果它是NSNumber,请查看objCType以确定数字的类型):

// now use the `values` array to bind values to the ? placeholders

[values enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    int rc = SQLITE_ERROR;

    if ([obj isKindOfClass:[NSString class]])
        rc = sqlite3_bind_text(statement, idx + 1, [obj UTF8String], -1, SQLITE_TRANSIENT);
    else if ([obj isKindOfClass:[NSNull class]])
        rc = sqlite3_bind_null(statement, idx + 1);
    else if ([obj isKindOfClass:[NSNumber class]]) {
        const char *objCType = [obj objCType];

        if (strcmp(objCType, @encode(int)) == 0 || strcmp(objCType, @encode(unsigned int)) == 0 || strcmp(objCType, @encode(short)) == 0 || strcmp(objCType, @encode(unsigned short)) == 0 || strcmp(objCType, @encode(char)) == 0 || strcmp(objCType, @encode(unsigned char)) == 0)
            rc = sqlite3_bind_int(statement, idx + 1, [obj integerValue]);
        else if (strcmp(objCType, @encode(long)) == 0 || strcmp(objCType, @encode(unsigned long)) == 0 || strcmp(objCType, @encode(long long)) == 0 || strcmp(objCType, @encode(unsigned long long)) == 0)
            rc = sqlite3_bind_int64(statement, idx + 1, [obj longLongValue]);
        else if (strcmp(objCType, @encode(float)) == 0 || strcmp(objCType, @encode(double)) == 0)
            rc = sqlite3_bind_double(statement, idx + 1, [obj doubleValue]);
        else {
            NSLog(@"column %d is %@ but has unknown numeric type %s; will use `description`", idx + 1, obj, objCType);
            rc = sqlite3_bind_text(statement, idx + 1, [[obj description] UTF8String], -1, SQLITE_TRANSIENT);
        }
    }
    else
        rc = sqlite3_bind_text(statement, idx + 1, [[obj description] UTF8String], -1, SQLITE_TRANSIENT);

    if (rc != SQLITE_OK)
    {
        NSLog(@"bind %d failed: %s", idx + 1, sqlite3_errmsg(db));
        sqlite3_finalize(statement);
        sqlite3_close(db);
        return;
    }
}];

if (sqlite3_step(statement) != SQLITE_DONE) {
    NSLog(@"step failed: %s", sqlite3_errmsg(db));
    sqlite3_close(db);
    return;
}

sqlite3_finalize(statement);