2012-05-26 39 views
3

我使用BLOB將對象插入到SQLite數據庫。插入後,我可以用「SELECT」語句獲取數據,數據是正確的,但當用「SQLite數據庫瀏覽器」瀏覽數據庫時,「TASK_HEAD」行是「空」。 但是,如果我銷燬剛剛插入的對象,我不能再獲取正確的數據,並且指針「pHead」指向其「id」成員的內容是「的地址」鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿鉿UG?」當在VS2008中以調試模式讀取時。已成功將blob數據插入SQLite數據庫,但無法獲取插入的數據

下面是一個例子:

// user-defined data type 
typedef std::string TASK_ID; 
struct TASK_HEAD 
{ 
    TASK_ID id; 
    std::string userData; 

    int Size() 
    { 
     return (id.size() + userData.size()) * sizeof(TCHAR); 
    } 
}; 

// when TEST_INSIDE is defined, pHead is invalid; but if undef it, I can get the "head" I just inserted 
// and if the blob data is a string (when USING_STRING is defined), I can get the string inserted into the db even though the "test" string has been destroyed 
void CDBWriter::WriteTestData() 
{ 
    // open db 
    sqlite3* db = NULL; 
    int nRet = sqlite3_open(DATABASE_NAME.c_str(), &db); 
    if (nRet != SQLITE_OK) 
    { 
     return; 
    } 

    if (db != NULL) 
    { 
     // create a table 
     std::string cmdCreate("CREATE TABLE IF NOT EXISTS testTable (id TEXT NOT NULL, TASK_HEAD BLOB, PRIMARY KEY(id));"); 
     char* errMsg = NULL; 
     nRet = sqlite3_exec(db , cmdCreate.c_str() , 0 , 0 , &errMsg); 
     if(errMsg != NULL) 
     { 
      sqlite3_free(errMsg); 
      errMsg = NULL; 
      return; 
     } 

//#define USING_STRING 
#define TEST_INSIDE 
#ifndef TEST_INSIDE 
     TASK_HEAD head; 
#endif // TEST_INSIDE 

     // insert blob data 
     const TASK_ID newID(NewGUID()); // NewGUID returns string like this: "5811307F-7AA7-4C44-831F-774FC5832627" 
     string query = "INSERT OR REPLACE INTO testTable (id, TASK_HEAD) VALUES ('"; 
     query += newID; 
     query += "', ?1);"; 
     sqlite3_stmt* res = NULL; 
     nRet = sqlite3_prepare_v2(db, query.c_str(), query.length(), &res, 0); 
     { 
#ifdef TEST_INSIDE 
      TASK_HEAD head; 
#endif // TEST_INSIDE 
      head.id = newID; 
#ifdef USING_STRING 
      std::string test("ewsjoafijdoaijeofsafsd"); 
      nRet = sqlite3_bind_blob (res, 1, test.c_str(), test.size(), SQLITE_TRANSIENT); 
#else 
      int nsizeHead = sizeof(head); 
      int nSizeHeadSt = sizeof(TASK_HEAD); 
      int sizeString = sizeof(std::string); 
      size_t nLen = newID.size(); 
      //nRet = sqlite3_bind_blob (res, 1, &head, sizeof(head), SQLITE_TRANSIENT); 
      nRet = sqlite3_bind_blob (res, 1, &head, head.Size(), SQLITE_TRANSIENT); 
#endif // USING_STRING 

      if (SQLITE_OK == nRet) 
      { 
       nRet = sqlite3_step(res); 
      } 
      if (nRet != SQLITE_OK && nRet != SQLITE_DONE) 
      { 
       return; 
      } 
     } 

     // get all columns in the database 
     query = "SELECT * FROM testTable;"; 
     nRet = sqlite3_prepare_v2 (db, query.c_str(), query.length(), &res, 0); 
     if (SQLITE_OK == nRet) 
     { 
      while (SQLITE_ROW == sqlite3_step(res)) 
      { 
#ifdef USING_STRING 
       const char* pHead = (const char*)sqlite3_column_blob(res, 1); 
#else 
       const TASK_HEAD *pHead = (const TASK_HEAD*)sqlite3_column_blob(res, 1); 
#endif // USING_STRING 
       continue; 
      } 
     } 
     sqlite3_finalize(res); 
     sqlite3_close(db); 
    } 
} 

起初,我想這可能是傳遞給sqlite3_bind_blob字節的問題,所以我獲得對象的字節有一個愚蠢的方法,你可以在這裏看到(TASK_HEAD的size()函數),但這沒有幫助。 然後我嘗試使用SQLITE_STATIC而不是SQLITE_TRANSIENT,仍然無法工作。 有什麼問題?我知道這是一個不好的解決方案,將對象插入數據庫,我只是想知道爲什麼我不能讀回我的數據插入數據庫。

回答

1

我認爲這個問題是:

nRet = sqlite3_bind_blob (res, 1, &head, head.Size(), SQLITE_TRANSIENT); 

你不能得到TASK_HEAD結構的地址,並把它傳遞給SQLite的這個樣子。要構建一個blob,你需要平坦的數據,沒有指針和動態緩衝區,如std :: string對象。

您需要在綁定操作前序列化緩衝區中的TASK_HEAD結構。 例如:

struct TASK_HEAD 
{ 
    TASK_ID id; 
    std::string userData; 

    std::string Data() 
    { 
     return id+userData; 
    } 

    int Size() 
    { 
     return (id.size() + userData.size()) * sizeof(TCHAR); 
    } 
}; 

和:

nRet = sqlite3_bind_blob (res, 1, head.Data().c_str(), head.Size(), SQLITE_TRANSIENT); 

請注意添加字段序列如上圖所示非常差(因爲此格式無法被序列化)。要處理blob,你需要找到一個好的序列化庫或格式(協議緩衝區,消息包,JSON等等)或者自己推出。

有一個在代碼中的第二個問題:

const TASK_HEAD *pHead = (const TASK_HEAD*)sqlite3_column_blob(res, 1); 

這將無法正常工作,由於類似的原因。

+0

非常感謝!這有很大幫助。順便說一句,有沒有關於BLOB數據的更多介紹(爲什麼它不能用指針和動態緩衝區構建)? – YoungLearner

+0

你可以看看http://en.wikipedia.org/wiki/Serialization –

+0

謝謝,這有幫助。 – YoungLearner

1

userData的內容可能存儲在堆中。即使它存儲在std::string(用於SSO)中,它仍然可以在內部使用一個指向自身的指針,因此當您將它複製到內存中的另一個位置時它不起作用(您正在執行的操作相當於memcpy) 。

但是,爲什麼它不起作用並不重要,因爲它只是未定義的行爲。不要像這樣「插入一個對象到數據庫」。使用一些序列化庫將其序列化,然後使用然後將其插入,或者在表中使用兩列,一個用於id,另一個用於userData

+0

謝謝你的解釋,我最好讀更多關於STL庫。並感謝您的建議。 – YoungLearner