C++开源项目:mysqlpp Row类

240 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情

本文是我之前在微信公众号上的一篇文章记录。原链接为:MySQL++学习笔记:Row类

前面我们讲了很多关于mysql++的,比如Query,Result以及type等。上一章中result类都用到了Row类型,而且我们也知道数据库查询结果的操作基本就是列和行,所以Row这个角色很重要。下面我们就来看看Row这个类型到底是什么。

成员变量

惯例了,先来看看它的成员变量,主要的有两个:data_ 和field_names_ :

list_type data_;
RefCountedPointer<FieldNames> field_names_;
bool initialized_;

list_type是什么?看下面:

/// \brief type of our internal data list
///
/// This is public because all other typedefs we have for
/// mirroring std::vector's public interface depend on it.
typedef std::vector<String> list_type;
​
/// \brief constant iterator type
typedef list_type::const_iterator const_iterator;
​
/// \brief constant reference type
typedef list_type::const_reference const_reference;
​
/// \brief const reverse iterator type
typedef list_type::const_reverse_iterator const_reverse_iterator;
​
/// \brief type for index differences
typedef list_type::difference_type difference_type;
​
/// \brief iterator type
///
/// Note that this is just an alias for the const iterator.  Row
/// is immutable, but people are in the habit of saying 'iterator'
/// even when they don't intend to use the iterator to modify the
/// container, so we provide this as a convenience.
typedef const_iterator iterator;
​
/// \brief reference type
///
/// \sa iterator for justification for this const_reference alias
typedef const_reference reference;
​
/// \brief mutable reverse iterator type
///
/// \sa iterator for justification for this const_reverse_iterator
/// alias
typedef const_reverse_iterator reverse_iterator;
​
/// \brief type of returned sizes
typedef list_type::size_type size_type;
​
/// \brief type of data in container
typedef list_type::value_type value_type;

在类型声明的一开头,mysql++首先就typedef了list_type相关的迭代器,反响迭代器,引用迭代器,元素类型值,元素类型长度等信息。而且可以看到迭代器都是const类型的。那就顺便提一下:

Row中所有的方法定义都是const的,可以理解为Row是操作MySQL C API中返回的Row,也就是数据库数据,理论上是不允许修改的,所以定义为const很nice。

FieldNames是派生自std::vector<std::string>的类型,从它的注释就看出来了,他是用来存储SQL查询结果的field names的

/// \brief Holds a list of SQL field names
class FieldNames : public std::vector<std::string>

构造函数

直接上源码:

Row::Row(MYSQL_ROW row, const ResultBase* res,
        const unsigned long* lengths, bool throw_exceptions) :
OptionalExceptions(throw_exceptions),
initialized_(false)
{
    if (row) {
        if (res) {
            size_type size = res->num_fields();
            data_.reserve(size);
            for (size_type i = 0; i < size; ++i) {
                bool is_null = row[i] == 0;
                data_.push_back(value_type(
                        is_null ? "NULL" : row[i],
                        is_null ? 4 : lengths[i],
                        res->field_type(int(i)),
                        is_null));
            }
​
            field_names_ = res->field_names();
            initialized_ = true;
        }
        else if (throw_exceptions) {
            throw ObjectNotInitialized("RES is NULL");
        }
    }
    else if (throw_exceptions) {
        throw ObjectNotInitialized("ROW is NULL");
    }
}

这个构造函数也比较简单,主要以下四部分:

  • 1.根据fields number给data_ 分配内存:data_.reserve(size);
  • 2.遍历这个row,将每一个field构造成一个value_type,并把这个value_type push_back到data_ 中,如果这个field是null就构造一个“NULL”的value_type。
  • 3.然后就是给field_names_ 和 initialized_ 赋值。
  • 4.最后是异常处理部分。

第三点value_type就是vector<String>::value_type,说更明白其实就是String类型。我们还没讲过String这个类型,看下它的构造函数:

/// \brief Full constructor.
///
/// \param str the string this object represents, or 0 for SQL null
/// \param len the length of the string; embedded nulls are legal
/// \param type MySQL type information for data within str
/// \param is_null string represents a SQL null, not literal data
///
/// The resulting object will contain a copy of the string buffer.
/// The buffer will actually be 1 byte longer than the value given
/// for \c len, to hold a null terminator for safety.  We do this
/// because this ctor may be used for things other than
/// null-terminated C strings.  (e.g. BLOB data)
explicit String(const char* str, size_type len,
    mysql_type_info type = mysql_type_info::string_type,
    bool is_null = false)

String用到了之前介绍过的mysql_type_info。这样子即使你没有看过String的构造函数,只看这个声明你也已经很明白了,这个String很神通广大,他存储了field的值以及具体的类型!也就是说,MySQL++就用了一个String包装了MYSQL C API中的行信息和行所对应的列的信息。

成员方法

下面简单说一下它的成员方法吧

