|
In the case of database maintenance applications, I think it's helpful to separate code that handles database I/O from screen I/O code. Applications are easier to maintain, when the code is separated. If there is a problem with screen I/O code, there's no need to wade through database I/O code, and visa versa. I'll attach a sample source member for a database I/O module at the end of this message. I normally create individual service programs from database I/O modules. In database maintenance applications, I use SQL for set oriented operations (queries and lists), but I still have a strong preference for traditional RPG operation codes for record-level access. Performance is much better, the data is always current, and there's no need for object-relational mapping middleware (like Hybernate for J2EE interfaces). The code I'll append to this message supports a browser based user interface that combines a scrollable list with a data entry form on a single screen. When users click on one of the records in the list, a request is sent to the server to retrieve the complete record from the database file, and update the browser's "content" frame. Users can also press arrow keys to navigate from row to row in the list. A highlight bar moves from row to row when arrow keys are pressed and the content frame is updated with a complete view of the record. If a user presses and holds down an arrow key, the highlight bar moves from row to row at a rate of about 25 rows per second, and the server is flooded with requests for database I/O and screen I/O, and records fly past in the content frame at a rate that's humanly impossible to read. Users like this level of performance and interactivity, and the only way I can conceive of delivering that level of performance is via RPG's record-level-access operation codes. When updating records, I agree that some applications may need to first check whether a record was previously updated by another user, and handle an exception, but in other applications, the rule may be that the last user to update the record is given the final say (configuration files, for example). A basic database I/O model can be extended to handle both situations if needed. Nathan M. Andelin ------------------------ RPG Database I/O module ------------------------ //----------------------------------------------------------------- // headers //----------------------------------------------------------------- H CopyRight('2006 Relational Data Corporation') nomain //----------------------------------------------------------------- // file specifications //----------------------------------------------------------------- FXTD140P UF A E K DISK //----------------------------------------------------------------- // copy members //----------------------------------------------------------------- /COPY *LIBL/QMODELSRC,WLMAIND1#1 /COPY *LIBL/QRPGLESRC,RDMSGAPI#1 //----------------------------------------------------------------- // module level data structures //----------------------------------------------------------------- D dettKey DS Export D det_key1 10A D det_key2 10A D det_key3 10A D msgDs DS Export D msg_type 1A D msg_text 128A Varying //----------------------------------------------------------------- // record buffers (containing prior and current state of a record) //----------------------------------------------------------------- D dettrec E DS Extname(XTD140P:XTD140R) D Export Prefix(m_) D dettrec1 E DS Extname(XTD140P:XTD140R) //----------------------------------------------------------------- // module level variables //----------------------------------------------------------------- D focusfld S 10A Varying Export D msg_module S 10A Inz('WLMAINT') //----------------------------------------------------------------- // key lists //----------------------------------------------------------------- C kls Klist C Kfld m_APP C Kfld m_FORM C Kfld m_FIELD //----------------------------------------------------------------- // initialization / use external application message file //----------------------------------------------------------------- P d1Init B Export /free msgFileOpen(msg_module); clear dettKey; clear dettRec; clear dettRec1; /end-free P d1Init E //----------------------------------------------------------------- // clean up //----------------------------------------------------------------- P d1Term B Export /free msgFileClose(); /end-free P d1Term E //----------------------------------------------------------------- // get record //----------------------------------------------------------------- P d1GetDettRec B Export D d1GetDettRec PI N /free Clear dettrec; Clear dettrec1; m_APP = det_key1; m_FORM = det_key2; m_FIELD = det_key3; Chain(n) kls XTD140R; If not %found; msg_type = type_error; msg_text = msgGetText(msg_module:1); Return *Off; Endif; dettrec = dettrec1; Return *On; /end-free P d1GetDettRec E //----------------------------------------------------------------- // change record //----------------------------------------------------------------- P d1ChgDettRec B Export D d1ChgDettRec PI N /free If not d1VfyDettRec(); Return *Off; Endif; Chain kls XTD140R; If not %found; focusfld = 'TITLE'; msg_type = type_error; msg_text = msgGetText(msg_module:2); Return *Off; Endif; dettrec1 = dettrec; Update XTD140R; msg_type = type_info; msg_text = msgGetText(msg_module:3); Return *On; /end-free P d1ChgDettRec E //----------------------------------------------------------------- // add record //----------------------------------------------------------------- P d1AddDettRec B Export D d1AddDettRec PI N /free If not d1VfyDettRec(); Return *Off; Endif; Chain(n) kls XTD140R; If %found; focusfld = 'APP'; msg_type = type_error; msg_text = msgGetText(msg_module:4); Return *Off; Endif; dettrec1 = dettrec; Write XTD140R; msg_type = type_info; msg_text = msgGetText(msg_module:5); Return *On; /end-free P d1AddDettRec E //----------------------------------------------------------------- // delete record //----------------------------------------------------------------- P d1DelDettRec B Export D d1DelDettRec PI N /free Delete(e) kls XTD140R; If %error; msg_type = type_error; msg_text = msgGetText(msg_module:6); Return *Off; Else; msg_type = type_info; msg_text = msgGetText(msg_module:7); Return *On; Endif; /end-free P d1DelDettRec E //----------------------------------------------------------------- // validate record //----------------------------------------------------------------- P d1VfyDettRec B Export D d1VfyDettRec PI N /free msg_type = type_error; msg_text = msgGetText(msg_module:8); If m_APP = *Blanks; focusfld = 'APP'; Return *Off; Endif; If m_FORM = *Blanks; focusfld = 'FORM'; Return *Off; Endif; If m_FIELD = *Blanks; focusfld = 'FIELD'; Return *Off; Endif; If m_TITLE = *Blanks; focusfld = 'TITLE'; Return *Off; Endif; msg_type = type_info; Return *On; /end-free P d1VfyDettRec E ----- Original Message ---- From: "Takken, Cor" <cor.takken@xxxxxxxxxxxxx> To: RPG programming on the AS400 / iSeries <rpg400-l@xxxxxxxxxxxx> Sent: Wednesday, January 24, 2007 11:35:10 PM Subject: RE: Organising modules, programs and service programs Johan, Externalizing file I/O is in my view the first step towards an architecture which enables you to use the same database layer (with all checks, rules and what have you) whether you try to do things via a web application or 'old fashioned' green screen terminal or enterprise server bus. A very usefull architecture with a lot of potential. However nice this is, you will have to do something about the fact that most oldfashioned programs are written in a fashion which implicitly takes for granted that the record locks are exclusive are placed and remain there in the program while it runs (or whenever the lock is released by another read etc.). If you externalize the file I/O to a service program, this no longer holds true. Take the example of a program which reads a record, goes into a subroutine which executes some stuff and then updates the record which it read before the EXSR. If the file is 'in the F-spec' this poses no problem, if the file I/O is done via a service program however there is a big snake under the grass: if the subroutine calls another program which also accesses this file where will the file pointer be directed at? Will updates have been done in the meantime by other processes in the same job? Is there a way to tell? The move to external file I/O should trigger you to check the architecture of all the programs in the (sub)system and an almost paranoid style of programming needs to be introduced: any update or delete needs to be preceeded with a read of the file (with lock) and a comparison of the old record buffer (the one before the fieldvalues were changed by the program) and the latest read record buffer. Only if the record contents is equal then the update or delete can go ahead. Granted: there are currently programs around which have this optimistic locking mechanism in place, but if this is not the case a considerable programming effort needs to go in to build this in (and test it thouroughly). Moving to SQL for file I/O is also suggested, and indeed SQL also offers a lot of advantages, however the optimistic locking mechanisms need to be in place as well. SQL also introduces a new concept of cursors, result sets, etcetera. Building up a subfile page by page and the scrolling mechanisms almost force you to link the I/O module to the program instead of exporting the program as a symbol from a service program because there is no easy way of transferring the resultset and the cursor attached between the service program and the using program. I am not saying the scrolling mechanism and cursor/resultset challenges compel you to link the I/O module to the program and that it is absolutely impossible to solve this another way. I am merely saying that any move away from 'ye olde way' of file I/O methods introduces all kinds of new issues which need to be addressed. Don't be surprised if the externalisation forces you to redesign the programs, and perhaps even forces you to rebuild your entire system. I am merely trying to warn anybody: The step of externalizing the file I/O to modules to the program is a step which is often taken much too lightly. Nonetheless it is an interesting subject and can offer a lot of advantages, or is even mandatory when moving to a service bus environment. My 2 cents, Cor Takken ____________________________________________________________________________________ Don't get soaked. Take a quick peak at the forecast with the Yahoo! Search weather shortcut. http://tools.search.yahoo.com/shortcuts/#loc_weather
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2025 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].
Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.