Kompatibilni sloj za migraciju sa BDE na FireDAC bez menjanja postojećeg koda
- Pregled
- Problem
- Rešenje
- Instalacija
- Kako radi
- Primeri upotrebe
- Migracija postojećeg koda
- Testiranje
- Ograničenja
BDE-FireDAC Bridge omogućava da postojeći BDE kod nastavi da radi bez izmena, ali koristi FireDAC kao backend. Ovo je idealno za:
- Velike legacy aplikacije (300+ formi, 1000+ procedura)
- Knjigovodstvene i poslovne sisteme
- Situacije gde je potpuna migracija prevelik rizik
- Potrebu za podrškom novijih verzija Firebird-a
// STARI KOD (BDE)
var
Database1: TDatabase;
Query1: TQuery;
begin
Query1.SQL.Text := 'SELECT * FROM CUSTOMERS';
Query1.Open;
while not Query1.Eof do
begin
ShowMessage(Query1.FieldByName('NAME').AsString);
Query1.Next;
end;
end;
// NOVI KOD (FireDAC) - KOMPLETNA ZAMENA!
var
FDConnection1: TFDConnection;
FDQuery1: TFDQuery;
begin
FDQuery1.SQL.Text := 'SELECT * FROM CUSTOMERS';
FDQuery1.Open;
while not FDQuery1.Eof do
begin
ShowMessage(FDQuery1.FieldByName('NAME').AsString);
FDQuery1.Next;
end;
end;Problem: Za 300 formi i hiljade procedura, ovo znači:
- Meseci ručnog rada
- Visok rizik uvođenja bagova
- Testiranje svakog ekrana i funkcije
- Izmena DFM fajlova
// ISTI KOD - samo uses i deklaracija
uses
BDEFireDACBridge; // <-- Dodati uses
var
Database1: TBDEFireDACConnection; // <-- TDatabase -> TBDEFireDACConnection
Query1: TBDEFireDACQuery; // <-- TQuery -> TBDEFireDACQuery
begin
// SVE OSTALO JE POTPUNO ISTO!
Query1.SQL.Text := 'SELECT * FROM CUSTOMERS';
Query1.Open;
while not Query1.Eof do
begin
ShowMessage(Query1.FieldByName('NAME').AsString);
Query1.Next;
end;
end;Prednosti:
- Logika ostaje potpuno ista
- Samo deklaracije tipova se menjaju
- Moguća masovna zamena stringova
- Postepena migracija po modulima
- Stari i novi kod mogu koegzistirati
uses
BDEFireDACBridge;Stari BDE tipovi → Novi wrapper tipovi
| BDE | Wrapper |
|---|---|
TDatabase |
TBDEFireDACConnection |
TQuery |
TBDEFireDACQuery |
TStoredProc |
TBDEFireDACStoredProc |
TTable |
TBDEFireDACTable |
U Delphi IDE:
- Component → Install Packages → FireDAC
┌─────────────────┐
│ BDE KOD │ (Aplikacija ne zna za promenu)
│ Query1.Open │
└────────┬────────┘
│
↓
┌─────────────────────────┐
│ BDEFireDACBridge │ (Kompatibilni sloj)
│ TBDEFireDACQuery │
│ ↓ │
│ FFDQuery: TFDQuery │ (Interna FireDAC komponenta)
└────────┬────────────────┘
│
↓
┌─────────────────┐
│ FireDAC │
│ Firebird DB │
└─────────────────┘type
TBDEFireDACQuery = class(TDataSet) // Nasleđuje TDataSet kao BDE
private
FFDQuery: TFDQuery; // Interno koristi FireDAC
public
procedure Open; // BDE interfejs
procedure ExecSQL; // BDE interfejs
end;
procedure TBDEFireDACQuery.Open;
begin
FFDQuery.Open; // Poziva FireDAC
end;Rezultat: Aplikacija poziva BDE metode, wrapper ih prosleđuje FireDAC-u.
var
Conn: TBDEFireDACConnection;
Query: TBDEFireDACQuery;
begin
Conn := TBDEFireDACConnection.Create(nil);
Query := TBDEFireDACQuery.Create(nil);
try
// Konekcija
Conn.DatabaseName := 'C:\Data\MYDB.FDB';
Conn.Params.Add('User_Name=SYSDBA');
Conn.Params.Add('Password=masterkey');
Conn.Connected := True;
// Query - ISTO KAO BDE!
Query.DatabaseConnection := Conn;
Query.SQL.Text := 'SELECT * FROM CUSTOMERS WHERE CITY = :CITY';
Query.Params.ParamByName('CITY').AsString := 'Beograd';
Query.Open;
// Obrada podataka
while not Query.Eof do
begin
WriteLn(Query.FieldByName('NAME').AsString);
Query.Next;
end;
Query.Close;
finally
Query.Free;
Conn.Free;
end;
end;var
Conn: TBDEFireDACConnection;
Proc: TBDEFireDACStoredProc;
Balance: Currency;
begin
Conn := TBDEFireDACConnection.Create(nil);
Proc := TBDEFireDACStoredProc.Create(nil);
try
// Konekcija
Conn.DatabaseName := 'C:\Data\MYDB.FDB';
Conn.Connected := True;
// Stored procedure - ISTO KAO BDE!
Proc.DatabaseConnection := Conn;
Proc.StoredProcName := 'GET_CUSTOMER_BALANCE';
Proc.Params.ParamByName('CUSTOMER_ID').AsInteger := 123;
Proc.ExecProc;
// Output parametar
Balance := Proc.Params.ParamByName('BALANCE').AsCurrency;
WriteLn('Stanje: ', Balance:0:2);
finally
Proc.Free;
Conn.Free;
end;
end;var
Conn: TBDEFireDACConnection;
Query: TBDEFireDACQuery;
begin
Conn := TBDEFireDACConnection.Create(nil);
Query := TBDEFireDACQuery.Create(nil);
try
Conn.DatabaseName := 'C:\Data\MYDB.FDB';
Conn.Connected := True;
Query.DatabaseConnection := Conn;
// Transakcija - ISTO KAO BDE!
Conn.StartTransaction;
try
Query.SQL.Text := 'UPDATE ACCOUNTS SET BALANCE = BALANCE - 100 WHERE ID = 1';
Query.ExecSQL;
Query.SQL.Text := 'UPDATE ACCOUNTS SET BALANCE = BALANCE + 100 WHERE ID = 2';
Query.ExecSQL;
Conn.Commit;
except
Conn.Rollback;
raise;
end;
finally
Query.Free;
Conn.Free;
end;
end;# Obavezno!
git commit -am "Pre BDE-FireDAC migracije"Automatski (Find in Files → Replace):
// Pronađi:
uses
Forms, Dialogs, DB, DBTables
// Zameni sa:
uses
Forms, Dialogs, DB, BDEFireDACBridgeSearch & Replace u celom projektu:
| Pronađi | Zameni sa |
|---|---|
: TDatabase |
: TBDEFireDACConnection |
: TQuery |
: TBDEFireDACQuery |
: TStoredProc |
: TBDEFireDACStoredProc |
: TTable |
: TBDEFireDACTable |
Search & Replace (Regex):
Pronađi: object (\w+): TQuery
Zameni sa: object $1: TBDEFireDACQuery
Pronađi: object (\w+): TDatabase
Zameni sa: object $1: TBDEFireDACConnection
Pronađi: object (\w+): TStoredProc
Zameni sa: object $1: TBDEFireDACStoredProc// Pokreni aplikaciju
// Testovi bi trebalo da prođu BEZ izmena logikePython skripta za masovnu zamenu:
import os
import re
def replace_in_file(filepath, replacements):
with open(filepath, 'r', encoding='latin-1') as f:
content = f.read()
for old, new in replacements.items():
content = re.sub(old, new, content)
with open(filepath, 'w', encoding='latin-1') as f:
f.write(content)
# Zamene
replacements = {
r': TDatabase': ': TBDEFireDACConnection',
r': TQuery': ': TBDEFireDACQuery',
r': TStoredProc': ': TBDEFireDACStoredProc',
r'object (\w+): TQuery': r'object \1: TBDEFireDACQuery',
}
# Prolaz kroz sve .pas i .dfm fajlove
for root, dirs, files in os.walk('C:\\MyProject'):
for file in files:
if file.endswith(('.pas', '.dfm')):
filepath = os.path.join(root, file)
replace_in_file(filepath, replacements)# Kompajliraj test
dcc32 TestCompatibility.pas
# Pokreni
TestCompatibility.exe- API kompatibilnost
- Potpisi metoda (method signatures)
- Rukovanje parametrima
- DFM kompatibilnost
- Dokaz da logika ostaje ista
================================================
REZULTATI TESTOVA KOMPATIBILNOSTI
================================================
[✓] Connection.DatabaseName svojstvo
[✓] Connection.Params svojstvo
[✓] Query.SQL svojstvo
[✓] Query.SQL tekst
[✓] StoredProc.StoredProcName
[✓] Connection metode (Open, Close, StartTransaction, Rollback)
[✓] Query metode (Prepare, UnPrepare, ExecSQL)
[✓] StoredProc metode (Prepare, UnPrepare, ExecProc)
[✓] Params.ParamByName - postavljanje
[✓] Params.Count
================================================
Prošlo: 10 | Palo: 0
================================================- TDatabase → TBDEFireDACConnection
- TQuery → TBDEFireDACQuery
- TStoredProc → TBDEFireDACStoredProc
- TTable → TBDEFireDACTable
- Parametri (
:PARAM) - Transakcije
- Dataset navigacija (First, Next, Prior, Last)
- Field access (FieldByName)
- Cached updates (treba custom implementacija)
- Master-Detail relacije (treba dodatna konfiguracija)
- Local SQL (nije potrebno sa FireDAC-om)
- Paradox/dBASE specifični API
| Funkcionalnost | BDE | Wrapper/FireDAC |
|---|---|---|
| Transakcije | Implicitne | Eksplicitne (bolje!) |
| Blob polja | Automatski | Automatski |
| NULL handling | Isto | Isto |
| Performance | Sporije | Brže! |
Za aplikaciju sa 300 formi i 1000 procedura:
| Pristup | Vreme | Rizik | Cena |
|---|---|---|---|
| Potpuna migracija | 6-12m | Visok | €50k+ |
| Wrapper pristup | 1-2 nedelje | Nizak | €5k |
- Podrška za nove verzije Firebird-a
- Bolje performanse
- Modernizacija bez rizika
- Manji uticaj na korisnika (bez prekida)
- Testiraj wrapper na manjem modulu
- Uporedi performanse BDE vs FireDAC
- Postepeno migriraj po modulima
- Finalno ukloni BDE zavisnosti
Za pitanja i pomoć:
Napomena:
Ovo je "proof-of-concept". Za produkcijsku upotrebu preporučuje se temeljno testiranje na realnim podacima.