在C ++中序列化对象并在mysql中存储为blob类型

时间:2010-05-03 15:49:28

标签: c++ mysql blob

我正在使用mysql / C ++连接器连接到mysql数据库。我有一些复杂的数据结构,所以我需要序列化这些并保存在数据库中。

我尝试过以下内容。

vector<int> vectorTest(10,100);
istream *blob = NULL;
ostringstream os;
int size_data = sizeof(vector<int>);

blob = new istringstream((char*)&vectorTest, istringstream::in | istringstream::binary);
string qry = "INSERT INTO vector(id,object) VALUES (?,?)";

prep_stmt = con->prepareStatement(qry);

prep_stmt->setInt(1,1);
prep_stmt->setBlob(2,blob);
prep_stmt->execute();

我在这里尝试了一个小例子。但是,矢量对象未被保存。

或者我可以使用以下方法。

ostringstream os;
int size_data = sizeof(vector<int>);
os.write((char*)&vectorTest, size_data);

但是我不知道如何将输出流重定向到输入流,因为setBlob()方法需要一个istream作为输入参数。

我可以知道如何使这些示例工作吗?如果我的方法不正确,任何人都可以提供代码示例或改进给定的代码段吗?非常感谢您的即时回复。

由于

3 个答案:

答案 0 :(得分:6)

你完全以错误的方式解决这个问题。这不是“序列化”,事实上它很可能与序列化相反 - 它只是试图将向量的原始内存转储写入数据库。想象一下这个向量看起来像这样的东西:

struct vector_int {
  unsigned int num_elements;
  int* elements;
};

其中elements是一个动态分配的数组,用于保存向量的元素。

最终写到数据库的是num_elements的值,然后是指针的值 elements。元素数据不会写入数据库,如果您要将指针位置加载回程序的不同运行中的向量,则它指向的位置将包含垃圾。 std::vector会发生同样的事情,因为它包含动态分配的内存,将在您的情况下写为指针值,以及其他内部状态,如果重新加载可能无效。

“序列化”的全部意义是避免这种情况。序列化意味着将这样的复杂对象转换为包含重构原始对象所需的所有信息的字节序列。您需要迭代向量并写出其中的每个整数。而且,你需要设计一种格式,当你重读它时,你可以确定一个整数的结束和下一个整数的开始。

例如,您可以用空格分隔整数,并将其写出来:

1413 1812 1 219 4884 -57 12 

然后当你读回这个blob时,你必须将这个字符串解析成七个独立的整数并将它们插入到一个新创建的向量中。

要写出的示例代码:

vector<int> vectorTest(10,100);
ostringstream os;

for (vector<int>::const_iterator i = vectorTest.begin(); i != vectorTest.end(); ++i) 
{
  os << *i << " ";
}

// Then insert os.str() into the DB as your blob

要读入的示例代码:

// Say you have a blob string called "blob"

vector<int> vectorTest;
istringstream is(blob);
int n;

while(is >> n) {
  vectorTest.push_back(n);
}

现在,这不一定是空间方面最有效的方法,因为在将它们插入数据库之前将整数转换为字符串,这比将二进制编码插入数据库所需的空间要多得多整数。但是,在这种情况下,写出和读入的代码会更复杂,因为您必须关注如何将整数打包成字节序列以及如何将字节序列解析为一堆整数。上面的代码使用字符串,以便标准库流可以使这部分变得简单,并且可以更直接地演示序列化所需的内容。

答案 1 :(得分:1)

我写入MySQL数据库的解决方案是使用Visitor设计模式和抽象基类。我没有使用BLOB数据结构,而是使用字段(列):

struct Field
{
    // Every field has a name.
    virtual const std::string    get_field_name(void) = 0;

    // Every field value can be converted to a string (except Blobs)
    virtual const std::string    get_value_as_string(void) = 0;

    // {Optional} Every field knows it's SQL type.
    // This is used in creating the table.
    virtual unsigned int         get_sql_type(void) = 0;

    // {Optional} Every field has a length
    virtual size_t               get_field_length(void) = 0;
};

我构建了一个层次结构,包括数字,bool和字符串的字段。给定Field指针或引用,可以生成SQL INSERTSELECT语句。

Record将是字段的容器。只需向访问者提供for_each()方法:

struct Field_Functor
{
    virtual void    operator()(const Field& f) = 0;
};

struct Record
{
    void    for_each(Field_Functor& functor)
    {
      //...
      functor(field_container[i]);  // or something similar
    }
};

通过使用更真实的访客设计模式,SQL细节将移动到访问者中。由于调用的方法,访问者知道字段属性。这会将Field结构简化为仅使用get_field_nameget_value_as_string方法。

struct Field_Integer;

struct Visitor_Base
{
    virtual void process(const Field_Integer& fi) = 0;
    virtual void process(const Field_String& fs) = 0;
    virtual void process(const Field_Double& fd) = 0;
};


struct Field_With_Visitor
{
    virtual void    accept_visitor(Visitor_Base& vb) = 0;
};

struct Field_Integer
{
    void    accept_visitor(Visitor_Base& vb)
    {
        vb.process(*this);
    }
};

The record using the `Visitor_Base`:
struct Record_Using_Visitor
{
    void accept_visitor(Visitor_Base& vistor)
    {
        Field_Container::iterator   iter;
        for (iter = m_fields.begin();
             iter != m_fields.end();
             ++iter)
        {
            (*iter)->accept_visitor(rv);
        }
        return;
    }
};

我目前的障碍是使用MySQL C ++ Connector和wxWidgets处理BLOB字段。

您可能还想在以下问题中添加标记:MySQLdatabase

答案 2 :(得分:0)

boost有一个序列化库(我从未使用过它)

或XML或JSON