If you haven't yet installed DX, click here for Installation Notes. Code for the tutorial examples is located in the following directory:
Sybase users
Oracle users
The tutorials in this chapter use the OrderEntry database. The OrderEntry database is a simple set of tables intended to support an invoicing application for a sports equipment sales company. The database consists of several tables with keys that allow rapid joining of the tables. Some of the tutorials use joined tables to demonstrate some of the more sophisticated features of Database Xcessory and DBPak. This distribution includes command files to create the OrderEntry database. If you choose to build these sample applications, create the databases on your own system by using the scripts Oracle users
Informix users
Accounts | A list of all customers who have ordered or might plan to order from the company. Indexed by Account_Number, an integer key which should be unique for each record. |
---|---|
Inventory | A list of all items that may be ordered. Part_Number is the key and should be unique. |
Invoice | A table of orders placed with the company. Keyed on Invoice_Number, an integer key which should be unique for each record. Also refers to the Account table through the Account_Number field. |
InvoiceDetail | Has one record for every line on an order form. Related to the Invoice table through the Invoice_Number column. Invoice_Number column and the Line column together make a unique key, allowing records to be updated. |
Sales | Contains name of every sales employee in the company, keyed on the Employee_Id, a unique integer field. Territory field is a territory name that can be used later for generating statistics on sales by territory. |
SalesByTerritory | A view and not an actual table. Data in the view is constructed when the application requests it, an is based on the Invoice and Sales tables. |
keys | Used to help generate unique keys for other tables. Each row of keys table has a tablename and nextkey pair where the nextkey is the next unique key value for the table in the tablename field. For example, the first record of keys has: Tablename: Nextkey: Accounts 3000 where 3000 is the value of the next available unique key for the Accounts table. |
This tutorial describes how to build a simple form-based database application that allows users to view and edit customer records from the Accounts table. Using Database Xcessory, you simply select fields from the Schema Browser window and drag them to the application under construction. Database Xcessory will automatically set all the resources to the correct values.
A Simple Forms-Based Application
Widgets in an Xt (X Toolkit) based application are arranged in a hierarchical parent-child structure. Shell widgets are parents to containers that contain other containers and more fundamental primitive widgets. The primitive widgets are usually the ones into which you enter data and with which you interact.
Any window in your finished application must have a container, which must be inside a shell. The container holds the widgets that make up the form.
Using Database Xcessory, all you have to do to begin is create a container. This is because Database Xcessory will automatically create a shell if you create a container without a parent.
The container we'll use in this application is a bulletin board (XmBulletinBoard). The bulletin board allows you to place children (labelled text fields, in this case) anywhere inside the bulletin board. A bulletin board imposes no constraints on the position of its children. The advantage of using the bulletin board as a container is that it is very easy to use. The disadvantage, for more sophisticated applications, is that it doesn't do anything special when the user resizes the window.
Use the following steps to create a bulletin board widget that will serve as the parent widget for the XiDBDataField widgets you will create later in this tutorial.
Notice the square widget outline that appears.
A top level shell widget is automatically created with a bulletin board inside of it. The hierarchy of these widgets is displayed as a tree display in the Browser window. The top level shell is typically used for windows that will be displayed most often in an application. You can create other types of shells, depending on the application. These are described in the Builder Xcessory Users Guide andBuilder Xcessory Reference Manual.
Once you've created a container, the next step is to add the labelled data fields using the Schema Browser window. You can use the Schema Browser as if it were a palette of Data Presentation widgets. Drag field names from the Schema Browser to the application. Database Xcessory then creates data fields, graphs, tables, and other Data Presentation widgets with the resources automatically set to display and edit that field (if editable; for example, if you create a graph instead of a data field, that graph is not editable).
Use the following steps to add labelled data fields as children of the bulletin board container you have already created:
The Database list (Sybase) or Owner list (Oracle) is located in the upper right corner of the Schema Browser. To display all available databases (Sybase), or owners (Oracle), click on the down arrow. Click on a database name (in this case, OrderEntry) or owner name (in this case DBPak) to select it.
The Tables list is located on the left side of the Schema Browser. Click on the Accounts table to select it.
The Columns window list now shows all the columns that correspond to the Accounts table along with their data types. The Sample Data window at the bottom of the Schema Browser now displays data that has been read from the first set of rows in the table.
Declarations of Accounts Table (Sybase version).
Check that Data Field is selected in the Object Type option menu of the Schema Browser. (The Object Type option menu is located in the upper left corner of the Schema Browser.)
To display the option menu:
The Object Type option menu displays the type of widget that will be created when you drag a field from the Schema Browser. Most other DBPak widgets can also be created with the Schema Browser.
Check that Labeled Fields is toggled on in the Schema Browser Option menu. The Option menu is in the menu bar at the top of the Schema Browser. The Labeled Fields menu option is toggled on by default.
Multiple Columns Selected
One XiDBDataField will be created in your interface for each column definition selected. These widgets are pre-configured to display data from the columns selected in the Schema Browser.
Database Xcessory creates the Data Source, Query and Data Field widgets. The children of the bulletin board, in the Browser object instance hierarchy look like the Instance Hierarchy shown below:
Instance Hierarchy
Notice that two objects, q_Accounts (Query widget) and orderEntry (Database Access widget) for Sybase, or q_ACCOUNTS andtest for Oracle, have been created but are not physica>
Note: We recommend that you print this tutorial for easy reference. To print, click on the right-hand frame before accessing your HTML Browser's File/Print command.
In this tutorial you will develop a form that has fields from two tables. A join is often necessary when there are fields from more than one table that will be displayed on the same form. In this case, you will be joining the Invoice table and the Accounts table.
Accounts are organized by a unique integer key, Account_Number. The Invoice table could store all the account information (such as the name, address, city, state, and zip code), but there is no reason to duplicate this data since it is already in the Accounts table. The Invoice table has eight fields. Only one of these shares information with the Accounts table. This field is the Account_Number field.
The Application You Will Create using a Simple Join
Use the following steps to perform a simple join:
The bulletin board should be about 3 inches high by 4 inches wide.
Add the fields from the Invoice table to the application. Later, you will add the account information.
Since the two fields are next to each other in the list, click on the Invoice_Number field and drag down to the Account_Number field to select both fields at the same time.
Place them in the upper left corner. Leave room to add the account information later.
If the Accounts information is not aligned with the Invoice, you can move them by selecting all the Accounts widgets and dragging them to a location of your choice. Hold down the Shift key and drag a rectangle with MB1 around all the Account fields to select all the widgets. Move them together by holding down the Ctrl key, positioning the pointer over one of the selected widgets, and dragging MB1 until the group is properly aligned.
When selecting a group of fields, if you inadvertently select the background container widget, you cannot move the group of widgets. Deselect the container by holding down Ctrl and click MB1on the container (the easiest way is to deselect the container object on theBrowser hierarchy). The other objects in the group remain selected, and you can now move the group.
To display and set the necessary resources for this application:
- Click on the Update icon located in the tool bar below the Resource Editor menu bar.
- Double-click on the widget to list the widget's resources and their settings in a scrollable window at the bottom of the Resource Editor.
Note: All database-related resources are grouped at the end of the list to help differentiate them from the appearance-setting resources.
Note: This resource was set automatically to both the Invoice and Accounts table when you dragged fields from both tables into the application. Although it is not necessary to supply the value, you must use qualified names in the data fields XmNexpression resources to allow DBPak to determine from which tables to obtain data.
where Resource Edited to Create the Join
After typing the value into the text input field, be sure to either press Enter or click on the OK button that is displayed to the right of the input field.
The text entered into the where resource is copied into the where
clause that is part of the SQL statement that is constructed and sent to the database server. By specifying the join conditions, the server will return data from both the Invoice table and the Accounts table, where the Invoice table's account number matches the Accounts table account number.
accounts_name
Drag the Control Panel from the DBPak Controls group on the Palette. If you have any questions about how to do this, see Adding a Control Panel to the Application.
Refer to Testing the Application in Play Mode for information about testing your interface.
When you enter Play Mode, you can use the control panel widgets to navigate through the data. Start by pressing Search, and notice that the Account information updates together with the Invoice information. The where
clause keeps them synchronized.
You can restrict the set of values shown when browsing through displayed data by using a technique called Query by Example.
Suppose you are looking for an invoice from a customer, but you have forgotten the customer's name. You know the customer's name begins with the letter "R".
To find the customer's name using QBE, use the following steps:
By default, the interface is in QBE Mode. If you enter data into any of the data fields and then initiate a search, that data will be interpreted as search strings to restrict the rows that will be returned by the server.
The "R%" represents all names that have an R as the first character and any number of other characters following. The percent character (%) is an SQL wild-card character that matches any string.
DBPak contains many capabilities for controlling database queries. For example, if you want this field to always search for values that begin with what was entered (without the user being required to enter the% operator), then you can set the Data Presentation widgets's XmNqbeOperator resource to XiDB_QBE_BEGINS_WITH. Refer to "XmNqbeOperator"in DX Hyperhelp.
You can now browse through all the invoices restricted to those having a customer name starting with the letter "R".
You can change the search parameters by clicking on the Stop button in the control panel. This places the interface back in QBE mode and you can then enter a new search string.
You will build upon this newly created interface in Tutorial 3: Master-detail Display and Entry Form Application. Write out the .uil
file for the interface and save it so you can use it for Tutorial 3 at your leisure.
First, make a directory in which to save tutorial .uil
files. The following steps describe how to write and save the .uil
file for an interface.
Note: The default path displayed at the top of the dialog is the path of the directory from which you started Database Xcessory.
Either enter the full path name of the directory in which you want to save the .uil
file or use the list of current directory contents to navigate to the desired directory.
uil
.uil
, which can be read back into Database Xcessory to reconstruct the collection.You created an interface with two tables joined together by setting a single resource on the query widget. Database Xcessory did the rest automatically. When you press the Search button, the Query widget (to which the Search button points) builds an SQL select
statement by:
select
statement.from
clause in theselect
statement.where
resource (and other query resources) to build the remainder of the select
statement.The query widget then sends the select
statement to the server.
Note: We recommend that you print this tutorial for easy reference. To print, click on the right-hand frame before accessing your HTML Browser's File/Print command.
In this tutorial you will build upon the previous application to create an order display and entry form. This will allow you to browse through all the invoices in the database and see the items ordered for each invoice in a table along with the order information. In a master-detail relationship, the master (or driving table) represents the "one" side of a one-to-many relationship, and the detail table is on the "many" side.
In this case, the InvoiceDetail table is related to the Invoice table by the Invoice_Number field. Although the Invoice_Number is enough to relate the InvoiceDetail records to the correct invoice, the Line field, a line number, is required to ensure that each InvoiceDetail record is uniquely specified when updating the table. Both of these fields make up the key for the InvoiceDetail table.
If you saved the .uil file for the Tutorial 2 interface, select Open or Read from the Browser File menu and select the file from the File Selection dialog that is displayed. The interface should look like:
Simple Join Application You Created in Tutorial 2
Use the following steps to add a table to the application shown above.
Existing Interface Resized
Option Menu Items
Table Added to Application
It is necessary to specify a where
clause that retrieves all of the Invoice_Detail records for the invoice number currently displayed in the Invoice Number field on the application.
where
clauseTo specify a where
clause, use the @value()
function within a "where" clause:
Table Widget Selected
where
resource in the Resource Editor.where
resource, enter the following: "InvoiceDetail.Invoice_Number = @value(invoice_Invoice_Number)".This creates a relationship between the dbTable widget and the invoice_Invoice_Number widget. When the value ofinvoice_Invoice_Number changes, it is substituted into the "where" clause of dbTable and the query is re-evaluated.
Note: We recommend that you print this tutorial for easy reference. To print, click on the right-hand frame before accessing your HTML Browser's File/Print command.
Most of the time you will probably use textual data fields (such as XiDBDataField) to allow the user to enter data into your application, but sometimes you may want to present the user with a set of choices.
In this tutorial you will create an application that allows the user to choose one salesperson from a list of all salespeople and displays all of that salesperson's sales (invoices) in a table. We'll use an XiDBCombinationBox for the one-of-many choice.
Use the following steps to create the application:
One Column Added as a Combination Box to the Interface
Enumerated widgets retrieve their choices from their XmNmap resource, in a way that is determined by the value of the XmNmapType resource. You will set XmNmapType to XiDB_MAP_SQL, which says that the XmNmap specifies an SQL statement that lists the choices.
The default setting for the map resource is XmNmap_Unset. The map resource has a text input field, so double-click on the default value and delete it to clear the input field.
The value of the widget is the first column--in this case, the employee ID. By specifying a second column, we are saying that the value in the second column (the name of the salesperson) should be displayed in the widget and then mapped to the first column value (for example, Employee_Id) if it is selected. We're doing this here so that we can select by name, but then use the corresponding Employee_Id in a join.
This resource should be located directly below the map resource.
By default, this resource is set to XiDB_MAP_STRING. Instead of a text input field, this resource presents an option menu of settings. Select XiDB_MAP_SQL from the option menu items.
All Possible Settings Displayed for mapType Resource
By default, when you click on the arrow of the combination box you have created, only one choice will be displayed. The visibleItemCount resource determines how many items will be displayed when you click on the arrow.
The next step is to create the table.
A table of the selected Invoice columns is created on your interface.
Although the combination box is connected to a query, you will not use that query in the search. The table acts as the query for the purposes of searching.
All Columns from Invoice Table Added to Interface as a Table
Either click on the dbTable object on the Browser hierarchy, or click on the table on the interface to select it.
Or use the mouse to resize the table. For example, use MB1 to drag the bottom edge down. The purpose of resizing the table is to display more data at once.
The table should now look like:
Resized Table
Once you have created the table, the next step is to set resources for it:
where
resource.where
input field, enter "Invoice.Employee_Id = @value(sales_Employee_Id)" to set the where
clause to retrieve only those invoices that correspond to the currently selected salesperson.You can now go into Play Mode and test the application:
Combination Box Choices Displayed
Information Associated with Customer Name Displayed in Table
In the previous tutorials you have created interfaces that presented information in the form of labelled data fields, enumerated widgets, and tables. One of the most exciting features of DBPak is that other presentation styles are available. In this tutorial you will learn how to present sales data graphically using exactly the same techniques as before.
When creating graphs, you will usually want to group the data by some common value. If you think about graphs you've seen, they seldom display actual data. They usually display sums or totals compared to each other. This grouping is accomplished with the XmNgroupBy resource on queries and widgets with query attributes, like the plotter in this example.
Suppose that you want to find out which items in your company's inventory are selling and which items are just taking up shelf space. One way of presenting the data is through a column of numbers, but it would be hard to find the "interesting" data. By creating a graph, however, those interesting values would become very obvious.
You will create a bar graph of sales for each item in your inventory with one bar per item. Individual item sales can be found in the InvoiceDetail table, while the actual descriptions of the items are in the Inventory table.
Notice that Database Xcessory created a XiDBPlotter widget along with the other familiar basic widgets, such as the Control Panel.
The Plotter widget is a container that holds various plots and axis widgets.
If you select Show Compound Children from the Browser View menu, you can see the axis widgets (xaxis and yaxis) that were automatically created, as shown below:
Compound Children of dbPlotter Widget
Database Xcessory sets the plotter's xColExpression resource to Inventory.Description. Those values will label the x axis when you switch to Play Mode to test the interface.
dbPlotter Display on Your Interface Before Entering Play Mode
Alternatively, you may drag the Price field onto the Plotter widget in the interface. XiDBBarPlot is now created as a child of the plotter widget, shown below:
Bar Plot (invoiceDetail_Price) is Child of Plotter Widget (dbPlotter)
The Plotter is a container widget that has x-axis and y-axis compound children. When you add a plot widget (such as a Bar Plot) to the Plotter, you are adding just the plot and a set of y values.
You may be wondering why a Plotter widget was created the first time, and a Bar Plot widget is created this time. Database Xcessory notices that you are dragging the field into an existing Plotter widget and it created a BarPlot. Each new plot dragged onto the Plotter widget will be added as a child and another set of y values. You can mix plot types in one plotter container.
How Interface Appears Before Entering Play Mode
We want the value of all like items on all invoices, and not just the price for a single unit. We have to take into account any lines in which a person ordered more than one item. This expression is added to the SQL select
statement that is generated and sent to the server. The server performs the calculation, and returns a single value to be used by the bar plot.
This joins the Inventory table to the InvoiceDetail table. By setting the where
resource, you can then use values from both tables with a single query. When the data is fetched from the database, both tables will be consulted.
All resources from the Plotter and the BarPlot are pulled together to create an SQL select
statement to gather data for the graph. The statement sent looks like the following:
SELECT
Inventory.Description
,
sum(InvoiceDetail.Price * InvoiceDetail.Quantity)
FROM
Inventory, InvoiceDetail
WHERE
Inventory.Part_Number = InvoiceDetail.Part_Number
GROUP BYInventory.Description
When reviewing these steps you can see how this statement was constructed: the select expressions came from the dbPlotter and dbBarPlot expression resources, the where
clause came from the where
resource on the dbPlotter, and the group by clause
came from the groupBy
resource on the dbPlotter.
When you press the Search button, the data is fetched and the graph is displayed, as shown.
Interface In Play Mode
You must make the window very wide so that the inventory descriptions aren't displayed on top of each other.
A stored procedure is a named group of SQL statements that have been previously created and stored in the server database. Stored procedures accept input parameters so that a single procedure can be used over the network by several clients using different input data. Stored procedures reduce network traffic and improve performance. Additionally, stored procedures can be used to help ensure the integrity of the database.
DBPak can use stored procedures as an alternate means of inserting, deleting, or updating records.
In this tutorial, we'll create and use a stored procedure as part of a new part-entry application. In the OrderEntry database, adding a part means inserting a new record into the Inventory table.
Suppose the following restrictions have been placed on the database. In the Inventory table, we have imposed the rule that all part numbers must be entered in uppercase. In addition, all fields in each record must be specified--none can be left blank. These are restrictions that we would rather not have in our application. Therefore, the first step is to write a stored procedure to take input data from our application and make it conform to the specified rules.
This section describes the Sybase and Oracle versions of the sample stored procedure provided for use with this tutorial.
create procedure newPart @pno PartType, @desc varchar(40), @price money @quant int = 0, @reorder int = 10 asThe following code converts the part number to uppercase:
declare @newPno PartType select @newPno = upper(@pno)If the quantity and reorder quantity values are NULL, the default values are used:
if @quant is NULL select @quant = 0 if @reorder is NULL select @reorder = 10The following code inserts the record into the Inventory table:
insert into Inventory (Part_Number, Description, Price, Quantity_On_Hand, Reorder_Quantity) values (@newPno,@desc,@price,@quant,@reorder )We select the fields from the record that we just inserted and use them as the return value of the procedure. These values will be displayed in DBPak widgets.
select Part_Number, Description, Price, Quantity_On_Hand, Reorder_Quantity from Inventory where Part_Number = @newPnoThis is the complete stored procedure. The next step is to use it in Database Xcessory. We will create a data entry screen with the Schema Browser.
create or replace procedure newPart( pno IN OUT char, thePrice IN OUT NUMBER, part_description IN varchar, quantity_on_hand IN OUT integer, reorder IN OUT integer ) as newPno CHAR(8); quan integer := 0; BEGIN
The following code converts the part number to uppercase:
newPno := UPPER(pno);
If the quantity and reorder quantity values are NULL, the default values are used:
IF (quantity_on_hand is not NULL) then quan := quantity_on_hand; end if; IF (reorder is NULL) then reorder := 10; end if;
The following code inserts the record into the Inventory table:
INSERT INTO INVENTORY (PART_NUMBER, DESCRIPTION, PRICE, QUANTITY_ON_HAND, REORDER_QUANTITY) VALUES (newPno, part_description, thePrice, quan, reorder);
We select the fields from the record that we just inserted and store the values in the in/out parameters of the stored procedure. These values will be displayed in DBPak widgets.
SELECT PART_NUMBER, PRICE, QUANTITY_ON_HAND,REORDER_QUANTITY
INTO pno, thePrice, quantity_on_hand, reorder
FROM INVENTORY WHERE PART_NUMBER = newPno;
END
Interface Created by Dragging and Dropping All Inventory Fields onto Browser
The Quantity_On_Hand and the Reorder_Quantity fields are considered optional items.To tell DBPak that these fields are not required for an XiDB_INSERT operation, set the XmNrequiredForInsert resource to False for each of these fields:
Use the following steps to tell DBPak to use the stored procedure you have written in this tutorial when performing an insert.
inventory_Part_Number, inventory_Description,
inventory_Price, inventory_Quantity_On_Hand,
inventory_Reorder_Quantity <- newPart(
[inventory_Part_Number], [inventory_Description],
[inventory_Price], [inventory_Quantity_On_Hand] opt,
[inventory_Reorder_Quantity] opt)
This resource setting specifies the following:
Call the
newPart
stored procedure with the following arguments:
- The current contents of the inventory_Part_Number, inventory_Price, and inventory_Description widgets:
[inventory_Part_Number], [inventory_Description], [inventory_Price]
- If they are not empty, the current contents of the inventory_Quantity_On_Hand and inventory_Reorder_Quantity widgets:
[inventory_Quantity_On_Hand] opt, [inventory_Reorder_Quantity] opt
- Assign the results of stored procedure "newPart" to the following widgets:
inventory_Part_Number, inventory_Description, inventory_Price, inventory_Quantity_On_Hand, inventory_Reorder_Quantity <- newPartThis ensures that the user sees the values that have actually been entered into the database.
The following resource setting is included as the tutorial-6.text file in your example/dx-tutorials/oracle directory. You can cut and paste the following from the file into the insertProc input field in the Resource Editor:
newPart([inventory_Part_Number] in out, [inventory_Price] in out, [inventory_Description], [inventory_Quantity_On_Hand] in out opt, [inventory_Reorder_Quantity] in out opt )
This resource setting specifies the following:
Call the newPart stored procedure with the following arguments: The current contents of the inventory_Part_Number, inventory_Price, and inventory_Description widgets: [inventory_Part_Number] in out, [inventory_Price] in out, [inventory_Description], If they are not empty, the current contents of the inventory_Quantity_On_Hand and inventory_Reorder_Quantity widgets: [inventory_Quantity_On_Hand] in out opt, [inventory_Reorder_Quantity] in out opt
Note: The parameters [inventory_ Part_Number], [inventory_Price], [inventory_Quantity_On_Hand], and [inventory_ Reorder_Quantity] are used for both input and output ("in out" parameters). The current values of each widget are used as input, and the values that the stored procedure returns are displayed in each widget. This ensures that the user sees the values that have actually been entered into the database.
You can now enter Play mode and test the interface.