본문 바로가기

Programming Language/Objective-C

Objective-C에서 SQLite 사용 예제

336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

Objective-C에서 SQLite 사용 예제


1-1. 헤더 추가하기


Objective-C에서 SQLite를 사용하려면, 다음과 같이 헤더 파일을 추가한다. Xcode 프로젝트를 새로 생성한 후, 아무 라이브러리도 추가하지 않은 상태이다.

#import <sqlite3.h>

 

1-2. SQLite의 객체


SQLite의 사용을 지원하는 객체에는,
데이터베이스 연결 정보를 가지고 있는 sqlite3와,
SQL 구문을 데이터베이스로 전송하기 전 컴파일을 하는 sqlite3_stmt 객체가 있다. 각 형식을 갖는 두 객체를 선언한다.

// ...
sqlite3 * database; // 데이터베이스 연결정보
sqlite3_stmt * databaseStatement; // 쿼리 구문 컴파일러
//...

 

1-3. SQLite 데이터베이스의 생성 또는 열기


SQLite 함수는 데이터베이스를 동적으로(소스코드 상으로) 생성과 기존에 존재하는 데이터베이스를 여는 방법을 구분하지 않는다. sqlite3_open 함수에 데이터베이스 파일 경로를 지정했을 때 해당 경로에 실제로 데이터베이스 파일이 있다면 그것을 열고, 존재하지 않으면 새로 생성한다음 이를 연다.

//...
NSString * databasePath = @"./test001.sqlite"; // 새로 생성할 데이터베이스 파일 또는 기존에 존재하는 데이터베이스 파일
sqlite3 * database; // 데이터베이스 연결정보
sqlite3_stmt * databaseStatement; // 쿼리 구문 컴파일러
//...
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
    NSLog(@"SUCCESS: sqlite3_open");
    sqlite3_close(database);
    database = nil;
}
else
{
    NSLog(@"FAILURE: sqlite3_open");
    sqlite3_close(database);
    database = nil;
}
// ...

 

실행 결과, 기존 데이터베이스 파일이 없다면 다음과 같이 0바이트 파일이 생성됨을 확인할 수 있다.

 

1-4. 반환 결과가 없는 SQL 쿼리 실행하기


테이블을 대상으로 하는 CREATE, DROP, 레코드를 대상으로 하는 INSERT, UPDATE, DELETE의 명령은 별도의 반환값이 필요하지 않다. SQL을 실행만 하는 방법은 sqlite3_exec 함수를 사용하는 것이다.

SQLITE_API int sqlite3_exec(
    sqlite3*,                                  /* An open database */
    const char *sql,                           /* SQL to be evaluated */
    int (*callback)(void*,int,char**,char**),  /* Callback function */
    void *,                                    /* 1st argument to callback */
    char **errmsg                              /* Error msg written here */
);

 

다음은 sqlite3_exec 함수를 사용하여 반환 결과가 없는 쿼리를 실행하는 예이다.

//...
NSString * databasePath = @"./test001.sqlite"; // 새로 생성할 데이터베이스 파일 또는 기존에 존재하는 데이터베이스 파일
sqlite3 * database = nil; // 데이터베이스 연결정보
sqlite3_stmt * databaseStatement = nil; // 쿼리 구문 컴파일러
NSString * databaseQuery = nil; // SQL 구문
char * databaseMessage = nil; // 오류 메시지를 전달받을 포인터 변수
// ...
if (sqlite3_open([databasePath UTF8String], &database) == SQLITE_OK)
{
    NSLog(@"SUCCESS: sqlite3_open");
    
    databaseQuery = @"CREATE TABLE IF NOT EXISTS `Table1`(`index` INTEGER PRIMARY KEY, `value` TEXT);";
    if (sqlite3_exec(database, [databaseQuery UTF8String], NULL, NULL, &databaseMessage) == SQLITE_OK)
    {
        NSLog(@"SUCCESS: sqlite3_exec: %@", databaseQuery);
    }
    else
    {
        NSLog(@"FAILURE: sqlite3_exec: %s", databaseMessage);
    }
    
    databaseQuery = @"INSERT INTO `Table1`(`value`) VALUES ('Hello, World!');";
    if (sqlite3_exec(database, [databaseQuery UTF8String], NULL, NULL, &databaseMessage) == SQLITE_OK)
    {
        NSLog(@"SUCCESS: sqlite3_exec: %@", databaseQuery);
    }
    else
    {
        NSLog(@"FAILURE: sqlite3_exec: %s", databaseMessage);
    }
    
    sqlite3_close(database);
    database = nil;
}
else
{
    NSLog(@"FAILURE: sqlite3_open");
    sqlite3_close(database);
    database = nil;
}
// ...

 

다음은 위에서 실행한 코드의 결과 생성된 파일을 DB Browser for SQLite로 연 것이다. 테이블과 필드가 정의되어 있는 것을 확인할 수 있다.

 

또한 INSERT 구문의 실행 결과 레코드가 1개 생성되어 있음을 확인할 수 있다.

 

1-5. 반환 결과가 있는 SQL 쿼리 실행하기


