Documentos de Académico
Documentos de Profesional
Documentos de Cultura
ZeosLib
+ Firebird
to access Firebird databases by using the ZEOS component Library in version 6.1.5 (including Patches 1&2) and how to use these components in database applications. It does not matter if you use the "real" SQL-Server or the embedded version which is restricted to local held databases. A couple of examples (also migrated Delphi-BDE demos) shall explain how to use the ZEOS components.
Although this article describes the usage of the ZEOS Library using Firebird, all the basics can be used with other SQL servers / databases that are supported by ZEOS. Note: The Firebird Server can be downloaded from download section of http://www.ibphoenix.com http://www.firebirdsql.org
InstallingtheZEOSLibraryandadditionalstuff
TheinstallationoftheZEOSLibraryunderDelphi7professionalisnotthatcomplicated.Oncethecurrent ZEOSversionandallthepatches(whilewritingthisarticleitwaslibraryversion6.1.5andthecorresponding patches1&2)aredownloadedandunzippedintoadirectoryofyourchoicecompletelyyouonlyhaveto followtheinstallationinstructions(note:FirebirddoesnotneedanyadditionalDLL's,here!): OpenthedelphiprojectgroupZeosDbo.bpgfromsubdirectorypackages\delphi7ZeosDbo.bpgandinstall thefollowingcomponentsingivenorder: ZCore.bpl ZParseSql.bpl ZPlain.bpl ZDbc.bpl ZComponent.bpl
Additive:EventAlertercomponent(TZIBEventAlerter) Anadditionalcomponent,thatonlyexistsforFirebird(andalsoInterbase)interceptsall(registered)events, thataretriggeredbyStoredProceduresofadatabase,andmakesitpossibletoreactuponthem.This componentwaspostedbyAlexeyGilev(aka"GAF")intotheZEOSForumandmadeavailabletotheZEOS Team.Theoriginalversioncanbedownloadedhere: http://sourceforge.net/tracker/index.php?func=detail&aid=911233&group_id=35994&atid=415826 TheeventalerterisonlytestedforZEOSversion6.1.4butrunswithsomemodificationswithoutany problemsinversion6.1.5(withPatch1&2).TheeventalerterthatwasportedtoZEOSversion6.1.5willbe installedasfollows(pleaseconsidertheReadMefile!I'mnotgoingtogiveanywarrentythatthissolutionis errorfree!Youuseitatyourownrisk!): DownloadpatchforZIBEventAlerter(integrated)here unzipthefilesanddistributethemtotheaccordingZEOSdirectories ifnecessaryrecompileandreinstallZEOS(seeabove)
Basics:Transactions
SomemandatorybasicsabouttransactionshavetobetoldinordertounderstandhowtheTZConnection componentworksinternallyandhowtoworkwithit. Ingeneral:Youonlycanhaveaccesstoadatabasewithinthecontextofavalid("running")transaction.A transactionhastofulfillthefollowingfourcharacteristicsthatareknownasACIDcharacteristicsofa transaction.
Atomicity Allactionsperformedonadatabasehavetobeexecutedsuccessfully.Ifonlyoneerroroccursthenthe originalstateofthedatabasehastoberestored.Accordingtotheprinciple"allornothing". Consitency Atransaktiontransfersadatabasefromoneconsitentstatetoanotherconsistentstate.Ifanerroroccurs thentheoriginalstateofthedatabasehastoberestored. Isolation Atransactionhastobehandledbytheserverasifitwastheonlyonerunning.Thismeansithastorun indepentlyfromothertransactions.Ausermustnotnoticethechangesdonebyotherusers. Durability ChangesinthedatasetofadatabasethatarecausedbySQLstatementsthatareexecutedbetweenthe startofatransactionandaCOMMIThavetobefixedirrevocably. Transactionsencapsulateconsecutiveaccessesonadatabase.Adatabaseaccessmaybeofreadingor writingnature(INSERT,UPDATE,DELETE)oritmaychangethestructureofadatabase.Transactionsare terminatedbyCOMMITorROLLBACK.COMMITconfirmsallchangesinadatabasesincethestartofa transaction.ROLLBACKresetsallchangesinadatabasesincethestartofatransaction. Transactionsrunningontheserverhavetobeisolatedfromeachother.Sotheymayrunindependently.A transactionhastobehandledbytheserverasitwastheonlyonethatiscurrentlyrunning.Thismeansfor theuserthathemustneverseethechangesofotheruserswhileheisinarunningtransactionbecausethe otherchangeshavenothingtodowithhistransaction.Thisiscalledtheisolationofatransaction.Byusing differenttransactionisolationlevels(TILs)thedevelopermayprotectthedataofanSQLresultsetfrom accessbyothertransactions.Thebehaviourdescribedaboveiscalledthestandardisolationlevelknownas SERIALIZABLE.ThestandardisolationlevelofFirebirdiscalledSNAPSHOTandcomesverycloseto SERIALIZABLE.
TZConnection
TheTZConnectioncomponentisacombinationofaBDETDatabaselikecomponentacomponentthat handlesatransaction.ThiscombinationmakessensebecauseallaccesstoaFirebirddatabase(andalso otherdatabases)isalwaysmadeinarunningtransaction.SuchatransactionisstartetbytheZEOSLibrary wheneveraconnection(methodConnectofTZConnection)toadatabaseisopened.Thiscausesthateach databaseaccessisdonewithinthecontextofarunningtransaction,automatically.Thesocalled AutoCommitmodeisalwayson(setto"True").ThisisalsothestandardbehaviourofthecorrespondingBDE component.IfAutoCommitisactivatedtheneverychangeofanSQLstatementwillbeconfirmedinthe databasebyCOMMITateritssuccessfullexecution.Ifthisbehaviourshallbeturnedoffandanexplicit transactionshallbestartedthenthemethodStartTransactionhastobecalled.Withinthisexplicittransaction itispossibletoexecuteacoupleofSQLstatementsthatmakechangestothedatabase,insuccession. Thesestatementsthencanbeconfirmedasa"group"byCOMMIT.Ifanexplicittransactionisactivethen AutoCommitisalwaysturnedoff.ByCallingtheMethodCommitallthechangesmadewithinthisexplicit transactionareconfirmed.CallingthemethodRollbackresetsthesechanges.InbothcasesAutoCommitwill besettoTruewhenthemethodcall(CommitorRollback)isdone.Theexplicittransactionhasended.
calledretaining.TheCOMMITandROLLBACKcommandsareexecutedwiththeadditionRETAINING. Retainingcausestheclosingofthecurrenttransactionandimmediatelyopeninganewtransactionwithall thedataandresources(especiallytheresultset)ofthe"old"transaction. Retainingbecomesaproblemifitisusesforhugetables.Itconstrainstheinternalcleanupmechanismof firebird(garbagecollection).Thisleads(becauseoftheversioningandthemultigenerationalarchitectureof Firebird)toalotofoldrecordsthathavetobekeptbutwillnotbeneededanymore.Thisinfluencesthe server'sperformancedinanegativeway.Asocalledsweepwoulddiscardtheseoldversionsandimprove theperformance.Thissweepwillonlybeexecutedbysendinga"hard"COMMITorROLLBACK.TheZEOS Libraryonlyexecutesthese"hard"commandswhenendingthedatabaseconnection(closingconnection).It isnotpossibletosendthemwhileadatabaseconnectionisactive.Sothedatabaseconnectionshouldbe deactivatedandimmediatelyactivatedoccasionallytoachievethisperformanceimprovement.
TransactionIsolationLevelsofTZConnection TheTZConnectioncomponentprovidesfourusefulandpredefinedTransactionIsolationLevels(TIL): tiRepeatableRead ItcorrespondstotheTIL"SNAPSHOT"whichisthestandardofFirebirdservers.Itisacombinationofthe trasactionparameters"concurrency"and"nowait".Asnapshotofthecurrentdatabaseismade.Otherusers areonlyinfluenced(constrained)iftwotransactionsworkononerecordsimultaneously.Ifconflictsarise whileaccessingdataanerrormessagewillbereturned.Changeswithinothertransactionswillnotbe noticed.ThisTILcoverstherequirementoftheSQLstandard(SERIALIZABLE)widely. tiReadCommitted ItcorrespondstotheTIL"READCOMMITTED".Itisacombinationofthetransactionparameters "read_committed","rec_version"and"nowait".ThisTILrecognizesallchangesinothertransactionthathave beenconfirmedbyCOMMIT.Theparameter"rec_version"isresponsibleforthebehaviourthatthemost currentvaluesthatwerecommittedbyotheruserswillbeconsidered.theparameter"nowait"isresposiblefor thebehaviourthatthereisnowaitingforthereleaseofalockedrecord.Sotheserverismorestressedthan inTILtiRepeatableReadbecauseithastodoalltherefreshestogetthesevaluesagainandagain. tiSerializable ItcorrespondstotheTIL"SNAPSHOTTABLESTABILITY".Itisusedtogetanexclusiveaccesstotheresult set.Realizedbytransactionparameter"consistency"itpreventsthat"foreign"transactionmayaccessthe writtendata.Onlythetransactionwhichhaswrittenthedatamayaccessthem.Thispreventsalsoamulti useraccesstothewrittendata.BecausethisTILisveryrestrictivebyaccessingwrittendataitshouldbe appliedwithcautionandcare. tiNone NoTILisusedtoisolatethetransaction. TheTILtiReadUncommittedisnotsupportedbyFirebird.IfthisTILisused,anerrorwillbetriggerdedand thetransactionwillnotbeisolated(likeusingtiNone). Recommendation ItisadvisabletoisolatetransactionswithtransactionisolationleveltiRepeatableRead(theFirebirdstandard). ThisTILcoverstherequirementoftheSQLstandard(SERIALIZABLE)widely.Itpreventsallproblems concerningconsistencythatmayarisebyusingtransactions.SecondchoicewouldbetiReadCommittedbut thisdependsontheapplicationandthenecessityifthereseultsetalwayshastobecurrent.
CustomizingTILs IfyouwanttocustomizeyourTILsorexpandagivenTILthenyoucandothisbyusingtheparametersof TZConnection.ThemostimportantthingaboutthisisthattheTILthatshouldbeexpandedhastobesetin propertyIsolationLevel.IfitissettheTILparameters(seeIB/FBAPIreference)youwanttoaddmaybe addedinsourcecode.ThefollowingexampleshowstheexpansionofaTILpresettotiNone: : ZConnection.TransactIsolationLevel := tiNone; ZConnection.Properties.Add('isc_tpb_concurrency'); ZConnection.Properties.Add('isc_tpb_wait'); ZConnection.Connect; :
Protocol ThemostimportantsettinginaTZConnectionobjectistheserverprotocolitissetinpropertyProtocol.It determineswhichprotocolshallbeusedandthuswhichSQLserverwillbeaccessed.Thismethodmakes ZEOSsoflexible.Youdon'thavetoinstallspecialcomponentsforeachdatabaseyouwanttoaccessasit wasinVersions5.xandearlier.Thecomponentswillbeinstalledonce.Thisisenough.Youonlychoosethe protocolforthesupportedSQLserveryouwanttoaccessandyouaredone.Soyousetprotocol"firebird 1.5"toaccessFirebird1.5server. ReadOnlyConnection ThedatabaseconnectionmaintainedbyaTZConnectionobjectissettoreadonlybydefault(ReadOnly= True).Thismeansthatnowritingaccesstotheconnecteddatabaseisallowed.Togetwritingaccesstothe databaseyouhavetosetReadOnlytoFalse. Codepages Codepageswillbedeterminedbysettingparameter"lc_ctype"or"Codepage"inTZConnection.This parametermustbeaddedtothepropertyProperties.E.g.: ZConection.Properties.Add ('lc_ctype=ISO8859_1'); or ZConnection.Properties.Add ('Codepage=ISO8859_1');
ittoFalsebeforecompiling.IfyouthenstartthecompiledapplicationwithIDErunningyouwillgetanerror thatsaysthatthedatabasecannotbeopenedbecauseitisalreadyinuse.Soyoushouldestablishthe connectiontothedatabasewhenstartingtheapplication(e.g.inOnCreateeventofthemainform)andthen opentheneededqueriesandtables.Deactivatingtheconnectionshouldalsobedoneinmainform(e.g.in OnDestroy).Itisnotnecessarytocloseallopenqueriesandtables.Thiswillbedonewhenclosingthe connectionoftheTZConnectionobject.Ifyouusedatamodelformsyouhavetotakecarethatthedatamodel formiscreatedbeforethemainformiscreated(setintheIDE'sprojectoptions).. UsefulTZConnectionparameters AdditionalparametersforestablishingconnectionstoFirebirddatabasesare:: CreateNewDataBase: AnewdatabasewillbecreatedbasedonthespecifiedCREATEDATABASEstatements.Whenthedatabase iscreatedtheconnectionwillbeestablishedimmediately.AllthishappensbycallingtheConnectmethodof TZConnection. : ZConnection1.Database := 'd:\db1.fdb'; ZConnection1.Protocol := 'firebird-1.5'; ZConnection1.Properties.Add ('CreateNewDatabase=CREATE DATABASE ' + QuotedStr ('d:\db1.fdb') + ' USER ' + QuotedStr ('sysdba') + ' PASSWORD ' + QuotedStr ('masterkey') + ' PAGE_SIZE 4096 DEFAULT CHARACTER SET ISO8859_1'); ZConnection1.Connect; :
TZQuery
TheusageofTZQueryissimilartotheusageofBDE'sTQuerycomponent. Recommandation:RequestLiveandTZUpdateSQL IfanSQLdatasetshallbeupdatablethenRequestLivehastobesettotrueandyoushouldgenerallyuse accordingupdateSQLstatementsthatwillbedefinedinTZUpdateSQL.Ifthisisdonejustassign TZUpdateSQLtotheTZQueryobject.Nowallchangesthatwillbemadeintheresultsetwillbedonetothe databasebyusingthedefinedstatementsofTZUpdateSQL.AccordingtoexperienceRequestLivemode runsmoresmoothlybyusingTZUpdateSQL.
TZReadOnlyQuery
ThisisaQuerycomponentthatisquitesimilartotheTZQuerycomponent.Thereisjustonedifference:The resultsetisreadonly.ThereisnopossibilitytoassignaTZUpdateSQLobject.
TZUpdateSQL
ATZUpdateSQLobjectprovidesstatementstomodifythedataofaresultsetthatisretrievedbyaTZQuery object.TheTZUpdateSQLcomponentiscomparabletoBDE'sTUpdateSQLcomponent.Hereisanexample howtodefinethestatementsofanSQLstatementwithcorrespondingupdatestatements(basedonadialect 3database): SQL.Sql: SELECT * FROM names UpdateSQL.InsertSql: INSERT INTO names (recno, name) VALUES (:recno, :name) UpdateSQL.ModifySql: UPDATE names SET recno = :RecNo, name = :name WHERE recno = :old_recno UpdateSQL.DeleteSql: DELETE FROM names WHERE recno = :old_recno
MultiplestatementsinTZQueryandTZUpdateSQL ThecomponentsTZQueryandTZUpdateSqlprovidethepossibilitytoexecutemultiplestatements,internally. SoitispossibletoplacemultipleSQLstatements(evenwithparameters)forexecutioninSQLproperty. Theyonlyhavetobeseparatedbysemicolon.Hereanexample: : With Query do Begin Sql.Clear; Sql.Add('DELETE FROM table1;'); Sql.Add('INSERT INTO table1 VALUES (:Val1, :Val2);'); Sql.Add('INSERT INTO table2 VALUES (:Val3, :Val2);'); Sql.Add('UPDATE table3 SET field1 = :Val4;'); Params.ParamByName('Val1').AsInteger := 123; : ExecSql; End; :
Thestatementswillbeexecutedingivenorder.Itisalsopossibletoexecutemultiplestatementsiftheyare groupedinthismannerinsidemultipleTZUpdateSqlObjectsinordertoupdatemultipletables.
TZTable
TZTableactslikeBDE'sTTable.AsaprincipleyouonlyshoulduseTZTableinaC/Sapplicationifyouhave verysmalltablesbecauseallrecordsofthetablewillbetransferredfromserverintoclient'smemoryby openingtheTZTable.Thisisabahavioursimilartoa"SELECT*FROMXYZ"statement.Youshouldeven preventastatementlikethisinaC/Sapplication.Theintensionistokeeptheresultsetthathastobe transferredfromservertoclientassmallaspossible(perferablyonleonerecord).
TZStoredProc
TZStoredProcprovidesthepossiblitytoexecutestoredproceduresthataresavedinadatabase.Thereare twokindsofstoredprocedures:Proceduresthatreturnaresultsetandproceduresthatdonotreturna resultset.TZStoredProcworkssimilartoBDE'sTStoredProc.Theonlydifferencebetweenthemisthatyou don'thavetocallPreparebeforeyoucalltheExecProcmethod. StoredProcedureswithResultsets IfastoredprocedurereturnsaresultsetthenitwillbeactivatedbycallingtheOpenmethod(whenall existingparametershavegottheirvalues): : With spSumByName do Begin Close; ParamByName ('Name').Value := 'DontKnowHow'; Open; End; :
TheresultsetcanbeworkedonlikearesultsetofaTZQuery.
StoredProcedureswithoutResultsets IfastoredprocedurehasnoresultsetthenitwillbeexecutedbycallingtheExecProcmethod(whenall existingparametershavegottheirvalues).Hereisanexample(conConnection.AutoCommit=True): : With spDeleteByName do Begin ParamByName ('Name').Value := 'DontKnowHow'; conConnection.StartTransaction Try // execute StoredProc ExecProc; Except conConnection.Rollback; End; conConnection.Commit; End; :
Attention :BuginZEOSLibraryV6.1.5! IfastoredprocedurewasexecutedviaExecProcatleastonetime,anexceptionwillbetriggeredwhen disconnectingTZConnection(usedbyTZStoredProc)fromdatabase.Exception'sMessageis:"invalid statementhandle".SQLerrornumberis901.ThisExceptionwillnotbetriggeredifthestoredprocedureis executedusingaTZQueryorTZReadOnlyQuery.Executionofthestoredprocedurewillbedonebycalling theprocedureusingthefollowingSQLcommand(putintotheSQLproperty): [syntax="sql"]EXECUTEPROCEDUREDeleteByName(:Name)[/color][/font][/list] ExecutingastoredprocedurethiswayissimilartoexecutioninTZStoredProc(conConnection.AutoCommit= True): : // using a TZQuery object With qryDeleteByName do Begin ParamByName ('Name').Value := 'DontKnowHow'; conConnection.StartTransaction Try ExecSQL; // execute SQL statement in TZQuery Except conConnection.Rollback; End; conConnection.Commit; End; : Info:Evenwithadialect3databaseitisnotneededtoputthenameofthestoredprocedureintoquotation marksNamesofstoredproceduresinFirebirdarenotcasesensitive. ProblemswithTZStoredProcwhenusingdialect1databases Theproblemwhenusingdialect1databasesisthatthemetadataofstoredproecedureisnotproperly transferredtotheTZStoredProcobjectswhenchoosingthestoredprocedureinobjectinspector.All parapeterdataisnotproperlyinterpreted.Anupdatefromdialect1todialect3solvesthisproblem.Isthe dialect1databasevitalfortheapplicationsystemthenaTZQueryorTZReadOnlyQueryhastobeusedas workaround.
TZSQLProcessor
ThiscomponentprovidesthefunctiontoprocessSQLscriptsthatcanbeloadedbycallingthemethods LoadFromStream()orLoadFromFile().TheloadedSQLScriptisputintoanaccordingpropertycalledScript. Importanisthatthecorrectdelimiterforthescriptisset(PropertyisalsoDelimiter).Bydefault";"willbeset asdelimiter.Thisissufficientforthemostscripts.Butifyouwanttocreatestoredproceduresortriggersvia scriptyoushouldsetdelimiteraccordingtothesettingofthescript's"SETTERM>newDelimiter< >oldDelimiter<"command(normally"^"isusedforthis).InadditiontothisthepropertyDelimiterTypehasto besettodtSetTerm.HeresomelinesofcodethatshowhowtoprocessanSQLscript: : sqlScript.Script.Clear; sqlScript.LoadFromFile('c:\temp\createdb.sql'); conConnection.StartTransaction; Try sqlScript.Execute; Except conConnection.Rollback; End; conConnection.Commit; : TheSQLscriptisprocessedwithinanexplicittransaktion(AutoCommitisturnedon).Ifexecutionsucceeds thechangeswillbecommittedotherwisetheywillberolledback.
TZSQLMonitor
UsingtheTZSQLMonitorcomponentyoumaylogcertainactionsoreventsoftheZEOSdatabase components.ThejournalmaybewrittenasfileorcollectedinaTMemoobjectorsomethinglikethat. Writingtheactionsoreventstoalogfileonlyneedsafewsettings: : sqlMonitor.FileName := 'C:\Log\MyAppLog.log'; sqlMonitor.Active := True; sqlMonitor.AutoSave := True; : TocollecttheloggedactionsoreventsinaTMemoobjectyouhavetoimplementtheOnLogTraceeventas follows: Procedure Tfrm_MyApp.sqlMonitorLogTrace (Sender: TObject; Event: TZLoggingEvent); Begin If Trim (Event.Error) > '' Then memMontor.Lines.Add (DateTimeToStr (Event.Timestamp) + ': ' + Event.Message + #13#10 + ' Error: ' + Event.Error) Else memMontor.Lines.Add (DateTimeToStr (Event.Timestamp) + ': ' + Event.Message); End; // sqlMonitorLogTrace TheOnLogTraceeventisalwaystriggeredwhenanactionoreventwasloggedbysqlMonitor.LogEvent (oEvent).TheoEventparameterstandsforaninstanceofaTZLoggingEventclassobject.
TZSQLMetadata
WiththisspecialTDataSetcomponentitispossibletoaccessthemetadataofadatabaseliketables, columns,etc.(Thischapterisstilltobeexpanded!)
TZIBEventAlerter(AddOnonlyforFirebirdandInterbase)
ByusingthiscomponentyouareabletointercepteventstriggeredbystoredproceduresofaFirebird databaseandreacttothem.Youonlyhavetoregistertheevent'stext(string)youwanttoreacttoinproperty Eventswhichisastringlist.Youcandothisusingobjectinspectororinsidethesourcecode.Theeventalerter isactivatedbyregisteringthelistedevents.TodothisyoushouldcalltheRegistereventsmethodbecause thePropertiesAutoRegisterandRegistereddonotworkproperly.Ifyouoncehaveregisteredtheeventsthe componentisabletoreacttothem.Alleventsareunregistered(deleted)bycallingmethodUnregisterEvents andtheeventalertetisturnedoff. Registrationofeventsand"activation"oftheeventalerter: : EventAlerter.Events.Add ('Minimum Stock Level Reached'); EventAlerter.Events.Add ('Credit Limit Exceeded'); EventAlerter.RegisterEvents; :
Master/DetailwithZEOSLibrary
ZEOSDataSetcomponentscomewithtwokindsofmaster/detailconnections:thosewithaserversidedfilter andthosewithaclientsidedfilter.BothkindsandonekindinadditiointhatisindependentfromZEOS(and thuswithoutanycomfort)willbedescribedhere. Note:Ifwetalkabout"master"or"detail"thenaTDataSetdescendant(TZQuery/TZReadOnlyQuery, TZTableoraTZStoredProc)ismeantthataccesseseitheramasterresultsetoradetailresultsetofa master/detailconnection.
Thisisanexampleforasimplemaster/detailqueries.Requirement:WeuseTZQueryorTZReadOnlyQuery toestablishthemaster/detailconnection: MasterSQL: SELECT id, feld1, feld2, feld3 FROM master [*]DetailSQL: SELECT feld1, feld2, master_id FROM detail WHERE master_id = :id Parameter:idstandsforthecontentofmaster's"id"field(istheprimarykey)becausethemaster's DataSourceisassignedtothepropertyDataSourceofthedetail.Sothisparameterreferencestothe"id" fieldofthemaster.Therield"master_id"indetailqueryistheforeignkeyfieldofthedetailtablewhich referencestheprimarykeyofthemaster. Ifthecursorofthemasterchangesitspositionwhileserversidedfiltersareused,theSQLstatementofthe detailisexecutedusingthecurrentkeyvalues.Sotheresultsetofthedetailisautomaticallyrefreshed. Master/Detailwithclientsidedfilters ThisisthedefaultbehaviourofaBDETTablecomponent.Hereamaster/detailconnectionbetweentwo DataSetsisestablishedasfollows: TheDataSourceofthemasterisassignedtothepropertyMasterDataSourceofthedetail. TheprimarykeyfieldsofthemasterareassignedtopropertyMasterFieldofthedetail. Theforeignkeyofthedetailwhichreferencestheprimarykeyofthemasterisassignedtopropety IndexFieldNames.
Thisisanexampleforasimplemaster/detailqueries.Requirement:WeuseTZQueryorTZReadOnlyQuery toestablishthemaster/detailconnection: MasterSQL: SELECT id, feld1, feld2, feld3 FROM master DetailSQL: SELECT feld1, feld2, master_id FROM detail WHERE master_id = :id Parameter:idstandsforthecontentofmaster's"id"field(istheprimarykey)becausethemaster's DataSourceisassignedtothepropertyDataSourceofthedetail.Sothisparameterreferencestothe"id"
fieldofthemaster.Therield"master_id"indetailqueryistheforeignkeyfieldofthedetailtablewhich referencestheprimarykeyofthemaster. Ifthecursorofthemasterchangesitspositionwhileserversidedfiltersareused,theSQLstatementofthe detailisexecutedusingthecurrentkeyvalues.Sotheresultsetofthedetailisautomaticallyrefreshed. Master/Detailwithclientsidedfilters ThisisthedefaultbehaviourofaBDETTablecomponent.Hereamaster/detailconnectionbetweentwo DataSetsisestablishedasfollows: TheDataSourceofthemasterisassignedtothepropertyMasterDataSourceofthedetail. TheprimarykeyfieldsofthemasterareassignedtopropertyMasterFieldofthedetail. Theforeignkeyofthedetailwhichreferencestheprimarykeyofthemasterisassignedtopropety IndexFieldNames.
WithclientsidedfiltersbothDataSetsfirsttransferalltablerowsfromservertoclient.Thedetailthensetsa filter(onclientside)togetthedetailsaccordingtothecurrentmasterrecord. Incaseofcreatinganewdetailrecordforamaster/detailconnectionwithaclientsidedfilterthereisakindof automatism:TheForeignkeyfieldsofthedetail(setinpropertyIndexFieldNamesofthedetail)willbefilled automaticallywiththeaccording(current)primarykeydataofthemaster(setinpropteryMasterFieldofthe detail).Note:Withserversidedfiltersyouhavetocareaboutthisfunctionality,manuallyinyourprogram's code.ThiscanbeachievedbyimplementingtheOnNewRecordeventofthedetail.Thiseventisalways triggeredwhenannewrecordistobecreated(see:DelphionlinehelpforTDataSet).AccordingtotheSQL statements,definedaboveyouonlyhavetoimplementthefollowing: Procedure dmMasterDetail.qryDetailNewRecord (DataSet: TDataSet); Begin qryDetailMASTER_ID.Value := qryMasterID.Value; End; CorrespondingTFieldswerecreatedforthefields"master_id"ofthedetailand"id"ofthemasterusingthe fieldeditor. Formaster/detailconnectionsinZEOSthereisanadditionaloptionthatissetinpropertyOptions:Itis doAlwaysResyncDetail.Ifthisoptionissetthentheresultsetofthedetailisonlyrefreshedwhenpostis calledorarecordchanges(bothwithinmasterDataSet). Master/Detail"byhand" Normallyyouimplementamaster/detailconnectionaccordingtothemethodusedbyserversidedfilters.The SQLstatementsforthislookexactlylikethat.Onlythepropertiesthataresetinmasteranddetail(see above)willnotbesethere.BothTZQuerisareworkingindependently.Thismeans:ThedetailDataSetdoes notrecognizeanychangesinmasterDataSet.Itssynchronzationhastobeimplemented,manually.Thiswill bedoneintheOnChangeeventofthemaster.OnChangeistriggeredwhenchangingtoanewrecordor fielddatahasbeenchanged(see:DelphionlinehelpforTDataSource).Synchronizationofthedetail (accordingtotheexampleabove)wouldbeimplementedlikethis: Procedure dmMasterDetail.dsMasterDataChange ( Sender: TObject; Field: TField); Begin With qryDetail do Begin Close; ParamByName('id').Value := qryMasterID.Value; Open; End; End;
CachedUpdates
ThedevelopersoftheZEOSLibraryareintendedtoimplementthefunctionalityoftheBDEcomponentsas goodaspossible.ThisiswhythereisalsothepossibilityofcachedupdateswithZEOS.Youonlyhavetoset thepropertyCachedUpdatesofaDataSetdescendant(TZTable,TZQueryoderTZStoredProc)totrue.From thistimeonallchangesintheresultsetwillbecachedandtheycanbeeasilycommittedtothedatabaseby callingApplyUpdatesandCommitUpdatesoneaftertheothereitherautomaticallyinyourprogramcodeor triggeredmanuallybyauser.CancelUpdatescausesthatthecangeswillnotbecommittedtothedatabase. Inthiscaseallcachedchangeswillbereset(likeusingaROLLBACK).Hereyouwillfindalittlecodesnippet thattriestoshowyouhowcachedupdatesareimplemented(don'targueaboutthesenseinthis...). AutoCommitmodeofTZConnectinisturnedon(True): : With DataSet do Begin bEverythingOK := True; CachedUpdates := True; DataSet.First; While (not DataSet.EOF) and (bEverythingOK) do Begin DataSet.Edit; : // process record : DataSet.Post; DataSet.Next; : bEverythingOK := AFunctionForValidation; End; If bEverythingOK Then Begin ApplyUpdates; CommitUpdates; End Else CancelUpdates; CachedUpdates := False; End; :
BLOBFields
AccordingtoBDE'scomponentsthecomponentsoftheZEOSLibraryarecapableofhandlingBLOBfields. HereisanexamplehowanewrecordwithaBLOBfieldiscreated.TheBLOBfieldisfilledwithabitmap.To achievethiswehavetouseaStream: : Var TheStream : TMemoryStream; Begin TheStream := TMemoryStream.Create; Try Image1.Picture.Bitmap.Savetostream(TheStream); With qryBlobInsert do Begin Sql.Text := 'INSERT INTO EVENTS (EventNo,EVENT_PHOTO) ' + 'VALUES (100,:ThePicture)'; Params.Clear; Params.CreateParam(ftBlob,'ThePicture', ptInput); ParamByName('ThePicture').LoadfromStream(TheStream,ftBlob); ExecSQL; End; Finally TheStream.Free; End; End; : Thissectionaboutblobfieldsisnotcompleted,yet.PleasefeelfreetosendmeaprivatemessageoreMailif youhaveanyissuesaboutblobsinordertoexpandthissection.
Sampleproject:"EasyQuery"
TopreventbeingtootheoreticalnowwewillcreateasmallsampleprojectthodemonstratehowtheZEOS componentsareusedingeneral.Wewillcreateasmallapplicationthataccessesthetables"Customer"and "Country"oftheFirebirdsampledatbase"Employee".Youshouldbeabletonavigateintable"Customer" andedititsdata.AndnotbeintooboringwewillimplementaDBLookupComboboxforfield"Country"intable "Customer".Thisfieldisaforeignkeythatreferencestofield"Country"intable"Country". Solet'sgetstarted! FirstofallwehavetocreateanewprojectinDelphi.Additionalytothedefaultformwehavetocreatea DataModule. ThefollowingpropertiesoftheDataModulehavetobeset: Name:dmEasyQuery ComponentsfortheDataModule
Note:YouhavetocreatepersistentTFieldsforalltablefieldsusingthefieldeditor! TheOnAfterPosteventofqryCustomerwillbeimplementedlikethis: procedure TdmEasyQuery.qryCustomerAfterPost(DataSet: TDataSet); begin // Refresh of resultset to actualize (sort) data shown in DBGrid. qryCustomer.Refresh; end;
NowthecreatedDataModulewillbesavedasdm_EasyQuery.pas.[/list] Componentsfortheform
Themainformwillhavethefollowingcomponentsandisinitializedasfollows: Properties Caption:EasyQueryDemo Name:frmEasyQuery IntheUnit'sinterfaceyouhavetoadddm_EasyQuerytotheusesclausetogetaccesstothe databasecomponents. Thefollowingeventsofthemainformhavetobeimplemented:: OnCreate Whencreatingtheformtheconnectiontothedatabasewillbeestablished.Afterconnectingtothe databasethequerieswillbeopened: procedure TForm1.FormCreate(Sender: TObject); begin dmEasyQuery.conEmployee.Connect; dmEasyQuery.qryCustomer.Open; dmEasyQuery.roqryCountry.Open; end; OnDestroy Whenclosingtheapplication(destroyingthemainform)thedatabaseconnectionwillbecut.All querieswillbeclosedautomaticallybeforedisconnecting. procedure TForm1.FormDestroy(Sender: TObject); begin dmEasyQuery.conEmployee.Disconnect; end; TLabel Caption:CUSTOMER
TDBEdit (5x) TLabel] (5x) TheseobjectswillbecreatedbyusingthecolumneditorofqryCustomer:Selectcolumns ADDRESS_LINE1,ADDRESS_LINE2,CITY,STATE_PROVINCEandPOSTAL_CODEanddragand dropthemontothenmainform.Alignthemandadaptthemtothelayoutyouseeinthescreenshot. TLabel Caption:COUNTRY TDBLookUpComboBox DataField:COUNTRY DataSource:dmEasyQuery.dsCustomer KeyField:COUNTRY ListField:COUNTRY ListSource:dmEasyQuery.dsCountry TDBNavigator DataSource:dmEasyQuery.dsCustomer
Nowthecreatedformwillbesavedasfrm_EasyQuery.pas.
AdditionalExamples
FishFact ThisisthemostpopulardatabasedemoforDelphi.ItwasmigratedtouseZEOScomponents. Transactions Asampleapplicationconcerning"transactionswithZEOS".Itusesasmallselfmadetestdatabase. StoredProc Thisisasampleapplikationthatshowshowtousestoredproecedures.ThedatabaseistheFirebird Employeesampledatabase. MasterDetail Asmallapplicationthatshowshowmaster/detailconnections(serverandclientsided)willbeimplemented. EventDemo AlsoaDelphidatabasesamplethatwasmigratedfromIBXtoZEOS.ItusesthecomponentTIBEventAlerter. ThisapplicationneedsthedatabaseEvents(shippedwithDelphi)thathadtobemigratedtodialect3toget thissamplerunning. YouwillfindtheseExamplesherefordownload. AdownloadablePDFVersionofthisTutorialisalsoonitsway!
MichaelSeeger ZeosLibDevelopmentTeam