Notes for week 8

For this week's links click here.

Abstract Factories and Factories Needed in This Database

Get Rental Button

You have used factories to create a pointer to objects retrieved from a sequential file after reading the type of object from the file. In this section you will consider the two types problems that require a factory to create a rental object. One of the simplest is a request for the rental information if the rental id is know. This information must be retrieved from two different tables to create a pointer to a Rental object of the appropriate type. You will learn to use the factory createFromDB to create this pointer.

 void __fastcall  TRentalForm::EditRentalClick(TObject *Sender)
{
   RentalPtr r = Rental::createFromDB(RentalId->Text.ToInt(),RentalQuery); 
   RentalType->ItemIndex = r->rentalType();
   CustomerId->Text = r->customerId();
   StayLength->Text = r->stayLength();
   CondoNumber->Text = r->unitNumber();
   BoatLength->Text = r->boatLengthRoomRate();
   Cost->Text= r->getTotal();
}

You can use this function when the user double clicks on a reservation in the CustomerForm summary box. Recall that in the case that the user clicks on a reservation, the handler calls TRentalForm::setOrder. This function does a partial clear, inserts the number of the order into the appropriate location and then calls EditRentalClick.

void  TRentalForm::setOrder(String rentId,TObject *Sender)
{
   RentalType->ItemIndex = 0;
   StayLength->Text = "";
   CondoNumber->Text = "";
   BoatLength->Text = "";
   Cost->Text = "";
   RentalId->Text = rentId;
   EditRentalClick(Sender);
}

Find Info Button

The Find Info button handler and the double click on the summary box in the customer form when you are looking for a customer record must fill the summary box with the list of rentals for the client. You will use the function getRentals to fill the summary box. Once again the abstract factory createFromDB is used to create pointers to each of the rental items retrieved for a given customer.

String TCustomerForm1::getRentals(String id)
{
   String retVal= "" ;
   RentalQuery->Close();
   RentalQuery->SQL->Clear();
   RentalQuery->SQL->Add( "Select * from Rental WHERE customerId like " + id );
   RentalQuery->Open();
   RentalQuery->First();
   double totalCost = 0.0;
   while  (!RentalQuery->Eof)
   {
      int rentalId = RentalQuery->FieldByName( "id" )->AsInteger;
      RentalPtr r = Rental::createFromDB(rentalId,CustomerQuery);
      retVal= retVal + r->getDescription();
      totalCost += r->getCost();
      RentalQuery->Next();
   }
   retVal = retVal +  "\t\t\t\t\t\t\t\tTotal:  "  
                           + CurrToStrF(totalCost,AnsiString::sffCurrency,2);
   return  retVal;
}

Rental Confirm Button

When the Rental Form Confirm button is pressed, you have to create a retal item from the information in the rental form. This is a fairly standard factory like those you used last semester.

void __fastcall  TRentalForm::ConfirmClick(TObject *Sender)
{
   RentalPtr r = Rental::create(RentalType->ItemIndex,
                                 CustomerId->Text.ToInt(),
                                 CondoNumber->Text.ToInt(),
                                 BoatLength->Text,
                                 Rental::RentalType(StayLength->Text.ToInt()));
   if  (RentalId->Text =="")
   {
      r->place(RentalQuery);
      //get id        
      RentalId->Text = r->id();
      // don't want to loose confirmation so no clear. 
   }
   else
   {
      r->setId(RentalId->Text.ToInt());
      r->replace(RentalQuery);
   }
   MainForm->CustomerForm()->updateInfoSummary();
}

Notice that two different database functions are associated with this problem. If the id is empty, this is a new reservation, so use place to add this reservation. If the id is not empty, then this is a change in the value currently in the database. The classes in the rental hierarchy will have a replace function for handling this.

The place functions for a mooring rental must call the parent place function. This function must know the actual key field value for the mooring entry in the database since it is part of the rental record. Key field values are assigned when the new entry is made in the MooringTable, so after the new mooring entry has been placed, you must retrieve the id from the database. The process of entering a new item into the database takes time, and the program doesn't wait until the database update is complete, so you must get the program to pause while the database does its job. You will notice that after the mooring is posted, the code calls for the program to sleep for 1000 milliseconds.

void Mooring::place(TQuery *source)
{
  source->Close();
  source->SQL->Clear();
  source->SQL->Add("Select * from Moorings ");//retrieve whole db
  source->Open();
  source->Append();
  source->FieldByName("length")->Value = _length;
  source->FieldByName("slipNumber")->Value = _slipNumber;
  source->FieldByName("days")->Value = _days;
  source->Post();
  Sleep(1000); //make sure that the datebase is updated before retrieving data  
  setRentalId(source->FieldByName("id")->AsInteger);
  Rental::place(source);
}

The Factory

The main secret in this problem is to retrieve the rental record first. This record contains the type of the record you seek. This is used to clone the rental pointer, and that pointer is used to retrieve the data. The database functions follow.

Rental* Rental::createFromDB(int id,TQuery *source)
{
  source->Close();
  source->SQL->Clear();
  source->SQL->Add( "Select * from Rental where id like "  + String(id) );
  //retrieve record from Rental db  
  source->Open();
  int type = source->FieldByName( "rentalType" )->AsInteger;
  // Builder made this necessary 
  Mooring m;
  Condo c;
  Rental* kind[] = {&c,&m}; ;
  Rental* returnVal = (Rental *)kind[type]->clone();
  returnVal->retrieve(source);
   return  returnVal;
}

The Database Functions

The retrieve function is virtual in the Rental hierarchy. Each concrete member of the hierarchy first calls the retrieve for rental then uses the id retrieved by that function to retrieve the rest of the data from the appropriate table. The Condo retrieve is very similar to this one. Notice that by the time the Rental retrieve function is called, the location of the item has been established in the createFromDB function. On the other hand, the location of the rest of the data must still be determined. So in the Mooring retrieve below and the Condo retrieve (not shown) the item is first located in the appropriate table.

 
void Mooring::retrieve(TQuery *source)
{
   Rental::retrieve(source);
   source->Close();
   source->SQL->Clear();
   source->SQL->Add( "Select * from Moorings where id like " +rentalId());
   source->Open();
   _id=source->FieldByName( "id" )->AsInteger;
   _length = source->FieldByName("length")->AsInteger;
   _slipNumber = source->FieldByName("slipNumber")->AsInteger;
   _days = source->FieldByName("days")->AsInteger;
   source->Close();
}

 void  Rental::retrieve(TQuery *source)
{
  _customerId=source->FieldByName("customerId")->AsInteger;
  _id= source->FieldByName("id")->AsInteger;
  _rentalId = source->FieldByName("rentalId")->AsInteger;
  _rentalType = Rental::RentalType(source->FieldByName("rentalType")->AsInteger);

}

Final Project

We will spend day two of this week discussing the final project and there will be time for groups to consult and prepare for the break.