Az előző bejegyzésben egy univerzálisan használható, adatbázis kapcsolatot kezelő osztályról volt szó. A gondolatmenetet folytatva és kiegészítve egy újabb, az adatbázis adatainak lekérdezését támogató osztályt készítünk.
A Recordset
A recordset objektum az adatbázis rekordjainak egy csoportját tartalmazza, ez lehet egy tábla az adatbázisból, vagy lehet egy (akár komplex) SQL lekérdezés eredménye. Ezek kezeléséhez szintén hozzunk létre egy osztályt AdoRecordset néven. Ez az osztály lesz felelős
- a recordset kapcsolat létrehozásáért,
- a lekérdezés végrehajtásáért,
- a kapcsolat automatikus lezárásáért és
- a hibák riportálásáért.
Mezők
Négy változónk lesz:
- Connection – egy aktív adatbázis kapcsolat szükséges a lekérdezéshez;
- Recordset – ez az ADO ‘Recordset’ típusú objektuma;
- Error – boolean típusú változó, jelzi, ha hiba történt végrehajtáskor;
- LastError – hiba esetén ez tartalmazza szövegként a hiba leírását.
A mezőket itt is privátként definiáljuk és kívülről tulajdonságokon keresztül érhetők el. A kódrészlet:
Private Type TAdoRecordset Connection As ADODB.Connection Recordset As ADODB.Recordset LastError As String Error As Boolean End Type Private this As TAdoRecordset Public Property Get Self() As AdoRecordset Set Self = Me End Property Public Property Get Connection() As ADODB.Connection Set Connection = this.Connection End Property Public Property Set Connection(ByRef obj As ADODB.Connection) Set this.Connection = obj End Property Public Property Get Recordset() As ADODB.Recordset Set Recordset = this.Recordset End Property Public Property Set Recordset(ByVal obj As ADODB.Recordset) Set this.Recordset = obj End Property Public Property Get LastError() As String LastError = this.LastError End Property Public Property Let LastError(ByVal Value As String) this.LastError = Value End Property Public Property Get Error() As Boolean Error = this.Error End Property Public Property Let Error(ByVal Value As Boolean) this.Error = Value End Property
Eljárások
Egyelőre négy eljárást hozunk létre:
- CreateFromSQL – ez egy parametrizált konstruktor eljárás lesz (magyarázatot lásd később), ezzel tudunk egy tetszőleges, SQL nyelven írt lekérdezést futtatni;
- CreateFromTable – szintén egy parametrizált konstruktor eljárás, ezzel tudunk egy táblát (egy az egyben) Recordset-ként megnyitni;
- CreateRecordset – ez nyitja meg ténylegesen a Recordset kapcsolatot a konstruktor eljárásoktól kapott paraméterek alapján, illetve ez az eljárás tartalmazni fog egy, a korábbihoz hasonló hibakezelő részt;
- CloseAdoRecordset – ezzel tudjuk a recordset kapcsolatot lezárni, valamint a Connection objektumra való hivatkozást megszüntetni.
Ezeken kívül szükségünk lesz az osztály destruktor (Class_Terminate) eljárására.
Először nézzük a Recordset kapcsolat létrehozásáért felelős eljárást:
Private Sub CreateRecordset(ByRef rs As ADODB.Recordset, _ ByVal CommandType As ADODB.CommandTypeEnum, _ ByVal Source As String, _ ByRef Error_ As Boolean, _ ByRef LastError_ As String) On Error GoTo ADOError Error_ = False 'kapcsolat létrehozása With rs .Source = Source Select Case CommandType Case ADODB.adCmdText .CursorType = adOpenStatic .CursorLocation = adUseClient .LockType = adLockOptimistic Case ADODB.adCmdTable .CursorType = adOpenStatic .CursorLocation = adUseClient .LockType = adLockOptimistic End Select .Open Options:=CommandType End With 'kapcsolat ellenőrzése If Not rs.State = adStateOpen Then Error_ = True LastError_ = "A Recordset létrehozása nem sikerült" Else Debug.Print "A Recordset létrejött" ' teszteléshez End If CleanExit: Exit Sub ADOError: Error_ = True Dim strErr As String ' VB által generált hibaüzenet strErr = Application.WorksheetFunction.Concat( _ strErr, vbCrLf, _ "VB Error # ", Str(Err.Number), vbCrLf, _ " Generated by ", Err.Source, vbCrLf, _ " Description ", Err.Description _ ) ' ADO API által generált hibaüzenetek Dim AdoErrors As ADODB.Errors Set AdoErrors = this.Connection.Errors Dim ADOError As ADODB.Error For Each ADOError In AdoErrors With ADOError strErr = Application.WorksheetFunction.Concat( _ strErr, vbCrLf, _ " ADO Error #", .Number, vbCrLf, _ " Description ", .Description, vbCrLf, _ " Source ", .Source _ ) End With Next ADOError LastError_ = strErr Err.Clear Resume CleanExit End Sub
Az eljárás paraméterei:
- rs: inicializált, de még üres (nem megnyitott) Recordset objektum;
- CommandType: meghatározza, hogy milyen típusú lekérdezést futtatunk. Az eljárás által kezelt értékek lehetnek adCmdText ha SQL parancsot hajtunk végre vagy adCmdTable ha egy táblát olvasunk be egy az egyben a tábla neve alapján;
- Source: ez a String típusú paraméter tartalmazza a lekérdezés típusa alapján vagy az SQL utasítást vagy a tábla nevét;
- Error_ és LastError_: azonos tartalmú változók, mint amik az osztály mezői között is szerepelnek hasonló névvel.
Az eljárás első felében a kapott paraméterek alapján beállítjuk, majd megnyitjuk a Recordsetet. A Select Case blokkban lehetőségünk van a CommandType alapján különböző Cursor és Lock tulajdonságok beállítására (jelenleg azonos beállítások szerepelnek a kódban). Az eljárás második fele pedig a hibakezelő utasítássor, ami majdnemmegegyezik azzal, mint amit az AdoConnection osztályban használtunk.
Mielőtt a konstruktor eljárások részleteibe belemennénk, tegyünk egy kitérőt.
A VBA osztálymodul saját konstruktor eljárása (Class_Initialize) nem ad lehetőséget paraméterek megadására. Tehát például ez nem működik:
Private Sub Class_Initialize (ByVal ValamiParameter As String)
Egy kis trükközéssel azonban tudunk parametrizált konstruktor eljárást is készíteni. Az ilyen konstruktorral rendelkező osztály nagyon leegyszerűsített váza (mezők/tulajdonságok és egyéb eljárások nélkül) így néz ki:
Public Property Get Self() As OsztalyNeve Set Self = Me End Property Public Function Create (ByVal ValamiParameter As String) As OsztalyNeve With New OsztalyNeve '[...] műveletek a paraméterrel Set Create = .Self End With End Function
Ahhoz, hogy a dolog működjön, még szükséges egy pár lépésből álló trükk:
- (A munkafüzet mentése után) töröljük az osztályt:
a Project Explorerben jobb klikk az osztályon > Remove OsztalyNeve….
A felugró ablakban válasszuk ki, hogy Igen, ki akarjuk exportálni az osztályt; - A kiexportált .cls kiterjesztésű fájlt nyissuk meg egy szerkesztővel (pl. Notepad);
- Az eleje felé keressük meg a következő sort:
Attribute VB_PredeclaredId = False
és írjuk át True-ra:
Attribute VB_PredeclaredId = True - Mentsük el a fájlt
- Importáljuk vissza az osztályt az munkafüzetbe:
jobb klikk a Project Explorer-ben > Import File…
A kitérő után lássuk a konstruktor eljárásokat. Mivel parametrizált konstruktor eljárások lesznek, át kell először állítani az osztálymodul PredeclaredId értékét igazra, az előző bekezdésben leírtak alapján.
A két eljárás nagyon hasonló, néhány változóban lesz csak eltérés. Paraméterként mindkettőnél meg kell adni a hivatkozást egy élő ADODB kapcsolatra, valamint egy SQL utasítást vagy egy adatbázis tábla nevét.
A kettő nagyon hasonló, néhány változóban lesz csak eltérés. Paraméterként mindkettőnél meg kell adni a hivatkozást egy élő ADODB kapcsolatra, valamint egy SQL utasítást vagy egy adatbázis tábla nevét.
Public Function CreateFromSQL(ByRef Connection As ADODB.Connection, _ ByVal SQL_String As String) Dim Error_ As Boolean Dim LastError_ As String With New AdoRecordset If Not Connection Is Nothing Then If Connection.State = adStateOpen Then Set .Connection = Connection Set .Recordset = New ADODB.Recordset .Recordset.ActiveConnection = Connection If Not SQL_String = vbNullString Then CreateRecordset .Recordset, SQL_String, _ ADODB.adCmdText, Error_, LastError_ .Error = Error_ .LastError = LastError_ Else .Error = True: .LastError = "Nincs SQL utasítás megadva" End If Else .Error = True: .LastError = "A kapcsolat nincs megnyitva" End If Else .Error = True: .LastError = "Nincs kapcsolat" End If Set CreateFromSQL = .Self End With End Function Public Function CreateFromTable(ByRef Connection As ADODB.Connection, _ ByVal TableName As String) As AdoRecordset Dim Error_ As Boolean Dim LastError_ As String With New AdoRecordset If Not Connection Is Nothing Then If Connection.State = adStateOpen Then Set .Connection = Connection Set .Recordset = New ADODB.Recordset .Recordset.ActiveConnection = Connection If Not TableName = vbNullString Then CreateRecordset .Recordset, TableName, _ ADODB.adCmdTable, Error_, LastError_ .Error = Error_ .LastError = LastError_ Else .Error = True: .LastError = "Nincs tábla név megadva" End If Else .Error = True: .LastError = "A kapcsolat nincs megnyitva" End If Else .Error = True: .LastError = "Nincs kapcsolat" End If Set CreateFromTable = .Self End With End Function
A fenti eljárások hasonlóan épülnek fel: az elején végzünk néhány ellenőrzést: van-e élő adatbázis kapcsolat, van-e paraméterként SQL utasítás vagy tábla név megadva. Ezután a CreateRecordset eljárás meghívásával ténylegesen létrehozzuk az adatállományt. Ha menet közben bármilyen hiba felmerül, akkor az látszani fog az osztály Error és LastError mezők értékében.
A kapcsolat lezárását kezelő eljárás így néz ki:
Private Sub CloseAdoRecordset() If Not this.Recordset Is Nothing Then If this.Recordset.State = adStateOpen Then this.Recordset.Close Debug.Print "A Recordsetet lezártuk" ' teszteléshez Else Debug.Print "Nincs lezárandó recordset" ' teszteléshez End If Set this.Recordset = Nothing End Sub
Az ezt meghívó destruktor eljárás pedig így:
Private Sub Class_Terminate() CloseAdoRecordset End Sub
Az osztály készen van, ezt is teszteljük, méghozzá az 1. részben írt teszt eljárás tovább bővítésével.
Public Sub KapcsolatTeszt() Dim conn As AdoConnection Set conn = New AdoConnection If Not conn.Error Then Dim SQL_String As String SQL_String = "SELECT * FROM TablaNeve" Dim rs As AdoRecordset Set rs = AdoRecordset.CreateFromSQL(conn.Connection, SQL_String) If Not rs.Error Then Debug.Print "Nem történt hiba" Else Debug.Print rs.LastError End If Set rs = AdoRecordset.CreateFromTable(conn.Connection, "TablaNeve") If Not rs.Error Then Debug.Print "Nem történt hiba" Else Debug.Print rs.LastError End If Set rs = Nothing Else Debug.Print conn.LastError End If Set conn = Nothing End Sub
Ha minden rendben ment, a következőket kell az azonnali ablakban látnunk:
A kapcsolat létrejött A Recordset létrejött Nem történt hiba A Recordset létrejött A Recordsetet lezártuk Nem történt hiba A Recordsetet lezártuk A kapcsolatot lezártuk