Active Record è un design pattern architetturale introdotto da Martin Fowler e descritto qui: Martin Fowler - 2003 .
Alla base del pattern Active Record vi è l'idea di astrarre una tabella del database rappresentandola con una classe; la classe seguirà i principi che caratterizzano il paradigma OOP. In questo pattern, ogni istanza della classe rappresenta una singola riga della tabella, mentre la classe stessa è specializzata con metodi che implementano tutte le operazioni CRUD. Una classe così strutturata incapsula tutta la logica necessaria per le operazioni di interazione con il database, creando un'interfaccia orientata agli oggetti per la manipolazione dei dati. È importante notare che questo pattern, per sua natura, crea un accoppiamento tra la struttura della classe e quella della tabella del database sottostante.
Alcune caratteristiche:
- Incapsulamento del database: Tutti i dettagli di connessione e query SQL sono nascosti all'interno della classe
- Validazione dei dati: La classe può implementare regole di validazione prima di eseguire operazioni sul database
- Proprietà dell'oggetto: Corrispondono direttamente alle colonne della tabella
- Relazioni tra oggetti: Possono essere gestite attraverso metodi che implementano le relazioni del database (one-to-many, many-to-many, etc.)
Delphi ad oggi non implementa Active Record.
DMVCFramework, invece, ( https://github.com/danieleteti/delphimvcframework ) porta con se l'implementazione di Active Record.
Active Record mi è stato molto utile qualche giorno fa: avevo l'esigenza di scrivere un piccolo applicativo che prendesse in input un JSON, lo parserizzasse e memorizzasse le informazioni in esso contenute, in un Database. Il JSON in questione è il JSON generato da Anthropic dopo aver richiesto il download delle conversazioni fatte con Claude.AI.
Dopo aver analizzato la struttura del JSON, ho creato le tabelle.
Qui non vi mostro tutto il processo ma una parte di esso che mi permetta di mostrarvi qualche caratteristica di Active Record.
La prima tabella che ho creato è stata quella per la 'testata' della Chat:
CREATE TABLE public.chats ( | |
chat_uuid varchar(40) NOT NULL, | |
chat_name varchar NULL, | |
created_at timestamptz NULL, | |
updated_at timestamptz NULL, | |
CONSTRAINT chats_pk PRIMARY KEY (chat_uuid) | |
); |
In Delphi ho definito una Entity che rappresentasse la tabella:
[MVCNameCase(ncLowerCase)] | |
[MVCTable('chats')] | |
TChats = class(TMVCActiveRecord) | |
private | |
[MVCTableField('chat_uuid', [foPrimaryKey])] | |
fChatUuid: String; | |
[MVCTableField('chat_name')] | |
fChatName: NullableString; | |
[MVCTableField('created_at')] | |
fCreatedAt: NullableTDateTime {dtDateTimeStamp}; | |
[MVCTableField('updated_at')] | |
fUpdatedAt: NullableTDateTime {dtDateTimeStamp}; | |
public | |
constructor Create; override; | |
destructor Destroy; override; | |
property ChatUuid: String read fChatUuid write fChatUuid; | |
property ChatName: NullableString read fChatName write fChatName; | |
property CreatedAt: NullableTDateTime {dtDateTimeStamp} read fCreatedAt write fCreatedAt; | |
property UpdatedAt: NullableTDateTime {dtDateTimeStamp} read fUpdatedAt write fUpdatedAt; | |
end; |
L'entity così definita è l'astrazione della tabella che abbiamo definito nel database.
Analizziamola:
TChats = class(TMVCActiveRecord)
La caratteristica principale che notiamo nella definizione della entity, riguarda i fields della classe.
[MVCTable('chats')] :è il Custom Attribute che ci permette di definire il nome della tabella del DB di cui questa classe è astrazione;
[MVCTableField('chat_uuid', [foPrimaryKey])]: è il Custom Attribute utilizzato per 'legare' il field della classe all'attributo
della tabella fisica del database.
lChat := TChats.Create; | |
try | |
lChat.ChatUuid := AChat.S['uuid']; | |
lChat.ChatName := AChat.S['name']; | |
lChat.CreatedAt := ISO8601ToDate(AChat.S['created_at']); | |
lChat.UpdatedAt := ISO8601ToDate(AChat.S['updated_at']); | |
try | |
if lFounded then | |
lChat.Update(false) | |
else | |
lChat.Insert; | |
except | |
raise Exception.Create('Error Message 1'); | |
end; | |
ExtractMessages(lChat.ChatUuid, AChat.A['chat_messages']) | |
finally | |
lChat.Free; | |
end; |
var lQuery := format('eq(chat_uuid, %s)', [AChat.S['uuid'].QuotedString('"')]); | |
var lChat := TMVCActiveRecord.SelectOneByRQL<TChats>(lQuery, false); | |
var lFounded := Assigned(lChat); |
Nessun commento:
Posta un commento