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