SELECT 구문은 레코드 집합을 결과로서 반환한다. 이를 실행하여 반환값을 얻기 위해서는 sqlite3_prepare_v2 함수를 사용한다. SQL 쿼리가 UTF-16으로 인코딩 되어 있다면 sqlite3_prepare16_v2 함수를 사용한다. 이 함수의 실행 결과 sqlite3_stmt 형 객체가 얻어지는데 이 객체를 통해 구문분석 및 컴파일된 SQL 쿼리가 보관되므로 잘 보관해 둔다.

SQLITE_API int sqlite3_prepare_v2(
    sqlite3 *db,            /* Database handle */
    const char *zSql,       /* SQL statement, UTF-8 encoded */
    int nByte,              /* Maximum length of zSql in bytes. */
    sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
    const char **pzTail     /* OUT: Pointer to unused portion of zSql */
); // UTF-8 version.

SQLITE_API int sqlite3_prepare16_v2(
    sqlite3 *db,            /* Database handle */
    const void *zSql,       /* SQL statement, UTF-16 encoded */
    int nByte,              /* Maximum length of zSql in bytes. */
    sqlite3_stmt **ppStmt,  /* OUT: Statement handle */
    const void **pzTail     /* OUT: Pointer to unused portion of zSql */
); // UTF-16 version

 

다음은 sqlite3_prepare_v2 함수와 sqlite3_stmt 객체를 사용하여 SELECT 구문을 수행하는 과정의 예이다.

//...
NSString * databasePath = @"./test001.sqlite"; // 새로 생성할 데이터베이스 파일 또는 기존에 존재하는 데이터베이스 파일
sqlite3 * database = nil; // 데이터베이스 연결정보
sqlite3_stmt * databaseStatement = nil; // 쿼리 구문 컴파일러
NSString * databaseQuery = nil; // SQL 구문
char * databaseMessage = nil; // 오류 메시지를 전달받을 포인터 변수
// ...
databaseQuery = @"SELECT * FROM `Table1`;";
if (sqlite3_prepare_v2(database, [databaseQuery UTF8String], -1, &databaseStatement, nil) == SQLITE_OK)
{
    int          query_column = 0;
    int          field_index  = 0;
    const char * field_value  = nil;
    
    query_column = sqlite3_column_count(databaseStatement);
    NSLog(@"SUCCESS: sqlite3_prepare_v2: %@, query_column = %d", databaseQuery, query_column);
    while (sqlite3_step(databaseStatement) == SQLITE_ROW)
    {
        field_index = (int)          sqlite3_column_int(databaseStatement, 0);
        field_value = (const char *) sqlite3_column_text(databaseStatement, 1);
        NSLog(@"SUCCESS: sqlite3_step: %d, %s", field_index, field_value);
    }
    sqlite3_finalize(databaseStatement);
}
else
{
    NSLog(@"FAILURE: sqlite3_prepare_v2");
}

 

다음은 실행 결과이다. 터미널로 출력되는 레코드와 DB Browser for SQLite에서 보여지는 레코드가 일치함을 알 수 있다.

 

1-6. 텍스트 바인딩


UPDATE SET ... 구문이나 SELECT ... WHERE 구문과 같이 쿼리에 사용자 문자열이 들어가는 경우, 사용자 문자열에 포함된 따옴표 및 기타 문자 의한 구문 오류가 발생할 수 있다. 이를 방지하기 위해 텍스트 바인딩을 사용할 수 있다.

//...
NSString * databasePath = @"./test001.sqlite"; // 새로 생성할 데이터베이스 파일 또는 기존에 존재하는 데이터베이스 파일
sqlite3 * database = nil; // 데이터베이스 연결정보
sqlite3_stmt * databaseStatement = nil; // 쿼리 구문 컴파일러
NSString * databaseQuery = nil; // SQL 구문
char * databaseMessage = nil; // 오류 메시지를 전달받을 포인터 변수
// ...
databaseQuery = @"SELECT * FROM `Table1` WHERE (`index` = ? AND `value` = ?);";
if (sqlite3_prepare_v2(database, [databaseQuery UTF8String], -1, &databaseStatement, nil) == SQLITE_OK)
{
    int          query_column = 0;
    int          field_index  = 0;
    const char * field_value  = nil;
    
    sqlite3_bind_int(databaseStatement, 1, 100); // index = 100인 레코드 중에서...
    sqlite3_bind_text(databaseStatement, 2, "foo", -1, nil); // value = "foo"인 레코드들을 찾는다.
    
    query_column = sqlite3_column_count(databaseStatement);
    NSLog(@"SUCCESS: sqlite3_prepare_v2: %@, query_column = %d", databaseQuery, query_column);
    while (sqlite3_step(databaseStatement) == SQLITE_ROW)
    {
        field_index = (int)          sqlite3_column_int(databaseStatement, 0);
        field_value = (const char *) sqlite3_column_text(databaseStatement, 1);
        NSLog(@"SUCCESS: sqlite3_step: %d, %s", field_index, field_value);
    }
    sqlite3_finalize(databaseStatement);
}
else
{
    NSLog(@"FAILURE: sqlite3_prepare_v2");
}