/// \brief Get a const reference to the field given its index
///
/// \throw mysqlpp::BadIndex if the row is not initialized or there
/// are less than \c i fields in the row.
const_reference at(size_type i) const;
​
/// \brief Get a reference to the last element of the vector
const_reference back() const { return data_.back(); }
​
/// \brief Return a const iterator pointing to first element in the
/// container
const_iterator begin() const { return data_.begin(); }
​
/// \brief Returns true if container is empty
bool empty() const { return data_.empty(); }
​
/// \brief Return a const iterator pointing to one past the last
/// element in the container
const_iterator end() const { return data_.end(); }
​
/// \brief Get a reference to the first element of the vector
const_reference front() const { return data_.front(); }
​
/// \brief Return maximum number of elements that can be stored
/// in container without resizing.
size_type max_size() const { return data_.max_size(); }
​
/// \brief Return reverse iterator pointing to first element in the
/// container
const_reverse_iterator rbegin() const { return data_.rbegin(); }
​
/// \brief Return reverse iterator pointing to one past the last
/// element in the container
const_reverse_iterator rend() const { return data_.rend(); }
​
/// \brief Get the number of fields in the row.
size_type size() const { return data_.size(); }

这一类就没什么好讲的了,都是直接复用vector中的通用方法,知识每个方法都加上了const,也就是不允许外部修改这个类成员变量。

/// \brief Returns a field's index given its name
Row::size_type
Row::field_num(const char* name) const
{
    if (field_names_) {
        return (*field_names_)[name];
    }
    else if (throw_exceptions()) {
        throw BadFieldName(name);
    }
    else {
        return 0;
    }
}
​
/// \brief Get the value of a field given its name.
///
/// If the field does not exist in this row, we throw a BadFieldName
/// exception if exceptions are enabled, or an empty row if not.
/// An empty row tests as false in bool context.
///
/// This operator is fairly inefficient.  operator[](int) is faster.
const_reference operator [](const char* field) const
{
    size_type si = field_num(field);
    if (si < size()) {
        return at(si);
    }
    else if (throw_exceptions()) {
        throw BadFieldName(field);
    }
    else {
        static value_type empty;
        return empty;
    }
}
​
/// \brief Get the value of a field given its index.
///
/// This function is just syntactic sugar, wrapping the at() method.
///
/// It's \b critical that the parameter type be \c int, not
/// \c size_type, because it will interfere with the \c const
/// \c char* overload otherwise.  row[0] is ambiguous when there
/// isn't an int overload.
///
/// \throw mysqlpp::BadIndex if the row is not initialized or there
/// are less than \c i fields in the row.
const_reference operator [](int i) const
    { return at(static_cast<size_type>(i)); }

这几个就是获取field下标和field值的方法了,获取field值有两个,一个是通过field的字符串获取,另一个是通过下标来获取。

最后,还有几个方法是跟SSQLS相关的

  • equal_list:用于生成"equals clause",例如在SELECT语句中的WHERE就是一个典型的"equals clause",例如:SELECT * FROM Tbl1 WHERE id = ‘1’, name = ‘root’

    这个是需要配合使用equal_list_b结构以及一个用于做quoting和escaping的类型。

  • field_list:主要用于返回各种FieldNames。这个函数会有一些输入(某些重构是vector<bool>,有些就是很多bool参数)来表明一个row中哪些field是需要被返回到结果集中的。不需要的就用false。

  • value_list:这个函数是获取mysqlpp::Row的整个(或者部分)值的函数。这个函数会有一些输入(某些重构是vector<bool>,有些就是很多bool参数)来表明一个row中哪些field是需要被返回到结果集中的。不需要的就用false。

用例

用例1:使用field字符串来查询value:

mysqlpp::Query query = conn.query("select * from stock");
if (mysqlpp::UseQueryResult res = query.use()) {
    // Display header
    cout.setf(ios::left);
    cout << setw(31) << "Item" <<
            setw(10) << "Num" <<
            setw(10) << "Weight" <<
            setw(10) << "Price" <<
            "Date" << endl << endl;
    // Get each row in result set, and print its contents
    while (mysqlpp::Row row = res.fetch_row()) {
        cout << setw(30) << row["item"] << ' ' <<
                setw(9) << row["num"] << ' ' <<
                setw(9) << row["weight"] << ' ' <<
                setw(9) << row["price"] << ' ' <<
                setw(9) << row["sdate"] <<
                endl;
    }
}

用例2:使用field下标来查询value:

mysqlpp::Query query = conn.query("select * from stock");
if (mysqlpp::UseQueryResult res = query.use()) {
	// Display header
	cout.setf(ios::left);
	cout << setw(31) << "Item" <<
			setw(10) << "Num" <<
			setw(10) << "Weight" <<
			setw(10) << "Price" <<
			"Date" << endl << endl;
	// Get each row in result set, and print its contents
	while (mysqlpp::Row row = res.fetch_row()) {
		cout << setw(30) << row[0] << ' ' <<
				setw(9) << row[1] << ' ' <<
				setw(9) << row[2] << ' ' <<
				setw(9) << row[3] << ' ' <<
				setw(9) << row[4] <<
				endl;
	}
}

好了,今天就讲到这里了。