| This chapter provides an introduction to BCB database
programming. I start by showing how to create aliases and simple database
applications, and then move on
to a discussion of various conventions that I use when programming databases.
Next, I discuss key database tools that ship with BCB, and close with a
discussion of getting ODBC and TCP/IP set up on your system.
In subsequent chapters I move on to a discussion of advanced client/server
issues. I am covering such basic material as you find in this chapter in a book
on intermediate-to-advanced programming because there are so many C++
programmers who are new to the database world. If you have much experience in
C++ desktop and systems programming, but little database experience, you should
take the time to work through this chapter. BCB makes this subject easy to
master, and once you have database tools at your fingertips, you will be
surprised how many uses you can find for them. This is especially true in the
data-centric, Internet-based world most programmers live in these days.
More advanced database programmers should also read this chapter, because I
include an overview of the BCB database architecture. All serious developers
need to be concerned with matters covered in this chapter such as Object
Repositories, business rules, the proper use of TDataModule, and so on.
More specifically, the subjects covered in this chapter include introductions
to the following:
¡¡
- The TTable, TQuery, and TDataSource objects.
The discussion of these subjects will show you how to connect to tables with
and without SQL.
¡¡
- The tools available for creating aliases.
¡¡
- The Object Repository, Database Explorer, and SQL Monitor.
¡¡
- The TDataModule keeps your form clear of database objects, and it
consolidates your database code and rules in a single object that can be
shared in multiple applications. In particular, it enables you to place
nonvisual components, and particularly nonvisual database components, on a
special window that remains hidden at runtime. This window can be stored in
the Object Repository and can be used to specify business rules that can be
reused by a wide range of projects and programmers.
¡¡
- The last part of this chapter covers the different means for accessing
data from inside BCB. For instance, you can use the BDE, ODBC, and SQL Links.
In this section you learn about the difference between Paradox, dBASE,
InterBase, Oracle, and Sybase, as well as other related matters.
¡¡
- The chapter ends with a discussion of using ODBC, and of setting up TCP/IP
on your system.
¡¡
After you have covered the basics in this chapter, the next step is to move
onto the in-depth discussions of these topics found in the next few chapters. In
particular, the next chapter covers the TTable object in depth, and the
following chapter covers the SQL-based TQuery object.
Here is an overview of what lies ahead:
¡¡
- Chapter 8: This chapter, which covers database basics, including
introductions to TTable and TQuery
¡¡
- Chapter 9: An in-depth look at TTable and TDataSet
¡¡
- Chapter 10: An in-depth look atTQuery and an introduction to SQL
programming
¡¡
Subsequent chapters in this section of the book cover intermediate and
advanced database topics. For instance, you will find more material on the
database tools in general and on CASE tools in particular, in Chapters 17 and
18.
The logic behind arranging things this way is to enable you to first get an
introduction to BCB and databases, and to then dig into the subject once you
understand the issues involved. This way I can keep the simplest subject matter
out of the latter chapters so they can focus on relatively serious programming
issues.
If you are interested primarily in design issues, you can safely skim through
this chapter, looking only for the parts that interest you. If you have not
built a BCB database program before, you should read this entire chapter through
from beginning to end.
On Setting Up the Database Tools
BCB comes ready to run Paradox, dBASE, and local InterBase tables. If you
have the client/server version of the product, it comes with the full InterBase
server. The client/server product also lets you access any DB2, Informix, MS SQL
Server, Sybase, and Oracle you have available. The very first version of BCB
does not ship with built-in support for Access or FoxPro, but if you have Delphi
3.0 installed on your system, you can get access to these features
automatically.
¡¡
- ¡¡
¡¡
¡¡
NOTE: The issue here is that BCB
1.0 ships with an older version of the BDE than Delphi 3.0, because the new
version of the BDE was not available at BCB ship time. If you can get the most
recent version of the BDE, BCB will work with it automatically, which means
you can get at Access tables. You can sometimes download the BDE directly from
the Borland Web site at
www.borland.com. If you can't get the most recent version of the
BDE, you can use ODBC to, as they say, access "Access."
It does not matter that BCB shipped with an earlier version of the BDE than
the one you may be running on your system. The BDE was designed to handle this
type of situation, and it is entirely regular. I use the most recent version
of the BDE with my copy of BCB, and I never have any hint of trouble.
¡¡
¡¡
The primary way to access data from BCB involves the BDE, or Borland Database
Engine. This engine, which is explained in more depth near the end of this
chapter, is the backbone of the BCB database tools.
Paradox, dBASE, and Access tables can be reached directly from the BDE. To
access the other databases, you also need SQL Links, which is a product that
ships with the client/server version of Delphi.
One simple way to find out what database access you have from your system
involves dropping down a TDatabase object on a form and examining its
DriverName property in the Object Inspector. This property contains a
list of the installed drivers on your system. For instance, all the databases
listed earlier in this section appear in this Property Editor on my system.
(Paradox and dBASE tables are accessed through the driver labeled "Standard.")
If you are having trouble accessing some or all databases from your system,
the best remedy is usually to do a complete uninstall and a complete reinstall.
The local database systems ought to work automatically out of the box without
any effort on your part. For instance, I have probably installed BCB 50 times on
a variety of systems, and I have always been able to reach Paradox or dBASE
tables immediately after installation.
Users of the BDE should be aware that BCB ships with a tool called the BDE
Configuration utility. This application can be accessed from the Start menu. You
can use this tool to make sure the BDE itself is running correctly. As a rule,
if the BDE is not running, you will not be able to access databases from inside
of BCB.
Database Basics
To create a simple database application, start by placing a TTable,
a TDataSource, and a TDBGrid component on a form, as shown in
Figure 8.1.
FIGURE 8.1. TTable,
TDataSource, and TDBGrid arranged on a form.
¡¡
Wire these three controls together by completing the following simple steps:
¡¡
- 1. Connect the DataSource property of the TDBGrid
to DataSource1.
2. Connect the DataSet property of the TDataSource
control to Table1.
¡¡
After completion of these steps, the three components are hooked together and
can communicate with one another.
Connecting the TTable object to a table that resides on disk is a
three-step process:
¡¡
- 1. Set the DatabaseName property either to a valid alias
or, in the case of Paradox or dBASE, to the subdirectory where your data
resides. For the example currently under discussion, you can set the
DatabaseName property to the BCDEMOS alias, which is created by
default during BCB's installation. Alternatively, you could type
c:\CBuilder\demos\data into the DatabaseName Property Editor, where you
might need to change some aspects of this path depending on where you choose
to install BCB.
2. Set the TableName property to the name of the table you
want to view; for instance, you might choose the CUSTOMER.DB table.
The Property Editor drops down a list of available tables, so there is no need
for you to type anything; the whole job can be done with the mouse.
3. Set the Active property, found at the very top of the
Object Inspector, to true.
¡¡
When you are done, the Object Inspector should look as it does in Figure 8.2.
FIGURE 8.2. The Object Inspector after
connecting to a table called Customer, using an alias called
BCDEMOS.
If you have completed all these steps properly, you should now be looking at the
data from the table you chose, as shown in Figure 8.3. To take this process one
step further, you can compile and run the program and then begin browsing and
editing your data.
FIGURE 8.3. Simple form displaying the
contents of CUSTOMER.DB.
If you want to simplify the task of browsing through the data in your
application, you can go back into design mode and add the TDBNavigator
control to the program. To hook this control into the loop, all you need to do
is set its DataSource property to DataSource1. Now you can run
the program and begin iterating through the records with the navigator, as shown
in Figure 8.4. In Figure 8.4, most of the functionality of the TDBNavigator
has been turned off by manipulating its VisibleButtons property. For
instance, a navigator can automatically enable you to edit, insert, delete,
post, cancel, and refresh. All those capabilities have been disabled and hidden
in the form shown here by manipulating the VisibleButtons property of
TDBNavigator.
FIGURE 8.4. A simple database program with a
TDBNavigator control.
A program like the one described here is found on the CD-ROM that accompanies
this book as Simple.cpp. Programs like this are the bread and butter of
the database world. You might even find it useful to create several programs of
this type, just be to be sure you can do it quickly and easily with little
thought. BCB database programmers work with the TTable, TDataSource,
and TDBGrid objects on a regular basis, and the basics of their
operation should be as easy as tying your shoe.
Naming and Architectural Conventions
In this section I lay out a number of conventions that I generally abide by
in
programming projects. Before beginning, I should emphasize that these are
merely conventions. There are no hard and fast rules in this area, and you
should feel free to follow my suggestions only to the degree that they suit your
taste. In fact, you
will find that I myself do not follow these conventions one hundred percent
of the time, though I generally conform to them when I am not feeling too
rushed. I also have included legacy code in this book in which I did not adopt
the techniques that I currently believe are best. In some cases, I have updated
the legacy code, but some sample programs still use old conventions.
When arranging tables on a data module, I usually follow some simple naming
conventions. If I attach a TTable object to a table called Customer,
I will call the TTable object CustomerTable. The data source
attached to that table will generally be called CustomerSource.
¡¡
- ¡¡
¡¡
¡¡
NOTE: An alternative technique,
called Hungarian notation, would name all TTable objects tblXXX,
where the XXX is the name of the table you want to use: tblCustomer,
tblBioLife, and so on. You could then prefix ds before the
table name to designate the name of the data source: dsCustomer,
dsBioLife, and so on. This was the technique I used in the past, but
which I no longer believe to be best.
The Hungarian system enables you to automatically group all the objects shown
in the Object Inspector according to type. If all the tables begin with
tbl, they will appear together in the Object Inspector.
Despite this advantage, I have decided against Hungarian notation on the
grounds that it tends to make even simple code appear a bit abstruse.
Recondite code has a certain emotional appeal, but it is not the effect I want
to strive for in a book that champions the virtues of clear, easy-to-read
logic.
In general, I try to avoid systems that force me to use hard to read
abbreviations, or that clutter up the beginnings of words. In particular, I
find an identifier like dsCustomer unpleasant because it makes the
type of the variable appear more important than the name of the variable. This
system also makes it hard to read the identifier, because you have to mentally
skip over the first syllable before getting to the key piece of information
contained in the name. Of course, my least favorite naming convention
abbreviates both the type and the variable name:
¡¡
DDSURFACEDESC ddsd;
HBITMAP hbm;
RGBQUAD * prgb;
Incredibly, these samples are taken from source code distributed by a major
software company in order to promote a new API. I am at least tempted to
believe that the people who came up with these variable names were trying to
be funny, or perhaps just to pull someone's leg. At any rate, these are
classic examples of naming conventions that I try to avoid.
So, in this book, it will generally be CustomerTable, and not
tblCustomer. If you prefer some other system, you should feel free to
pursue your tastes. After all, many of the best programmers use Hungarian
notation on a regular basis. In fact, in the world of C++, my tastes probably
represent a minority opinion.
¡¡
¡¡
In simple projects that have only one data module, I will usually call the
files associated with the data module DMod1.cpp and DMod1.h.
The TDataModule object itself I usually rename to TDMod. In
more complex projects that have multiple data modules I might rename the data
module to something more meaningful such as TDModAddress, and I might
then save the file under the name DModAddress1.cpp.
¡¡
Please note that my convention is to name the file in which a data module or
form is stored as the same name as the data module or form, except that I append
a 1 to it. Thus the file in which a data module called TDMod
is stored will be called DMod1. This prevents name conflicts between
the object name and the filename. If I have a form called TAddress, I
will save it in a file called Address1.cpp. The one exception to the
previous rule is that I tend to name the main module of a project Main.cpp,
and I then usually keep the main form default name of Form1.
Please understand that I have included this section more as a courtesy than
out of a desire to attempt to force my tastes on someone else. I want you to
know and understand the conventions I use, but you should feel free to use the
techniques that you feel work best.
Enough on naming conventions. It's time now to move on to a related, but
slightly
different matter regarding the proper use of data modules.
Using the TQuery Object
You can create a BCB SQL statement by using a TQuery component in
the following manner:
¡¡
- 1. Drop down TQuery, TDataSource, and TDBGrid
objects on a form and wire them together.
2. Assign an alias to the DatabaseName property of the
TQuery object. For instance, use the BCDEMOS or DBDEMOS
alias. These aliases are created automatically by BCB and Delphi,
respectively, during installation.
3. Use the SQL property to enter a SQL statement such as
Select * from Country.
4. Set the Active property to True. If you completed
each step correctly, and if the BDE is set up correctly, the grid should now
contain the records from the Country table.
¡¡
If you're working with local data, you can substitute a fully qualified
subdirectory path for an alias. When using the latter method, it's best if you
don't include the actual name of a table, but only the subdirectory in which one
or more tables exist.
In my opinion, however, it is almost always better to work with an alias
rather than specify the path directly in the DatabaseName property.
That's all I'm going to say about TQuery for now. Later in this
chapter I discuss the SQL monitor tool that comes with BCB. In subsequent
chapters I begin using SQL statements more heavily. I find it easier to use
TTable than TQuery for many basic database operations. However, as
I discuss matters of increasing complexity throughout the database section of
this book, I will rely more and more on SQL.
The Data Module
Earlier in this chapter, you placed a TTable and TDataSource
component on the same form with your visual components. When you ran the
program, the icons representing these components disappeared. However, they are
visible at design time and have a tendency to clutter up the form. Partially out
of a desire to eliminate this clutter, BCB features a component called a
TDataModule, which can be used to store nonvisual controls such as
TTable and TDataSource. A program on disk called SimpleTable
shows how to use the TDataModule component.
To get started working with TDataModules, first begin a new
application. Next, choose File | New and select the Data Module component from
the New page of the New Items dialog, as shown in Figure 8.5. You can also
choose to create a data module directly from the New Data Module option on the
File menu. You should, however, get used to using the Object Repository as it
plays a big role in BCB programming.
FIGURE 8.5. Selecting the TDataModule
component from the New Items dialog.
¡¡
- ¡¡
¡¡
¡¡
NOTE: A TDataModule is
not the same thing as a form. For instance, if you look in its ancestry you
will see that it is a direct descendant of TComponent. When you first
see a TDataModule object, there is a tendency to view it as merely a
special kind of form, which is, to some degree, true, at least in a very
nontechnical sense. However, the hierarchy for a TForm component
looks like this:
¡¡
-TComponent
-TControl
-TWinControl
-TScrollingWinControl
-TForm
The hierarchy for a TDataModule, on the other hand, looks like
this:
-TComponent
-TDataModule
Clearly, TForms and TDataModules are two very different
beasts, despite some apparent similarities between them.
The header file that contains the declaration for the TDataModule
class is FORMS.HPP. If you have the Pascal source to the VCL, the
TDataModule object is found in FORMS.PAS.
¡¡
¡¡
After adding a TDataModule object to your application, take a moment
to save your code. You might save Unit1 as MAIN.CPP and
Unit2 as DMOD1.CPP. Click Form1, open the File menu, and
choose the Include Unit Header expert from the menu. In the Include Unit dialog,
select DMod1 and press OK. This is a simple way of automatically
inserting an #include directive at the top of Unit1. In
particular, the following changes are made to your code:
#include <vcl\vcl.h>
#pragma hdrstop
#include "Main.h"
#include "DMod1.h" // This directive references the data module
#pragma resource "*.dfm"
You can, of course, type in the #include directive without using the
small expert found on the File menu. There is no particular advantage in using
the expert other than its ease of use. Remember, however, that if you want to
include a unit in your project, it is generally not enough to simply add a
header file to a unit. You must also be sure the unit has been explicitly added
to your project. In the case discussed here, there is no need to explicitly add
Unit2 (a.k.a. DMod1) to the project, because it was done
automatically when you first created the unit.
¡¡
- ¡¡
¡¡
¡¡
NOTE: This is a time when some
programmers may need to force themselves to abandon the old "command-line"
attitude, and to instead embrace a visual tool that can help make you more
productive. As always, the choice is yours, but if the visual tools are easier
to use, and if they make you more productive, you should consider using them.
Command-line programming has an honorable place in this world, but it is
generally not part of the attitude that makes for good RAD programmers.
Let me tack on one additional piece of information to this note. If you want
to add a module to a project without using the project manager, and without
editing a makefile, you can use the following syntax:
¡¡
#pragma link "dmod1.obj"
This syntax can be very useful when adding components to the component
palette. See the FTP2.cpp module in the Utils directory from
the CD-ROM that ships with this book for an example of using this approach.
¡¡
¡¡
Arrange Form1 and DataModule2 on the screen so you can view
them both at the same time. Drop a TTable and TDataSource
component on the data module, as shown in Figure 8.6.
FIGURE 8.6. Form1 and
DataModule2 arranged on the screen so that you can easily view them both at
the same time.
¡¡
- ¡¡
¡¡
¡¡
NOTE: For various reasons I
snapped my screen shots for this book at 640x480 resolution. Needless to say,
I don't usually run BCB at that resolution. 1024x768 is probably a more
reasonable size when working with an environment like this, though even higher
resolutions would be better. 800x600 is tolerable, but I still feel the pinch
when working at that low a resolution.
¡¡
¡¡
Wire the TDataSource to the TTable object, and set the
DatabaseName of the TTable object to BCDEMOS and the
TableName to BioLife. Set the Active property of the
TTable object to True.
Right-click the TTable object and bring up the Fields Editor.
Right-click the Fields Editor, and bring up the AddFields dialog. Make sure all
the fields in the dialog are selected, which is the default behavior for the
tool. Click the OK button.
The Fields Editor now contains a list of all the fields in the BioLife
table. To add these fields to your form, simply click one or more fields, hold
the left mouse button down, and drag the fields onto the form. For instance,
select the Graphics field, making sure that it is the only one highlighted. Now
drag it onto the form. When you let go of the left mouse button, the Graphics
field will automatically display itself in a TDBImage component. Drag
over several other fields, and create a form that looks something like the image
in Figure 8.7.
FIGURE 8.7. Some of the fields of the
BioLife table arranged in visual controls on a TForm object.
If you want to drag multiple fields over at the same time, perform a
multiselect operation in the Fields Editor, just as you would in a list box. Now
drag all the fields over to the main form at once. Most likely they will scroll
on past the bottom of your form when you insert them, but you can fix this
easily enough by aligning them as you like with the mouse.
The TDBImage component, where the picture of the fish is displayed,
may be in some disarray when you first insert the components on the form. To
straighten things out, select the TDBImage component, go to the Object
Inspector, and set the Stretch property to True.
A sample program illustrating these principles ships on the CD-ROM that
accompanies this book. It is called SimpleDataModule.
¡¡
The Purpose of TDataModule
Now that you know how to use the TDataModule, let me add a few words
on its significance. This component's primary purpose is to provide a place
where
you can define the means for accessing a set of tables. However, it is also a
place to put business rules, and a place to create reusable means of accessing
data.
Client/server database programmers often want to put all the rules for
accessing
data on the server side. Indeed, BCB supports this paradigm, and you can use
stored procedures, views, and other advanced database technologies to whatever
degree you want when accessing SQL databases. However, you also have the ability
to define a
set of rules that live on the client side, inside a TDataModule. You
can then use the Object Repository from the File | New menu choice to store this
form in a place where it can be reused by multiple programmers. To put a form in
the Object
Repository, right-click it and chose Add to Repository from the menu. I
discuss the Object Repository in more depth later in this chapter.
There is no simple way to decide when it is best to put rules on the server
side or when it is best to put them inside a TDataModule. Often the
best solution is to use a combination of the two techniques. Put things on the
server side when that is simplest, and store things on the client side when you
think the power of C++ and its strong debuggers will be useful to you.
There is considerable complexity in the whole set of related subjects that
involve designing databases, creating business rules, and creating metadata such
as stored procedures on a server. Many of these topics will be explored in
considerable depth in the next few chapters. However, a thorough examination of
these topics requires considerably more scope than I have in this book.
Conventions Regarding the Use of TDataModules
When working with database code, I often prefer to put my TTable,
TQuery, and TDatasource objects in a TDataModule,
rather than placing them on a form. In other words, I think there are
architectual reasons for using TDataModules rather than placing tables
directly on a form.
There are several interrelated advantages to this scheme, most of which have
to do with proper design issues. In particular, the scheme outlined previously
enables me to do the following:
¡¡
- Separate my database code and my GUI code. In an ideal situation, all the
front-end code is in my form, and all the database code is in the
TDataModule.
¡¡
- Protect the data in database code. It can sometimes be bad practice to
directly access any of the methods or fields of an object from inside another
object. Instead, you can present a series of public properties and methods as
the interface for an object. There is, however, nothing wrong with accessing
the properties of a TTable and TQuery object through pointer
notation. In other words, the rules of aggregation state that it is acceptable
and safe to write DMod->Table1, rather than trying to hide Table1
behind methods of TDMod that wrap the object and hide it from sight.
¡¡
- Promote reuse. By isolating the database code in the TDataModule,
and by protecting the data associated with my database code, I am creating a
robust object that can be reused in multiple programs. In particular, several
programs may want to access data in the same manner. By using a
TDataModule correctly, you can encapsulate your data, and its associated
rules, inside a single related object that can be reused in multiple programs.
This is one way to create a set of reusable business rules that can be shared
by multiple members of your team, or by multiple programmers.
¡¡
I will often use the constructor of a TDataModule or its
OnCreate event to open up the tables or data modules used in a project:
void __fastcall TDModBioLife::DModBioLifeCreate(TObject
*Sender)
{
BioLifeTable->Open();
}
This is the proper and ideal way to do things. I want to stress, however,
that it is also correct from an OOP standpoint to access the same table from
inside your main form through the scoping operator:
void __fastcall TForm1::Button1OnClick(Tobject *Sender)
{
DMod->BioLifeTable->Open();
}
You can use either technique, depending on your needs. Most of the time, you
will use the second technique shown here, the one that uses scoping
operators. There are, however, some good arguments in favor of using the first
method. In particular, the first technique is pure from an OOP point of view in
that it completely hides the details of what goes on in the TDMod
object.
One problem with the rigorous technique illustrated by the first example is
that it can add complexity to simple programs. If you only need to access one
table, and only need one TDataSource object, even the simple act of
creating a data module can seem like overkill. Going even further and insisting
that the code for manipulating the table also reside in the TDataModule
can then seem almost absurdly roundabout and abstruse.
In the type of simple database projects that you will see in this chapter, it
is possible to forgo the use of data modules altogether and to instead place the
TTable and TDataSource objects directly on your main form.
However, I will generally use a TDataModule even in such simple cases
simply because it is the best way to architect an application. The point is to
get in the habit of doing things the right way, because ultimately, in large
scale projects, decisions such as this do matter.
¡¡
- ¡¡
¡¡
¡¡
NOTE: Data modules can be used
not only for data controls, but for all nonvisual controls. You can, for
instance, place a TMenu object on a data module, and then add the
data module's header file to your main form, thereby accessing the TMenu
object through the Object Inspector. The problem with this technique, of
course, is that the methods you want to access from the menu are not always
going to be located in the data module. Another issue is that your form and
the data module will then be bound together, at least to some degree.
¡¡
¡¡
Remember that one of the key features of data modules is that they provide a
place to store a set of business rules. You can create tables and queries, link
them together, and use code to define rules regarding the way they work. To
replicate these rules in multiple projects, simply reuse the data module that
contains them.
The Object Repository
In the last section, I said that data modules can help promote reuse of code.
BCB has a specific mechanism called an Object Repository that can help with this
process. In particular, Object Repositories are a place where you can store data
modules and forms so that they can be reused in multiple applications. If you
define a set of business rules in a data module, you can save it to the Object
Repository and reuse it in multiple projects. This helps you propagate the rules
and promote conformity to them across a wide range of projects.
The simplest way to introduce you to the Object Repository is to just lead
you step-by-step through the process of using it. After you have seen how it
works, I will take a moment to explain its significance.
Save the program you created in the last section as follows:
¡¡
- 1. Save Unit1 as Main.cpp.
2. Save Unit2 as DModBiolife.cpp.
3. Save the project file as Biolifeapp.
4. Rename the table and data source to BioLifeTable and
BioLifeSource.
5. Select the data module and use the Object Inspector to rename it
from DataModule2 to DModBioLife.
¡¡
Right-click the data module and select Add To Repository. Fill in the Add To
Repository dialog by setting the Title, Description, and Author fields as you
see fit. In the Page drop-down combo, select Data modules. Use the Browse button
to select an icon from the ..BCB\images\icon subdirectory, or from any
place else where you might have some icons stored.
Start a new project. Choose File | New. This time, instead of choosing the
Data module component from the New page, select the Data modules page and choose
the DModBioLife component that you just finished creating. When it
appears on the screen, you will see that it contains a TTable and
TDataSource component. The components are wired together, and the
TTable object is set to the BioLife table with its Active
property set to True.
To access this table from Form1, you must first employ the Include
Unit Header menu option from the File menu to add Unit2 to the uses
clause in Unit1. Go to the DataControls page of the Component Palette,
and drop down a TDBGrid object on Form1. In the Object
Inspector, drop down the DataSource property of the TDBGrid
object, and you will see the TDataSource object from the
DModBioLife module listed. Select this item, and the grid will
automatically fill up with data.
If you drop down a TDBEdit control instead of a TDBGrid
control, you proceed the same way, except that you will need to fill in not only
the DataSource property in the Object Inspector, but also the
DataField property. There is no need to type information into the
DataField property, because it will
automatically contain a list of the available fields in the BioLife
table.
The true significance of the Object Repository is only hinted at by this
example. The importance of this tool is made more obvious if you have six or
seven tables dropped onto a data module. You might then define several
relationships between the tables and add other related code. For instance, you
might have some one-to-many relationships established, possibly a many-to-many
relationship established, and you might have several filters, lookups, and
several calculated fields defined.
Altogether, a data module of this type might encapsulate several sets of
business rules defining exactly how tables should be accessed and how they
relate to each other. The ability to save all this work in the repository, and
to then automatically reuse it in multiple projects, is extremely valuable. I
am, however, getting ahead of myself. Discussions of filters, lookups,
calculated fields, and other database issues occur in various places over the
next few chapters.
The Database Explorer
In addition to the data module, another key tool to use when working with
databases is the Database Explorer. You can access the Database Explorer by
choosing the Database | Explore menu item. The Explorer is a stand-alone
executable, so you can also access it from the Windows Start button on the
taskbar. You can use the Explorer even if
BCB is not running, but BCB and the Explorer work best in conjunction with
one another.
Once you have loaded the Explorer, make sure you have selected the Databases
page and not the Dictionary page. Click the BCDEMOS node to expose the
Tables node. Now click the little plus sign before the Tables
node. A list of all the tables in the database will appear. Select the
BioLife table and choose the Data page to view the contents of the table,
as shown in Fig- ure 8.8.
FIGURE 8.8. Exploring the BioLife
table in the Database Explorer.
Click the little plus symbol in front of the BioLife.db node, and you
will see a list of properties for the BioLife table. The properties
listed are Fields, Indices, Validity Checks,
Referential Constraints, Security Specs, and Family Members.
You can expand each of these nodes to view their properties. For instance, if
you select the Fields node, you will see a list of all the fields in
the table. As you select each individual field, you will see a description of
its primary characteristics.
¡¡
- ¡¡
¡¡
¡¡
NOTE: The Database Explorer
provides a means for viewing the text of stored procedures and triggers. You
cannot edit these values, but you can view their code.
¡¡
¡¡
The Database Explorer is a fairly complex tool with a number of powerful
traits, many of which will undoubtedly be expanded in future versions of the
product. In particular, you should note that it contains a DataDictionary
that enables you to define a new alias or modify existing aliases.
At this stage, I want to show you only one key trait of the Database
Explorer. Arrange your screen so you can view both the Explorer and Form1
at the same time. Select the BioLife table in the Explorer with the
mouse, and then drag and drop it onto Form1. If you want, you can
experiment further by expanding the BioLife node in the Explorer and
dragging and dropping individual fields of the table onto Form1, just
as you did when using the Fields Editor.
If you start a new application, and then drag and drop the BioLife
table onto Form1 from the Explorer, you will find that the TTable
and TDataSource objects are placed on Form1. If you want to
move them off the form, you can add a TDataModule object to the
project, and then select both the TTable and TDataSource
objects and choose Edit | Cut from the menu. Now select the TDataModule
object and choose Edit | Paste. Make sure Form1 contains a reference (#include)
to the unit that contains the TDataModule, and then hook up the grid to
the TDataSource object in the TDataModule. This sounds like a
fairly complicated process when written out, but you can perform this task in
just a few seconds using BCB's visual tools.
Once again, the last few paragraphs have done nothing more than introduce you
to the Database Explorer. This is a complex tool that will prove useful to you
in many different ways, some of which might not even have been apparent to its
creator. For now, the key point to grasp is that it gives you an overview of all
the data in a database and enables you to drag and drop fields and tables onto
your forms.
Working with the SQL Monitor
The SQL Monitor is an advanced tool that enables you to see exactly what SQL
statements are being generated by your application when you are running queries
against SQL databases such as InterBase. The SQL Monitor only works when you are
using an ODBC connection or SQL links to access real databases such as
InterBase, Oracle, Sybase, or Informix. In other words, it is not useful when
you are using the TTable object, or when you are accessing Paradox or
dBASE tables.
There is no trick to using the SQL Monitor. If it ships with your version of
BCB, you can simply select the SQL monitor from the Database menu, run your
program, and then browse through the SQL Monitor to see the specific statements
generated by your program.
Here is code produced from a simple SQL request for all the rows from the
Customer table from the IBLOCAL alias. Note that in this case I am
using an InterBase table. InterBase is an ANSI 92 SQL-compatible database
server:
1 18:49:40 SQL Prepare: INTRBASE - select * from
Customer
2 18:49:40 SQL Vendor: INTRBASE - isc_dsql_allocate_statement
3 18:49:40 SQL Vendor: INTRBASE - isc_start_transaction
4 18:49:40 SQL Vendor: INTRBASE - isc_dsql_prepare
5 18:49:40 SQL Vendor: INTRBASE - isc_dsql_sql_info
6 18:49:40 SQL Vendor: INTRBASE - isc_vax_integer
7 18:49:40 SQL Transact: INTRBASE - XACT (UNKNOWN)
8 18:49:40 SQL Vendor: INTRBASE - isc_commit_retaining
9 18:49:40 SQL Execute: INTRBASE - select * from Customer
10 18:49:40 SQL Vendor: INTRBASE - isc_dsql_execute
11 18:49:40 SQL Stmt: INTRBASE - Fetch
12 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
13 18:49:40 SQL Stmt: INTRBASE - Fetch
14 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
15 18:49:40 SQL Stmt: INTRBASE - Fetch
16 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
17 18:49:40 SQL Stmt: INTRBASE - Fetch
18 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
19 18:49:40 SQL Stmt: INTRBASE - Fetch
20 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
21 18:49:40 SQL Stmt: INTRBASE - Fetch
22 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
23 18:49:40 SQL Stmt: INTRBASE - Fetch
24 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
25 18:49:40 SQL Stmt: INTRBASE - Fetch
26 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
27 18:49:40 SQL Stmt: INTRBASE - Fetch
28 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
29 18:49:40 SQL Stmt: INTRBASE - Fetch
30 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
31 18:49:40 SQL Stmt: INTRBASE - Fetch
32 18:49:40 SQL Vendor: INTRBASE - isc_dsql_fetch
All this information can be a bit overwhelming at times. To simplify the
output from the SQL Monitor, select Options | Trace Options from the SQL Monitor
menu. The dialog shown in Figure 8.9 is launched. You can then select just the
first two, or perhaps only the second option. The output from the same test run
previously then looks like this:
2 04:54:44 Log started for: Project1
3 04:55:02 SQL Prepare: INTRBASE - select * from customer
4 04:55:03 SQL Execute: INTRBASE - select * from customer
Now you see only the prepare and execute statements, which is probably all
the information you needed. A screen shot of the SQL Monitor with this simple
information in it is shown in Figure 8.10.
Once again, the SQL Monitor is only for use with powerful databases such as
InterBase or Oracle. I will present in-depth discussions of InterBase later in
this section of the book.
FIGURE 8.9. The SQL Monitor Trace Options
dialog is accessible from one of the program's speed buttons.
¡¡
FIGURE 8.10. The SQL Monitor showing
prepared and executed SQL statements.
¡¡
Understanding the BDE and Aliases
The BDE is the Borland Database Engine, which used to be called IDAPI. This
engine is the gateway to all the databases accessed from BCB, except under
certain unusual circumstances.
The BDE gives you direct access to Paradox and dBASE tables. If you own a
copy of Paradox or dBASE, you already have the BDE installed on your system. The
brains behind these two products is the BDE. Paradox and dBASE are merely
wrappers around the BDE, in much the same way that the BCB IDE is a wrapper
around a C++ compiler.
Besides giving you access to Paradox and dBASE, the BDE also uses Borland's
SQL Links technology to give you fast access to client/server databases. In
particular, BCB ships with SQL Links drivers for InterBase, Oracle, Sybase, MS
SQL Server, DB2, and Informix.
The list of available client/server databases changes depending on the
version of the BDE you are using. To check the current list, open up the BDE
Configuration application that ships with BCB and look at the available list of
drivers. (See Figure 8.11.)
FIGURE 8.11.
The Borland Database Configuration utility (BDECFG32.exe)
provides a list of available SQL Links drivers.
The BDE Configuration utility lists the actual files that contain the SQL Links
drivers. For instance, the SQL Links driver for Oracle is called
SQLORA32.dll. The driver for InterBase is called SQLINT32.dll, and
so on, as shown in Table 8.1.
Table 8.1. DLLs associated with various SQL drivers.
| Database |
SQL Links driver names |
| DB2 |
SQLDB232.dll |
| Informix |
SQLINF32.dll |
| InterBase |
SQLINT32.dll |
| MS SQL |
SQLMSS32.dll |
| Oracle |
SQLORA32.dll |
| Sybase |
SQLSSC32.dll |
The mere presence of the preceding DLLs is not enough, however, to connect you
to a client/ server database. You will also need to be running a network
protocol such as TCP/IP, and you will need local drivers and tools for the
database supplied by the database vendor. The next section of this chapter
discusses setting up TCP/IP on your computer.
¡¡
Each vendor will have a different set of local drivers and tools for you to
use. For instance, if you want to connect to Oracle, you need to install the
Oracle SQL Net tools.
Your copy of BCB comes with a local version of InterBase. If you look at the
right-hand portion of the taskbar at the bottom of your copy of Windows 95 or
Windows NT, you should see the green and gray icon for the InterBase Server
Properties applet. Other InterBase applets include the InterBase Server Manager
(IBMGR32.exe) and InterBase Windows SQL (WISQL32.exe). (When
reading these cryptic and inadvertently humorous 8/3 executable names, you
should break the abbreviations up like this: IB-MGR-32.exe, not like
this: IBM-GR-32.exe.)
If you are running BCB, your connection to InterBase will be set up
automatically during program install. However, if you want to connect to Oracle,
Sybase, or some other SQL server, you can be in for a rather complicated ordeal.
The difficult part of these installs is almost always setting up the third-party
tools. Borland's half of the equation is usually automatic, and occurs without
effort on your part during BCB's install.
To get connected to Oracle or Sybase, the first thing to do is close BCB and
all the Borland tools, and consult the manuals for your server. They will show
you how to get the server set up, how to test the connection, and how to run
some tools for managing your database. Once this part of the procedure is over,
you can launch BCB, and you should be able to connect right away after
establishing an alias. Aliases are described in the next section of this
chapter, and in- depth in several of the upcoming chapters, including Chapter
15, "Working with the Local InterBase Server." They are also discussed in the
readme file from the CD that accompanies this book. Of course, you have to have
the client/server version of Delphi to connect to these databases.
There is a Sams Publishing book called the Database Developer's Guide with
Delphi 2 (Ken Henderson, ISBN 0-672-30862-2), which goes into considerable depth
on connecting to most of the major servers. The portion of the book you want to
see is Chapter 24, "Delphi's Database Drivers Demystified." Almost anything this
book says about Delphi databases will apply equally to BCB's databases. Delphi
and BCB are sister and brother tools, and their approach to databases is almost
identical.
I will, however, go to some lengths to ensure you are properly connected to
InterBase, when I introduce that topic in Chapter 15. Until that time, there is
no need for you to be connected to InterBase while reading this book. I should
add, however, that connecting to InterBase is a simple task that should have
been done for you automatically during the install of BCB. To check whether you
are properly connected, try using the IBLOCAL alias set up by the
install program for connecting to an InterBase table called Employee.gdb.
Aliases
You can create aliases inside any one of three tools:
¡¡
- The SQL Explorer accessed through the Database | Explore menu option. From
inside this tool, choose Object | New to create an alias.
¡¡
- The Database Desktop. This is a stand-alone program installed by default
in the Program Files\Borland\Database Desktop directory. Choose Tools
| Alias Manager from the DBD menu to create an alias.
¡¡
- The BDECFG32.exe program found in the directory where the BDE is
installed. Turn to the Alias page in this program to create an alias.
¡¡
Both Paradox and InterBase aliases are created automatically when you install
BCB. You can study these aliases as guides when creating your own aliases. Also
see the readme files on the CD that accompanies this book, and the section on
creating ODBC aliases near the end of this chapter.
Various installation programs, such as Wise from Great Lakes Business
Software (www.glbs.com,
Tel (313) 981-4970, Fax
(313) 981-9746) and InstallShield, can create aliases for you automatically.
If you need to add aliases to a client machine during installation, you should
let one of these programs handle it for you. They will also automate the
installation of the
BDE. InstallShield Express ships in the ISX directory found on some versions
of the BCB CD.
Some Notes on Installing TCP/IP
In this section I briefly discuss the process of setting up TCP/IP on a
Windows 95 machine. The process should be nearly identical on a Windows NT 4.0
machine, though the dialogs might have a slightly different name or appearance.
TCP/IP is the protocol of choice when connecting to client/server databases.
It ships automatically with the 32-bit Windows products. To see if it is
installed on your system, open the Control Panel and launch the Network applet.
If you have TCP/IP installed, it will show up on the Configuration page of this
applet, as shown in Figure 8.12.
FIGURE 8.12. The TCP/IP information from the
Control Panel.
If TCP is not installed, you should push the Add button on the Configuration
page and bring up the Select Network Component Type dialog. Select Protocol from
the list of drivers, and again choose the Add button. In the Select Network
Protocol dialog, choose Microsoft in the left-hand list box, and TCP/IP in the
right-hand list box. Windows will then install the necessary software, which may
require the use of your Windows Install CD-ROM.
You will probably also have to specify an IP address, subnet mask, gateway,
and DNS server. This information can be garnered from your network
administrator. If you are working on a small local network with Windows machines
that you have set up in your office or home, you can ignore the DNS server, and
can make up your own IP address, subnet mask, and gateway. For instance, the
following numbers would do, as long as you are not connected to the real
Internet, and are only talking to the machines in your home or office:
IP Address: 143.186.186.2
Subnet mask: 255.255.255.0
Gateway: 143.186.186.1
The other machines on your network should have the same subnet and gateway,
but the IP address should be unique. For instance, the next machine should have
an IP address of 143.186.186.3, and then 143.186.186.4, and so
on. Remember, don't make up your own numbers if you are connected to the real
Internet! If you have an Internet connection, contact your network administrator
or Internet service provider (ISP).
¡¡
- ¡¡
¡¡
¡¡
NOTE: If you are appalled by my
suggestion that people make up their own IP addresses, you should remember
that many people connect their machines without being on the Internet. I do
this all the time with two laptops when I am on the road showing BCB and
Delphi. I also have a network at home with machines on it that are never
connected directly to the Net.
I want to stress, however, that if you are connected to the Internet, it is
very simple to ask your IS department for an IP address for your computer.
There are plenty of IP addresses in this world, and everyone can afford to
have several for their own use.
¡¡
¡¡
To check whether you are connected properly, open up a DOS window and try to
ping one of the machines in your network. Ping is a built-in application that
ships with Windows 95 and Windows NT. If you installed TCP/IP as explained
previously, ping will be set up on your machine.
To get started, you can try to ping yourself:
Ping 143.186.186.2
Here is a built-in address for referencing your own machine:
Ping 127.0.0.1
Or you can try to ping one of the other machines in your network:
Ping 143.186.186.3
Here is the result of successful session:
c:\4dos>ping 143.186.186.2
Pinging 143.186.186.2 with 32 bytes of data:
Reply from 143.186.186.2: bytes=32 time=55ms TTL=32
Reply from 143.186.186.2: bytes=32 time=1ms TTL=32
Reply from 143.186.186.2: bytes=32 time=1ms TTL=32
Reply from 143.186.186.2: bytes=32 time=1ms TTL=32
c:\4dos>
Here is the result of a failed session:
c:\4dos>ping 143.186.186.3
Pinging 143.186.186.3 with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.
c:\4dos>
Failed sessions usually occur because your machine is not configured properly
or else the wires connecting you to the network are not set up correctly. (For
instance, you might have forgotten to plug into the network!)
If you are attached to the Internet and have a DNS server, you can try to
ping one of the big servers on the Net:
Ping compuserve.com
¡¡
Here is successful session:
c:\>ping compuserve.com
Pinging compuserve.com [149.174.207.12] with 32 bytes of data:
Reply from 149.174.207.12: bytes=32 time=298ms TTL=239
Reply from 149.174.207.12: bytes=32 time=280ms TTL=239
Reply from 149.174.207.12: bytes=32 time=333ms TTL=239
Reply from 149.174.207.12: bytes=32 time=332ms TTL=239
c:\>
Pinging compuserve.com is the same thing as pinging
149.174.207.12. In fact, it's the job of the DNS server (the Domain Name
Server) to resolve a human-readable name such as compuserve.com into an
IP address.
If you want to create a human-readable IP address on a local office or home
network, you can edit the HOSTS files that ship with Windows 95 or
Windows NT. Under Windows 95, you will find a sample HOSTS file called
Hosts.sam in your Windows directory. Here is what this file looks like:
# Copyright (c) 1994 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Chicago
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a `#' symbol.
#
# For example:
#
# 102.54.94.97 rhino.acme.com # source server
# 38.25.63.10 x.acme.com # x client host
127.0.0.1 localhost
You can rename this file to HOSTS. with no extension, and then add
your own list of IP address to it:
143.186.186.3 MarysPC
143.186.186.4 MikesPC
After doing this, you can ping the other machines with a human-readable name:
ping maryspc
Connecting to ODBC
ODBC is a popular means of connecting to databases. For many developers, ODBC
plays the same role in their development that the BDE plays in the life of
Borland developers. ODBC is so popular that Borland has added a high-performance
ODBC socket to the BDE that enables you to access data through ODBC.
¡¡
- ¡¡
¡¡
¡¡
NOTE: It's important to
understand that ODBC is not a database, but only a means of accessing a
database. ODBC is a standard for creating drivers; it is a not a type of data.
For instance, Borland developers commonly use SQL Links to connect to Oracle
or Sybase. If you wanted, you could also use ODBC drivers in lieu of SQL
Links, though this is usually not a wise thing to do because ODBC is often
slow, while SQL Links are usually fast.
¡¡
¡¡
The main appeal of ODBC is its popularity. There are ODBC drivers for
connecting to nearly every database imaginable. If Borland does not ship with
drivers for the data you want to access, the logical thing to do would be to
search for ODBC drivers and then use them from BCB through the ODBC socket layer
currently under discussion in this section of the chapter.
Some copies of BCB ship with an ODBC driver for InterBase. Because this
driver is readily available, I will use it as the model for this discussion.
To get started using ODBC, you should close down your Borland tools, open up
the Windows Control Panel, and start one of its applets called, quite
poetically, "32 Bit ODBC." This applet can be used to manage the ODBC
connections on your machine.
As shown in Figure 8.13, on the bottom-right corner of the applet is a button
with the word Driver on it, and another one right above it with the word Add on
it. If you click either button you can see a list of the drivers available on
your system.
FIGURE 8.13. The 32-Bit ODBC application.
On my system one of the available drivers is called InterBase 4.X driver by
Visigenic (*.gdb). If I selected this button in the Add dialog, the
dialog shown in Figure 8.14 would appear.
FIGURE 8.14. The dialog used for configuring
an ODBC connection.
To set up a connection to the Employee.gdb table through the local
InterBase server, I fill out the fields of the dialog, as shown in Figure 8.14.
The database field of the dialog might have a path in it that looks like this:
c:\program files\borland\intrbase\examples\employee.gdb
The rest of the fields should look like this:
Data Source Name: InterBase Test
Description: Test of InterBase
Driver: Local
DataBase: c:\program files\borland\intrbase\examples\employee.gdb
User Name: SYSDBA
Password: masterkey
You should enter SYSDBA as the user name, and enter masterkey
as the password. After entering all this information, you should be able to
connect to the table by pressing the Test Connect button. However, you may not
be able to run this test until you first press OK in the dialog, and then bring
the dialog back up by pressing the Settings button from the main screen of the
32-Bit ODBC applet.
If all is working correctly, you can now bring up the BDE Configuration
utility that ships with Borland C++Builder. On the drivers page pick New ODBC
Connection to bring up the dialog for creating a new BDE connection, as shown in
Figure 8.15. Fill in the fields as follows:
SQL_Link_Driver: ODBC_Test2
Default ODBC Drive: InterBase 4.x Driver by Visigenic
Default Data Source Name: InterBase Test.
After creating the driver for the ODBC test, switch to the Alias page of the
BDE Configuration Utility. Select New Alias and type in ODBCTest1. Set
the Alias type to ODBC_Test2. Click OK, save your work from the File
menu, and exit BDECFG.exe.
If BCB is already running, close it down. Now start up BCB and drop a
TTable object on the main form. Select ODBCTest1 from the list of
available aliases in the DatabaseName property. Now proceed as you
normally would, selecting a TableName and attaching a TDataSource
and TDBGrid object to the table. When prompted for a password, enter
SYSDBA as the Username and masterkey as the password. If you
want, you can relaunch the BDE configuration program and set the user name for
this alias permanently to SYSDBA.
In the summary presented here, I have hardcoded in the names of the various
drivers and aliases you create. You can, of course, name the alias anything you
want, just as you can enter whatever names you want in the Data Source Name and
Description fields of the ODBC Configuration dialog in the 32-Bit ODBC applet
from the Control Panel.
FIGURE 8.15. The dialog for creating a new
ODBC. connection.
¡¡
Summary
In this chapter you have learned some of the fundamental facts you need to
know to start accessing databases from BCB. In particular, you have learned
about the following:
¡¡
- The basics of using TTable and TQuery
¡¡
- The TDataModule object
¡¡
- The SQL Monitor and Database Explorer
¡¡
- TCP/IP and ODBC connectivity
¡¡
Now that the groundwork has been laid, the next few chapters start digging
into the objects that BCB uses to access databases. As a rule, this is not a
particularly difficult subject, but there is a good deal of information that
needs to be covered.
¡¡
Once you have the facts that you need at your fingertips, you can start
building real databases with BCB. C++Builder is one of the premier tools in the
industry for accessing data, so you should be prepared to find yourself quickly
writing powerful database applications that can be used by hundreds of people at
one time.
¡¡
|