持续创作,加速成长!这是我参与「掘金日新计划 · 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;
}
}
好了,今天就讲到这里了。