|
YOUR FEEDBACK
Did you read today's front page stories & breaking news?
SYS-CON.TV SYS-CON.TV WEBCASTS |
POWERBUILDER LINKS YOU MUST CLICK ON All Things New PowerBuilder Standards and Frameworks
Building a better foundation
By: Steve Katz; Larry Cermak
Oct. 27, 2005 04:15 PM
The ability to call stored procedures or functions in the database as remote procedure calls (RPCFuncs) is very powerful. However, in order to use RPCFuncs, you must create a standard class object of type transaction (you can follow the same instructions as those describing DataStore objects below). Create the object and save it as n_tr in appbase.pbl. Your application can create a descendant of n_tr and declare application-specific RPCFuncs there. Another reason to create an object of type transaction would be to handle transaction management in a more structured way. You can create placeholder functions for of_Begin() and of_End() (either a commit or rollback ends a transaction) as well as coding default implementations for of_Connect(), of_Commit(), and of_Rollback(). Take a look at the PFC object pfc_n_tr in pfcmain.pbl for additional ideas or just copy that object and n_tr from pfemain.pbl to your appbase.pbl. (Note that pfc_n_tr is not decoupled from the rest of the PFC and some slight code modification will be necessary to completely decouple it.) DataStores do not have DBError event handlers nor can you add functions or other event handlers. However, it is extremely important that you handle database errors when you use DataStores. The way to do this is to create a new object of type DataStore. From the File menu, select New (see Figure 1), then click on the PB Object tab, select Standard Class, specify the target in your workspace in which you want to create the object, and then click OK. Highlight DataStore in the Standard Class Type list (see Figure 2) and click OK. You will now be able to write scripts, create functions, add instance variables, etc., in an object of type DataStore that, otherwise, would not have these capabilities. In our case, we know we need to handle the DBError event when we use DataStores. In our base DataStore object we can code the DBError event as indicated in Figure 3. There are additional things you may want to do, such as storing the error code and text values in protected instance variables for later user with a function such a of_GetLastError() that you can add to your DataStore object. Save your DataStore object as n_ds. Why would I need to have my instance variables scoped as protected rather than just being public or perhaps being private? If they're public, they can be modified by any code that uses n_ds, allowing an easy way to make those variables useless. Since the values are set within the object as a result of an action the object took - a retrieve or update, for example - we want to encapsulate the values within the object. How do we access the values? Through an accessor or getter function we write - in this case, of_GetLastError(). If we wanted the values to be settable by the outside world, we would create a mutator or setter function like of_SetError(integer ai_ErrorCode, string as_ErrorText). On the other hand, we don't want our instance variables to be private because that will prevent any objects that are inherited from this object from accessing the instance variables directly. Descendant objects, in most cases, are pure extensions of the ancestor object and should have full read/write privileges on instance variables declared in the ancestor. If the instance variables were scoped as private, even the descendants would have to call of_GetLastError() to have access to the values. And, if there were no mutator functions, as in our case, the descendant would never be able to modify the values. (If you really get fancy, you can create the instance variables on the ancestor as private and create a protected scope mutator function that the descendants can use. However, you should do this only if the variables to be set need to be validated in the mutator against a very strict and static set of values or ranges that can confidently be built into the ancestor object. It is still better to implement this in a descendant object that may have more specific uses such as this.) While issuing a rollback before a message box is crucial to ensuring that locks on the database are released as quickly as possible, the problem is determining which transaction object you should reference. However, since this is a base class, you can assume it will be SQLCA and just code rollback, without specifying a transaction object. This can always be overridden at a descendant level. Better yet, create a function, of_SetTransObject(), where you can keep track of the transaction object on which you would need to issue the rollback in case of an error. Declare the transaction object, of type n_tr, as a protected instance variable and name it itr. After building our base object library, we should have (at least) the following objects:
w_base This should cover most of the basic objects that we need and provide a foundation on which we can build our application, leaving room for some flexibility and extensibility. Now we're ready to get started. PBDJ LATEST STORIES . . .
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK BREAKING POWERBUILDER / SYBASE NEWS
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||