сериализация объектов в C ++ и сохранение в виде типа BLOB-объектов в MySQL - PullRequest
3 голосов
/ 03 мая 2010

Я использую соединитель 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 ]

6 голосов
/ 03 мая 2010

Вы идете об этом совершенно неправильно. Это не «сериализация», на самом деле это вполне возможно противоположность сериализации - это просто попытка записать дамп памяти вектора в базу данных. Представьте на секунду, что вектор выглядит примерно так:

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 голос
/ 03 мая 2010

Мое решение для записи в базу данных 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 INSERT и SELECT.

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

Используя более верный шаблон проектирования Visitor , специфика SQL перемещается в посетителя. Посетитель знает атрибуты поля благодаря вызванному методу. Это сокращает структуру Field до наличия только методов get_field_name и get_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;
    }
};

Моя текущая задача - обрабатывать BLOB-поля с помощью MySQL C ++ Connector и wxWidgets.

Вы также можете добавить теги: MySQL и database к следующим вопросам.

0 голосов
/ 03 мая 2010

Boost имеет библиотеку сериализации (я никогда не использовал ее)

или XML или JSON

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...