Visual C++ 2008进行MySQL编程这个系列已经出了好几篇了,如下:
visual c++ 2008进行MySQL编程(ODBC) -- (一) 套装安装
visual c++ 2008进行MySQL编程(ODBC) --(二) CDatabase操作数据库
visual c++ 2008进行MySQL编程(ODBC) --三 查询数据库
visual c++ 2008进行MySQL编程(ODBC)-- (四) 终极实现 之 派生CRecordset 上
在上面的CDatabase操作数据库这块,使用的是CDatabase这个类执行一个我们要操作数据库的sql语句,比如:
CString sql_str; sql_str.Format(_T("delete from customer where cust_id = %d"), cust_id); m_db_opr.ExecuteSQL(sql_str);
现在不用了,我们使用CRecordset类的这几个函数来实现这一切:
AddNew Prepares for adding a new record. Call Update to complete the addition. Cancel Update Cancels any pending updates due to an AddNew or Edit operation. Delete Deletes the current record from the recordset. You must explicitly scroll to another record after the deletion. Edit Prepares for changes to the current record. Call Update to complete the edit. Update Completes an AddNew or Edit operation by saving the new or edited data on the data source.
具体这几个函数怎么用的,有空可以参考一下MSDN,这里我只补充一点,就是,AddNew和Edit操作调用之后,一定要调用Update才会更新到数据库,而Delete就不用了。
接着上一讲,我们使用MFC ODBC机制从CRecordset派生了自己的类Ccostomer:
class Ccustomer : public CRecordset { public: Ccustomer(CDatabase* pDatabase = NULL); DECLARE_DYNAMIC(Ccustomer) // Field/Param Data
在customer.c里面有如此一段:
#error Security Issue: The connection string may contain a password // The connection string below may contain plain text passwords and/or // other sensitive information. Please remove the #error after reviewing // the connection string for any security related issues. You may want to // store the password in some other form or use a different user authentication. CString Ccustomer::GetDefaultConnect() { return _T("DSN=chh1;DESCRIPTION={\x8fd9\x4e2a\x662fchh1 database table};SERVER=127.0.0.1;UID=zhoutianzuo;PWD=000000;DATABASE=chh1;PORT=3306"); }
删除#error,修改为:
CString Ccustomer::GetDefaultConnect() { return _T("DSN=chh1;SERVER=127.0.0.1;UID=zhoutianzuo;PWD=000000;DATABASE=chh1;PORT=3306"); }
然后在类Ccustomer里面添加两个变量,一个是id一个是name,这两个是和数据库对应的绑定值的设计了:
public: int db_cust_id; CString db_cust_name;
然后,我们注册变量和数据库变量的关系,修改函数DoFieldExchange如下:
void Ccustomer::DoFieldExchange(CFieldExchange* pFX) { pFX->SetFieldType(CFieldExchange::outputColumn); // Macros such as RFX_Text() and RFX_Int() are dependent on the // type of the member variable, not the type of the field in the database. // ODBC will try to automatically convert the column value to the requested type RFX_Int(pFX, _T("[cust_id]"), db_cust_id); RFX_Text(pFX, _T("[cust_name]"), db_cust_name); }
实际上就是添加了两个绑定关系,这里一定要注意了,_T("[cust_id]"),中括号里面的cust_id,一定要和数据库里面的变量一致,中括号可以不要,有一个情况一定要的,就是数据库变量里面存在空格,比如定义为:cust id。
还要注意个东西,就是我们可以看到构造函数里面,有这么定义:
Ccustomer::Ccustomer(CDatabase* pdb) : CRecordset(pdb) { m_nFields = 0; m_nDefaultType = dynaset; }
这个m_nFields是什么呢?这个就是我们绑定的量个数,所以我们要修改如下:
Ccustomer::Ccustomer(CDatabase* pdb) : CRecordset(pdb) { m_nFields = 2; m_nDefaultType = dynaset; }
因为我们注册了两个绑定量,所以要相应修改为2,如果不是2呢?那么程序运行的时候会抛出异常的。不信可以试试,当然很多人是忘记了,忘记了,就看看异样的异常长啥样,也不错的嘛。呵呵。
我们在工程的类Cmy_dbDlg里面给对话框在第二篇文章里面做了add edit和del的操作,这里我们拿add的实现做一个修改,重写其实现,之前的实现是:
void Cmy_dbDlg::OnBnClickedAddNew() { // TODO: Add your control notification handler code here int cust_id = 0 ; CString cust_name; if(!GetCustIdFromEdit(cust_id)) { AfxMessageBox(_T("NULL cust id, return")); return; } if(!m_cust_name.GetWindowTextLengthW()) { AfxMessageBox(_T("NULL cust name, return")); return; } m_cust_name.GetWindowTextW(cust_name); try { CString sql_str; sql_str.Format(_T("insert customer value (%d, '%s')"), cust_id, cust_name); m_db_opr.ExecuteSQL(sql_str); } catch(CDBException* pe) { // The error code is in pe->m_nRetCode pe->ReportError(); pe->Delete(); } }
我们不做sql语句的执行了,我们用派生的类来实现,不过在my_dbDlg.cpp的开始要添加头文件customer.h的支持:
#include "stdafx.h" #include "my_db.h" #include "my_dbDlg.h" #include "customer.h" #ifdef _DEBUG #define new DEBUG_NEW #endif
在重写OnBnClickedAddNew的实现:
void Cmy_dbDlg::OnBnClickedAddNew() { // TODO: Add your control notification handler code here int cust_id = 0 ; CString cust_name; if(!GetCustIdFromEdit(cust_id)) { AfxMessageBox(_T("NULL cust id, return")); return; } if(!m_cust_name.GetWindowTextLengthW()) { AfxMessageBox(_T("NULL cust name, return")); return; } m_cust_name.GetWindowTextW(cust_name); Ccustomer my_record(&m_db_opr); try { my_record.Open(CRecordset::snapshot, _T("customer")); my_record.AddNew(); my_record.db_cust_id = cust_id; my_record.db_cust_name = cust_name; if(!my_record.Update()) { AfxMessageBox(_T("Add New failed!")); } } catch(CDBException* pe) { // The error code is in pe->m_nRetCode pe->ReportError(); pe->Delete(); } }
还是解释一下代码,不管是增删还是修改,首先必须打开数据库先,修改和删除需要先判断条目是否存在,而增加就不用了,如果不允许重复条目,而插入了重复条目,也会抛出异常的,但是没有关系,就权当是一个提醒,没有任何危害。而上面的Open函数,就相当的需要解释了,在绑定了数据库变量后,第二个参数仅仅需要给出一个数据库名字customer,相当于是 “"select cust_id, cust_name from customer",至于这个Open该怎么输入,具体有MSDN为参考:
The lpszSQL Parameter and the Resulting SQL String
Case |
What you pass in lpszSQL |
The resulting SELECT statement |
---|---|---|
1 |
NULL |
SELECTrfx-field-listFROMtable-name CRecordset::Open callsGetDefaultSQL to get the table name. The resulting string is one of cases 2 through 5, depending on whatGetDefaultSQL returns. |
2 |
A table name |
SELECTrfx-field-listFROMtable-name The field list is taken from the RFX statements inDoFieldExchange. Ifm_strFilter andm_strSort are not empty, adds theWHERE and/orORDER BY clauses. |
3 * |
A complete SELECT statement but without aWHERE orORDER BY clause |
As passed. If m_strFilter andm_strSort are not empty, adds theWHERE and/orORDER BY clauses. |
4 * |
A complete SELECT statement with aWHERE and/orORDER BY clause |
As passed. m_strFilter and/orm_strSort must remain empty, or two filter and/or sort statements are produced. |
5 * |
A call to a stored procedure |
As passed. |
* m_nFields must be less than or equal to the number of columns specified in theSELECT statement. The data type of each column specified in theSELECT statement must be the same as the data type of the corresponding RFX output column.
具体的含义我就不翻译了,计算机这个玩意总要懂一点英语,有时候我翻译出来也总有词不达意的时候。
那么我们看看运行结果如何吧,在添加之前,我们查看数据库:
添加一个条目,id为45,name为‘李四’:
添加后的结果如下:
很好,代码也简单多了,易于扩展,方便设计。
剩下Edit Delete操作,我就不做了,大家自己做做哈,有问题联系我哈哈。
下一文,就写一下遍历数据库,同时,涉及参数化等操作,那么这个系列就算了完美结束了。