重要概念
整个Data数据库架构独立于具体的数据库实现,对数据库操作抽象出一套通用接口,体现在几个重要的概念,如Statement、Session、RecordSet、Transaction,其他的一些概念都是为了实现上述的重要概念而引申出来的,比Binder、Extractor、Preparerator、Column等。
AbstractBinder
绑定操作的具体实现者,每一个具体的数据库需要提供相应子类实现,会被AbstractBinding关联。
案例分析:
Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db");
tmp << "DROP TABLE IF EXISTS Simpsons", now;
tmp << "CREATE TABLE IF NOT EXISTS Simpsons (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Age INTEGER(3))", now;
tmp << "INSERT INTO Simpsons VALUES(?, ?, ?, ?)", useRef("Simpson"), useRef("Bart"), useRef("Springfield"), useRef(age), now;
=======================================================
tmp << "INSERT INTO Simpsons VALUES(?, ?, ?, ?)" - 返回一个Statement;
useRef("Simpson") - 返回一个Binding;
Statement重载的,操作符会添加这些binding到Statement(StatementImpl)的成员变量AbstractBindingVec _bindings;
AbstractBinding
绑定操作的对外接口,关联一个Binder作为其实现。即,外界调用AbstractBinding的bind接口会转发去调用Binder的bind函数。
AbstractExtractor
提取操作的具体实现者,每一个具体的数据库需要提供相应子类实现,会被AbstractExtraction关联。
AbstractExtraction
提取操作的对外接口,关联一个Extractor作为其实现。即,外界调用AbstractExtraction的extract接口会转发去调用Extractor的extract函数。
AbstractPreparator
类似同上AbstractExtractor,Prepare等效于数据库中的存储过程,当反复调用一类似操作,只是参数不同时,可以存储第一次编译后的语句,替换不同的参数,减少编译耗时,提高运行效率。目前SQLite不支持此功能。
AbstractPreparation
类似同上AbstractExtraction
Statement
Statement代表着一条可执行的SQL语句,一条SQL语句有各个操作符组成,所以Statement类需要有收集这些操作符组成一个完整的可执行的SQL语句接口函数,Statement的实现里面是通过重载各种逗号(,)操作符,达到级联收集Statement操作符(包括添加Binding、Extraction)效果。同时从数据库的操作流程上来看,我们一般都是先绑定参数,然后执行,再提取结果,这就需要绑定(Binding)和提取(Extratraction)功能作为接口,以及真正干活的Binder和Extractor。一个Statement对应一维数组个Bindings,而这些Bindings共用一个Binder,对应二维数组个Extratractions,这些Extratractions共用一个extractor。
案例分析1:(Session和Statement运行关系)
ses << "CREATE TABLE Dummy (data INTEGER(10))", now;
首先调用的是session的<<函数,参数是字符串,如下:
template <typename T>
Statement operator << (const T& t)
/// Creates a Statement with the given data as SQLContent
{
return _statementCreator << t;
}
返回一个Statement,然后调用Statement的,操作符,参数是now函数指针
Statement& operator , (Manipulator manip);
案例分析2:(添加Binding)
int i = 0;
Statement stmt = (ses << "INSERT INTO Dummy VALUES(:data)", use(i));
首先调用的是session的<<函数,参数是字符串,如下:
template <typename T>
Statement operator << (const T& t)
/// Creates a Statement with the given data as SQLContent
{
return _statementCreator << t;
}
Statement& operator , (AbstractBinding::Ptr pBind);
案例分析3:(添加Extraction)
/// std::set<int> retData;
/// ses << "SELECT * FROM Dummy", into(retData));
首先调用的是session的<<函数,参数是字符串,如下:
template <typename T>
Statement operator << (const T& t)
/// Creates a Statement with the given data as SQLContent
{
return _statementCreator << t;
}
Statement& operator , (AbstractExtraction::Ptr extract);
Session
Session表示一个建立对数据连接的会话,从功能上有以下接口:第一、和会话相关的属性的设置和读取接口;第二、数据库操作接口,包括Connect/Open/Begin/Commit/Rollback/Close;第三、每一个Session内含SQL语句创建器(StatementCreator),重载Session的<<操作符,返回一个在此会话之上的Statement,然后就是对Statement的设置属性、绑定、提取等(都是通过重载逗号)操作。
Transaction
对应的是数据库中的事务,表达的是一个逻辑上的整体,不可分割。需要符合ACID准则。Transaction内含一个Session和Logger,分别用于说明在哪个Session上的事务,以及可以记录日志。
重要的实现分析:
template <typename T>
Transaction(Poco::Data::Session& rSession, T& t, Poco::Logger* pLogger = 0):
_rSession(rSession),
_pLogger(pLogger)
{
try { transact(t); }
catch (...)
{
if (_pLogger) _pLogger->error("Error executing transaction.");
rollback();
throw;
}
}
构造函数里面调用transact函数
template <typename T>
void transact(T& t)
/// Executes the transactor and, unless transactor throws an exception,
/// commits the transaction.
{
if (!isActive()) begin();
t(_rSession); //
仿函数,即重载了operator ()的函数
commit();
}
在transact函数里调用T类型的仿函数,即t(_rSession);所以我们在具现的时候提供的T类型是有一些要求的,例如:
/// Example usage:
///
/// struct Transactor
/// {
/// void operator () (Session& session) const
/// {
/// // do something ...(添加事务逻辑业务的地方)
/// }
/// };
///
/// Transactor tr;
/// Transaction tn(session, tr);
RecordSet
RecordSet是SQL语句(Statement)执行的结果,即一张二维表格,从数据结构的角度看,二维表格本质上是一个容器,STL里面对容器的访问都是通过迭代器(Iterator)的方式,所以才有了RowIterator类的存在,对于RowIterator的解引用是每一行的数据,抽象成用Row来表示,我们也能想象出Row的成员变量中应该含有描述每一个变量的名称(类似题头)和对应的数值的结构,具体的代码里面是采用两个一一对应的数组来表示。对于每一行的显示样式,就通过RowFormatter来控制。另外需要提到的是RowFilter,我们在使用数据库查询的时候经常会带上各种过滤条件(比如大于xx等),因此RowFilter实现Statement执行结果RecordSet的过滤。
AbstractTypeHandler和TypeHandler
提供Binding关联Binder,Extraction关联Extractor,Prepareration关联Prepartor的便捷函数。当需要绑定、提取结构体的时候,需要偏特化TypeHandler。
/// template <>
/// class TypeHandler<struct Person>
/// {
/// public:
/// static std::size_t size()
/// {
/// return 3; // lastName + firstname + age occupy three columns
/// }
///
/// static void bind(std::size_t pos, const Person& obj, AbstractBinder::Ptr pBinder, AbstractBinder::Direction dir)
/// {
/// // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Age INTEGER(3))
/// // Note that we advance pos by the number of columns the datatype uses! For string/int this is one.
/// poco_assert_dbg (!pBinder.isNull());
/// TypeHandler<std::string>::bind(pos++, obj.getLastName(), pBinder, dir);
/// TypeHandler<std::string>::bind(pos++, obj.getFirstName(), pBinder, dir);
/// TypeHandler<int>::bind(pos++, obj.getAge(), pBinder, dir);
/// }
///
/// static void prepare(std::size_t pos, const Person& obj, AbstractPreparator::Ptr pPreparator)
/// {
/// // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Age INTEGER(3))
/// poco_assert_dbg (!pPreparator.isNull());
/// TypeHandler<std::string>::prepare(pos++, obj.getLastName(), pPreparator);
/// TypeHandler<std::string>::prepare(pos++, obj.getFirstName(), pPreparator);
/// TypeHandler<int>::prepare(pos++, obj.getAge(), pPreparator);
/// }
///
/// static void extract(std::size_t pos, Person& obj, const Person& defVal, AbstractExtractor::Ptr pExt)
/// {
/// // defVal is the default person we should use if we encunter NULL entries, so we take the individual fields
/// // as defaults. You can do more complex checking, ie return defVal if only one single entry of the fields is null etc...
/// poco_assert_dbg (!pExt.isNull());
/// std::string lastName;
/// std::string firstName;
/// int age = 0;
/// // the table is defined as Person (LastName VARCHAR(30), FirstName VARCHAR, Age INTEGER(3))
/// TypeHandler<std::string>::extract(pos++, lastName, defVal.getLastName(), pExt);
/// TypeHandler<std::string>::extract(pos++, firstName, defVal.getFirstName(), pExt);
/// TypeHandler<int>::extract(pos++, age, defVal.getAge(), pExt);
/// obj.setLastName(lastName);
/// obj.setFirstName(firstName);
/// obj.setAge(age);
/// }
/// };
问题
1. Statement和Session是什么关系?
2. Session和Transaction是什么关系?
3. Statement(StatementImpl)里面为什么Bindings是一维数组和Extractions是二维数组?
4. Session里面的Property和Feature有什么区别?
5. StatementImpl成员变量_columnsExtracted和_subTotalRowCount含义和区别?