上一篇讲到了根据一个typelist实现一个结构,这个结构带有typelist中所有类型的成员,同时,可以通过Field0,Field1等接口访问和赋值.在这个基础上,我们可以构建对应一条记录的结构data_row,
二.一条记录
#define ROW_NO_CHANGE 0
#define ROW_ADD 1
#define ROW_UPDATE 2
#define ROW_DELETE 3
template<typename _tlist>
class data_op_record_row : public struct_mem<_tlist>
{
public:
enum _enum_RowState {
_en_Row_NoChange = ROW_NO_CHANGE, //未修改
_en_Row_Add = ROW_ADD, //新增记录
_en_Row_Update = ROW_UPDATE, //更新
_en_Row_Del = ROW_DELETE, //删除
} m_en_State;
public:
void SetState(int newstate) {
if (m_en_State != newstate) {
if (newstate == _en_Row_NoChange ||
newstate == _en_Row_Add ||
newstate == _en_Row_Update ||
newstate == _en_Row_Del)
{
m_en_State = (_enum_RowState)newstate;
}
}
}
public:
data_op_record_row () : m_en_State(_en_Row_NoChange) {}
~data_op_record_row (){}
protected:
private:
};
上面的类增加了一个枚举,标识这条记录当前的状态,在记录集中,将根据记录的状态进行相关的操作.这个其实类似与.net中的DataRow,不过.net中的DataRow可以用wizzard生成需要的类,而这里是用模板生成的.
三.数据类型转换
数据库中的表对应的字段有不同的数据类型,ADO操作接口中用不同的数据类型与之对应,其中Field接口的GetType就是返回字段类型的,但是ADO的GetValue返回的是_variant_t类型,为了实际业务的需要,还需要转换成不同的类型,因此,需要对_variant_t类型进行转换.为此需要提供模板转换函数
template <typename _type>
void DbData_Change(_type& ret, _variant_t& field)
{
AfxMessageBox("未定义的数据转换");
}
template <>
void DbData_Change<int>(int & ret, _variant_t& field)
{
ret = field.lVal;
}
template <>
void DbData_Change<std::string>(std::string& ret, _variant_t& field)
{
_bstr_t temp;
if (field.vt != VT_NULL) {
temp = field.bstrVal;
ret = temp.operator const char*();
}
}
template <>
void DbData_Change<bool>(bool& ret, _variant_t& field)
{
ret = (field.boolVal == (short)0xFFFF ? TRUE : FALSE);
}
template <>
void DbData_Change<double>(double& ret, _variant_t& field)
{
if (field.vt != VT_NULL) {
//ret = field.cyVal;
ret = field.dblVal;
}else {
ret = 0;
}
}
上面的模板函数的功能是从_variant_t转换到某中数据类型,对于其它类型还可以进行扩充,对于没有进行特化的函数,编译器会使用缺省模板,在运行时候就会出现提示.有了上面的函数,现在我们需要一个数据类型的封装类,使得被封装的类型可以自动进行从_variant_t类型的需要类型的转换,所以有了下面的封装类.
//数据库数据类型封装
template <typename _type>
class _db_data_wrapper
{
public:
_type& Value() {
return m_Data;
}
const _type& operator =(const _type& value) {
m_Data = value;
return value;
}
_db_data_wrapper& operator = (const _db_data_wrapper& other) {
if (this != &other) {
m_Data = other.m_Data;
}
return *this;
}
public:
virtual void GetDbValue(_variant_t& field_value) {
DbData_Change(m_Data, field_value);
}
public:
_db_data_wrapper(){}
_db_data_wrapper(const _type& value) : m_Data(value) {}
~_db_data_wrapper(){}
private:
_type m_Data;
};
typedef _db_data_wrapper< int > DB_INT;
typedef _db_data_wrapper< std::string > DB_STRING;
typedef _db_data_wrapper< bool > DB_BOOL;
typedef _db_data_wrapper< double > DB_DOUBLE;
上面的代码预定义了几种常用的数据类型,这个数据的封装类支持 = 操作符,因此可以直接用被封装的数据对其赋值.象下面
int i = 4;
DB_INT di;
di = i;
i = di.Value();
同时通过成员函数GetDbValue(_variant_t& field_value), 支持从_variant_t转换为自身封装的类型.
现在,可以用上面的数据类型构建记录结构了
typedef data_op_record_row< TYPELIST_2(DB_INT,DB_STRING) > my_DataRow;
在上面的my_DataRow类型中,有两个成员Field0, Field1,类型分别为 DB_INT,DB_STRING, 支持_variant_t类型转换.
当然,用wrapper对数据进行封装在实际的使用中毕竟有些麻烦, 如数据必须通过 Value() 函数来取得,如果直接使用象 int, std::string 等类型作为 记录结构的成员,也可以实现_variant自动转换,但是需要在上一层的记录集类中加入相关的转换操作,实现起来比较麻烦,所以我这里选择了对数据进行封装.