如何使用一个结果集处理程序消除多个类似的SQL查询?

时间:2015-05-22 03:29:31

标签: c++ database qt c++11 sqlite

我有一些类似的SQL查询,所有选中的字段都相同,只是条件不同。因此,结果集处理程序也是相同的。我使用sqlite3和sqlite_modern_cpp

以下是两个类似的功能(还有其他几个):

QVariantMap getItemById(int id) {
  QVariantMap map;
  database db(dbfile);
  db << "select id,name,price,qty from items where id=?"
     << id 
     >> [&] (int id, std::string name, double price, int qty) {
     map.insert("id", id);
     map.insert("name", QString::fromStdString(name));   
     map.insert("price", price);
     map.insert("qty", qty);
  };
  return map;
}

QVariantMap getItemByCatAndSeq(int cat, int seq) {
  QVariantMap map;
  database db(dbfile);
  db << "select id,name,price,qty from items where cat=? and seq=?"
     << cat << seq 
     >> [&] (int id, std::string name, double price, int qty) {
     map.insert("id", id);
     map.insert("name", QString::fromStdString(name));   
     map.insert("price", price);
     map.insert("qty", qty);
  };
  return map;
}

您看,查询中的所有字段都是“id,name,price,qty”,结果集处理程序也是相同的。

一个结果集处理程序应该足够了,但我必须在每个函数中复制并粘贴相同的代码。此外,所选字段的名称和顺序必须与参数resultset handler - lambda function。

匹配

如何消除这种重复的代码?有更好的解决方案吗?提前谢谢。

修改

我尝试了仿函数方法,如下所示:

struct ItemResultHandler {
    explicit ItemResultHandler(QVariantMap& map_) : map(map_) {}
    void operator() ( int id, std::string name, double price, int qty ) {
         map.insert("id", id);
         map.insert("name", QString::fromStdString(name));   
         map.insert("price", price);
         map.insert("qty", qty);
    }
    QVariantMap& map;
};

以上两个类似的功能之一将改为:

QVariantMap getItemById(int id) {
  QVariantMap map;
  database db(dbfile);
  db << "select id,name,price,qty from items where id=?"
     << id 
     >> ItemResultHandler(map);
  return map;
}

但是,它有编译错误:

../../myapp/src/utility/function_traits.h:12: error: type 'void (ItemResultHandler::*)(std::__1::basic_string<char>, std::__1::basic_string<char>, int, int)' cannot be used prior to '::' because it has no members
        decltype(&Function::operator())
                  ^

 ../../myapp/src/sqlite_modern_cpp.h:218: error: incomplete definition of type 'sqlite::utility::function_traits<ItemResultHandler>'
                        binder<traits::arity>::run(*this, func);
                               ~~~~~~^~

1 个答案:

答案 0 :(得分:1)

映射我们处理的所有不同结果类型的问题。所以模板有点复杂,看起来它不处理这种情况。

以下是如何使用类型扩展来完成此操作的示例。

// The thing you want
struct Item {
    int id;
    string name;
    double price;
    int qty;
};
// to get it back we need to give a deserialization method:
namespace sqlite {
    template<> void get_col_from_db(database_binder& db, int inx, ::Item*& ret) {   // specify the type it will be used on, a pointer is necessary if you’re type has no default constructor. Otherwise a object ref does the job too.
        ret = new Item();
        get_col_from_db(db, inx++, ret->id);
        get_col_from_db(db, inx++, ret->name);
        get_col_from_db(db, inx++, ret->price);
        get_col_from_db(db, inx++, ret->qty);
    }
}

{// usage
    Item* item;
    db << "select val,name,price,qty from test" >> item;
    std::cout << item->name;
    delete item;
}

但这很丑陋,肯定需要改变,我将在下一版本中摆脱这个指针混乱。

然而有一个更短的版本,只需用这种方式将你的仿函数转换为lambda,用你的结构替换你的QMap:

auto ItemResultHandler = [](Item& item) -> std::function<void(int, std::string , double , int )> {
    return [&item](int id, std::string name, double price, int qty) mutable {
        item.id = id;
        item.name = name;
        item.price = price;
        item.qty = qty;
    };
};

只需像这样使用它:

Item item;
db << "select val,name,price,qty from test" >> ItemResultHandler(item);