t e m p o r a l 
 d o o r w a y 

The Simplest C++ Builder Database Program

 

This is even simpler in some ways than the simplest C++ Builder program... It needs no code at all to work.

Here's the form's appearance:

And here is the form VCL code - you can copy and paste this onto a blank form in C++ Builder.

object Table1: TTable
    Left = 648
    Top = 16
end
object DataSource1: TDataSource
    Left = 648
    Top = 56
end
object DBGrid1: TDBGrid
    Left = 8
    Top = 0
    Width = 673
    Height = 337
    TabOrder = 0
    TitleFont.Charset = DEFAULT_CHARSET
    TitleFont.Color = clWindowText
    TitleFont.Height = -13
    TitleFont.Name = 'MS Sans Serif'
    TitleFont.Style = []
end
object DBMemo1: TDBMemo
    Left = 8
    Top = 344
    Width = 185
    Height = 89
    TabOrder = 1
end
object DBCheckBox1: TDBCheckBox
    Left = 240
    Top = 360
    Width = 97
    Height = 17
    Caption = 'DBCheckBox1'
    TabOrder = 2
    ValueChecked = 'True'
    ValueUnchecked = 'False'
end
object DBEdit1: TDBEdit
    Left = 384
    Top = 352
    Width = 121
    Height = 24
    TabOrder = 3
end

Setting Up To Use This Form

All you need to do is create a table. Use the BDE Administrator program (Bdeadmin.exe) to establish an alias to a directory or file in your favorite DBMS.

Then use Database Desktop to create a simple table in that alias. Set up at least a memo field, a text field, and a boolean field.

Now, at design-time using the IDE Object Inspector...

  1. Connect the table to your project by picking the alias from the Object Inspector combo for the DatabaseName property of Table1 and the name of the table for the TableName property.

  2. Then connect the DataSource1 component to the TTable component by picking the table from the list provided by the DataSource1 DataSet property.

  3. Then shift click the grid and the other data aware controls, and set their DataSource property to DataSource1. Click on the form, and then for the non-grid controls, set their DataField property to the appropriate field (the DBMemo1 to the memo field, DBCheckBox1 to the boolean field, and DBEdit1 to the text field).

  4. Set the Active property of Table1 to true.

  5. Save the form as Form1Unit.cpp and the project as whatever you want to call it.

Compile and run the application. You can click on different rows in the grid and see the content of the data aware controls change as you do. You can edit the fields in the table by clicking on them in the grid or on their data aware controls on the form. You can create a new record in the grid with the Insert key or by using the down arrow key when on the last row. You can delete a row by clicking on the leftmost column (the grey indicator column with the arrow in it) and pressing the Delete key.

Easy, isn't it?

Using A Query

Want to use a query instead of a table? Try this at design-time in the IDE...

  1. Click on the form. Then right click and the form popup appears. Pick "View As Text". The form will appear in the editor looking much like what was provided above.

  2. Change TTable to TQuery in "object Table1: TTable". Delete the line of the Table1 object definition which reads "TableName=".

  3. Right click and pick "View As Form". The form will reappear. The Table1 object is now a query. Click on Table1 and then in th Object Inspector on the Table1 SQL property "..." button. A small editor for entering a query will appear. Enter SELECT * FROM tablename and subsitute your own table's name for "tablename" (for instance - SELECT * FROM mytable)

  4. In the Object Inspector for Table1, set RequestLive to true (so you can edit the table through the query), and Active to true (because it was set to false when you changed the SQL).

  5. Save all. The IDE will ask if you want to change the declaration for Table1 from TTable to TQuery. You do.

  6. Compile and run the application. It should behave just the same as when you were using the table directly.

Using a query in this manner is common when connecting to client server databases.

Proper Practice

The way this program has been set up is not really the best way to set up a larger database application. To make this project more scalable...

  1. File | New Data Module. A data module form (invisible at run time) will appear.

  2. Click on Table1 and DataSource1. Edit | Cut.

  3. Click on DataModule1. Edit | Paste. Save the data module as DataModule1Unit.cpp

  4. Click on Form1. File | Include Unit Hdr. Pick DataModule1Unit. Now the table and data source are visible to the form.

  5. Reattach the controls to the data source and fields. Now the drop down will show entries like DataModule1->DataSource1.

  6. View | Project Source. Move the CreateForm for DataModule1 to preceed that of Form1. This is necessary to ensure that the data module has been created when the form needs to link to its data source.

  7. Compile and run.

In a real application, the data module should be named the same as your database entity (i.e. Account, Order, Member) and your Table, if the primary entity table should be named (surprise) Table. Then your code which works with the table from the form can refer to things like Member->Table, which is neat. The DataSource for the entity table should be Source.

You may also need other tables in an entity data module - for instance, domain tables from which values (foreign keys) are drawn. Such things as Order->Product or Member->Dues. And there is sometimes a need for a lookup table which can be used for Locate and lookup fields - those can be called Account->LookupTable... etc.

Another important concept for the scalable database program is the persistent field. In many database applications, programmers hard code the name of a field in expressions like Account->Table->FieldByName("ID"). It is better to set this up at design time by double-clicking on the TTable component and then right clicking to get a menu from which you Add Fields from the database. This action creates permanent TField objects which can be directly referenced... such as Account->TableID (this is the ID field of Account->Table not an ID of Table in Account); these references can also be checked at compile time for existence, which makes your program safer at run time. And since the definition of the field does not need to be looked up on every reference, it is even more efficient.

More details on these concepts can be found in the September 1999 issue of C++ Builder Developer's Journal, where an article by Mark Cashman discusses advanced database concepts and planning for scalability.

Copyright © 2004 by Mark Cashman (unless otherwise indicated), All Rights Reserved