The Code Project View our sponsorsCodejock Software - Serious GUI CodeAdvertise on the CodeProject
Home >> Database >> ODBC

CODBCRecordset class
By Stefan Chekanov

CODBCRecordset class is intended to be full replacement of all ClassWizard generated CRecordset derived classes in MFC projects. 
 VC6, Win9x, NT4, MFC
 Posted 12 Apr 2000
 Updated 7 Jan 2001
Articles by this author
Send to a friend
Printer friendly version
Home Latest updates Submit your article About Us Advertise on the Code Project Contact us Discussion Forums
Navigation bar
56 users have rated this article. result:
4.88 out of 5.
  • Download source files - 13 Kb
  • Introduction

    In a usual MFC database project we have a lot of CRecordset derived classes generated by ClassWizard. The class presented here, CODBCRecordset is a very easy to use replacement of all these CRecordset classes. CODBCRecordset is derived from CRecordset but it does not have hardcoded inside the number and the type of the database fields.

    I have seen some implementations trying to solve this problem. But CODBCRecordset has at least two advantages:

    1. CODBCRecordset can be used not only to get values from the database, but to write values into the database using the MFC way.
    2. The other implementations cannot open simultaneously more than one recordset through one database connection when the database is MS SQL Server. In some cases this could be very big problem because opening a new connection is time and resources expensive.

    Because CODBCRecordset is derived from CRecordset and uses its data exchange mechanism it is fully compatible with MFC ODBC class CDatabase .

    Each field value is stored in a CDBField object. The CDBField class is inherited from CDBVariant and has some added functionality so it's simple to use.

    CODBCRecordset and CDBField classes support all database data types. CDBField makes implicit type conversion where it is appropriate to supply the data in the requested format.

    Here is a list of CODBCRecordset and CDBField methods:

    CODBCRecordset class
    Constructor The same as for CRecordset accepting CDatabase*
    BOOL Open( LPCTSTR lpszSQL, UINT nOpenType, DWORD dwOptions ); Open the recordset
    lpszSQL is a SQL statement that returns recordset
    e.g. SELECT * FROM tablename
    nOpenType is CRecordset open type, see CRecordset::Open()
    dwOptions is CRecordset options, see CRecordset::Open()

    Note that lpszSQL and nOpenType have exchanged their positions compared to CRecordset::Open()

    short GetODBCFieldCount() Returns number of the fields (columns) in the recordset. This method is defined in CRecordset .
    int GetFieldID( LPCTSTR ) Returns a field index by given field name. It is case insensitive. CRecordset::GetFieldIndexByName() works, but is case sensitive.
    CString GetFieldName( int ) Returns a field name by given field index
    CDBField& Field( LPCTSTR )
    CDBField& Field( int )
    Through this method you can get a reference to the internal CDBField object corresponding to the specified column (See the CDBField class table for details about CDBField ).

    There are two forms of the method - with argument of type LPCTSTR szName - specifying the name of the column, and int nID - specifying the index of the coulmn in the recordset.

    These methods can be used as lvalue in expressions. Thus you can write values to the database.

    CDBField& operator( LPCTSTR )
    CDBField& operator( int )
    The function operator is defined for easy of use of the class and just calls the corresponding Field() method

    There are two forms of the function operator - with argument of type LPCTSTR szName - specifying the name of the column, and int nID - specifying the index of the coulmn in the recordset.

    These can be used as lvalue in expressions. Thus you can write values to the database.

    bool GetBool()
    unsigned char GetChar()
    short GetShort()
    int GetInt()
    long GetLong()
    float Getfloat()
    double GetDouble()
    COleDateTime GetDate()
    CString GetString()
    CLongBinary* GetBinary()
    All of these methods do the appropriate type conversions depending on their return value and the type of the underlying data in the database.

    These methods just call Field().AsXXX()
    (See the CDBField class table for details about CDBField).

    There are two forms of these methods - with argument of type LPCTSTR szName - specifying the name of the column, and int nID - specifying the index of the coulmn in the recordset.

    These cannot be used as lvalue in expressions.

     

    CDBField class
    Constructors No public constructors. CDBField cannot be instantiated except by CODBCRecordset to be used in internal structures to support data exchange. These objects are accessible through CODBCRecordset methods.
    bool AsBool()
    unsigned char AsChar()
    short AsShort()
    int AsInt()
    long AsLong()
    float AsFloat()
    double AsDoble()
    COleDateTime AsDate()
    CString AsString()
    CLongBinary* AsBinary()
    All of these methods do the appropriate type conversions depending on their return value and the type of the underlying data in the database (See the AsXXX methods table for data conversion rules).

    There is no data type Int but AsInt() is equal to AsLong()

    assignment operators There are defined assignment operators accepting bool, unsigned char, short, int, long, COleDateTime and CString. So CDBField objects can be lvalues. There is no assignemt operator accepting CLongBinary because MFC does not support writing CLongBinary values into database. These assignment operators do appropriate conversions to the underlying database column data type except CLongBinary (See the assignment operators table for data conversion rules).
    const CString& GetName() Returns the field name this object corresponds to.
    bool IsNull()
    bool IsBool()
    bool IsChar()
    bool IsShort()
    bool IsInt()
    bool IsLong()
    bool IsFloat()
    bool IsDouble()
    bool IsNumber()
    bool IsDate()
    bool IsString()
    bool IsBinary()
    Each of these return true if the field contains a value of the corresponding data type.

    There is no data type Number but IsNumber() returns true if IsShort() || IsLong() || IsFloat() || IsDouble().

    There is no data type Int but IsInt() returns true if IsLong() returns true.

     

    Conversions made by AsXXX methods

     

    Values in the database

      NULL BOOL UCHAR SHORT LONG SINGLE DOUBLE DATE STRING BINARY
    AsBool false * * * * * *   *  
    AsChar 0x32 * * * * * *   *  
    AsShort 0 * * * * * *   *  
    AsInt 0 * * * * * *   *  
    AsLong 0 * * * * * *   *  
    AsFloat 0.0 * * * * * *   *  
    AsDouble 0.0 * * * * * *   *  
    AsDate null invalid invalid * * * * * *  
    AsString empty * * * * * * * * *
    AsLongBinary NULL                 *
    Empty cells indicate the conversion is not available, thus code asserts.
    Cells marked with * indicate conversion is available (See the Conversion algorithms table for data conversion rules).

     

    Conversions made by assignment operators

    Database Field Type

    Argument of the assignment operator

      bool unsigned char short int long float double COleDateTime String
    NULL                  
    BOOL * * * * * * *   *
    UCHAR * * * * * * *   *
    SHORT * * * * * * *   *
    LONG * * * * * * *   *
    SINGLE * * * * * * *   *
    DOUBLE * * * * * * *   *
    DATE               * *
    STRING * * * * * * * * *
    BINARY                  
    Empty cells indicate the conversion is not available, thus code asserts.
    Cells marked with * indicate conversion is available (See the Conversion algorithms table for data conversion rules).

     

    Conversion algorithms
    String to Bool comparing the first character of the string with 'T'
    Char to Bool comparing the character with 'T'
    Bool to String String = (bVal) ? 'T' : 'F'
    Bool to Char Char = (bVal) ? 'T' : 'F'
    String to Number appropriate atoX() function
    Number to String CString::Format() method using the appropriate format specifier string
    String to Date COleDateTime::ParseDateTime() method
    Date to String COleDateTime::Format() method

     

    Examples of how to use CODBCRecordset

    You should include the files ODBCRecordset.h and ODBCRecordset.cpp in your project.

    I usually include this line in my StdAfx.h file.

    #include "ODBCRecordset.h"

    Here is a simple code showing how CODBCRecordset can be used.

    /////////////////////////////////////////////////////////////////////////////
    CDatabase	db;
    //	This connect string will pop up the ODBC connect dialog
    CString		cConnect = "ODBC;";
    db.Open( NULL,				//	DSN
    	 FALSE,				//	Exclusive
    	 FALSE,				//	ReadOnly
    	 cConnect,			//	ODBC Connect string
    	 TRUE				//	Use cursor lib
       );
    
    COleDateTime	dOrderDate;
    
    CODBCRecordset	rs( &db );
    rs.Open( "SELECT * FROM Orders \
    	WHERE ORDER_DATE > 'jan 1 2000' \
    	ORDER BY ORDER_DATE" );
    for( ; ! rs.IsEOF(); rs.MoveNext() )
    {
    //	The choice is yours. You may choose whatever way
    //	you want to get the values
    //
    	//	These return COleDateTime value
    	dOrderDate = rs.GetDate( "ORDER_DATE" );
    	dOrderDate = rs.Field("ORDER_DATE").AsDate();
    
    	//	These make implicit call to AsDate()
    	dOrderDate = rs("ORDER_DATE");
    	dOrderDate = rs.Field("ORDER_DATE");
    
    	//	Now edit the fields in the recordset
    	rs.Edit();
    	rs("ORDER_DATE") = "jan 1 1999";	// Implicit conversion
    	rs.Field("ORDER_DATE") = "jan 1 1999";  // Implicit conversion
    	rs.Update();
    }	//	for(....
    /////////////////////////////////////////////////////////////////////////////
    

    If ORDER_DATE is stored in the database as datetime or compatible data type the value will be get directly.
    If ORDER_DATE is stored in the database as string or compatible data type (char, varchar) the value will be converted via COleDateTime::ParseDateTime() method. If conversion fails, dOrderDate will be set to COleDatetime::invalid.

    When opening a resultset generated by join statements it is possible to get 2 or more columns that have the same name. CODBCRecordset leaves the name of the first column intact but other repeated columns are renamed with adding the number this columns repeats the name. Not repeated column names are left intact. E.g.

    SELECT * FROM Orders, Customers WHERE Orders.CUST_ID = Customers.ID

    If the table Orders have a column with name ID and Customers have a column with name ID, CODBCRecordset will rename ID from Customers to ID2 and all other not repeating column names will be intact.

    Well, here is a tip: Rename columns manualy to be sure what name they have, e.g.

    SELECT Orders.*, Customers.ID as CUSTOMERS_ID
        FROM Orders, Customers 
        WHERE Orders.CUST_ID = Customers.ID

    How does CODBCRecordset work?

    CODBCRecordset allocates storage for all fields in the resultset and uses MFC Record Field eXchange mechanism like it has been inherited from CRecordset using ClassWizard.

    [Top] Sign in to vote for this article:     PoorExcellent  

    View our sponsorsClick here for Whole Tomato Software - Home of Visual AssistAdvertise on the CodeProject

    Hint: For improved responsiveness, use Internet Explorer 4 (or above) with Javascript enabled, choose 'Use DHTML' from the View dropdown and hit 'Set Options'.
     Keyword Filter
     View   Per page   Messages since
    New threadMessages 1 to 16 of 16 (Total: 16)First Prev Next Last
    Subject 
    Author 
    Date 
      Error
    Andrew Jones 10:56 12 Jul 01 
      Reading Cyrillic,Greek etc
    Trifon 8:55 8 Jun 01 
      Re: Reading Cyrillic,Greek etc
    Stefan Chekanov 2:18 11 Jun 01 
      Help me!
    Valeriy 12:03 31 May 01 
      Help: Insert data into long field
    Kannan 7:41 9 Jan 01 
      Stored Procedure ...
    Rustam 23:57 8 Jan 01 
      Re: Stored Procedure ...
    Josef Wolfsteiner 3:32 8 Feb 01 
      bool variable problem
    Oleg Reabciuc 17:38 20 Nov 00 
      Better memory allocations
    Doug Garno 15:14 28 Oct 00 
      Problem encountered updating a double field named "Value"
    Someone 6:14 12 Sep 00 
      New source ZIP file available
    Valerie Bradley 0:17 13 Aug 00 
      Great .... but slow ?
    Chris S. 9:59 2 Jul 00 
      I do not know how did you get that result.
    Stefan Tchekanov 3:27 3 Jul 00 
      Wow...
    Chris S. 12:50 3 Jul 00 
      Thats great !
    Dimiter Shtilianov 12:06 12 Apr 00 
      Re: Thats great !
    suiphone 23:51 22 Feb 01 
    Last Visit: 12:00 Friday 1st January, 1999First Prev Next Last
    Home >> Database >> ODBC
    Advertise on The Code Project
    Article content copyright Stefan Chekanov, 2000
    everything else © CodeProject, 1999-2001.