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;
}
4

1 回答 1

1

就你在这里得到的东西而言,很难说问题出在哪里。似乎没有什么明显的错误。至少,一个人应该:

  • 检查INSERT您以编程方式构建的结果语句,以确保没有一些微妙的问题无法对代码进行粗略检查;

  • 如果任何sqlite3_xxx()调用失败(特别是如果sqlite3_exec返回除 之外的任何内容SQLITE_OK),则记录错误消息(err变量或调用sqlite3_errmsg());如果您不查看这些错误消息,您只是在盲目飞行;和

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

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


话虽如此,我真的不鼓励您使用stringWithFormat. 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数组中对象的类进行动态检查(如果是 a 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);
于 2014-01-02T01:57:45.597 回答