SimpleORM
ORM completo para Delphi — Entity mapping, CRUD, validacao, paginacao, Horse integration
Repositorio: github.com/academiadocodigo/SimpleORM
Instalacao
Via Boss (recomendado)
boss install academiadocodigo/SimpleORM
Manual
Adicione o diretorio src/ ao Library Path do Delphi. Nao e necessario instalar componentes.
Uses necessarias
uses
SimpleInterface, // Interfaces (iSimpleDAO, iSimpleQuery, etc)
SimpleDAO, // TSimpleDAO<T>
SimpleAttributes, // Atributos (Tabela, Campo, PK, etc)
SimpleQueryFiredac; // Driver de conexao (ou outro driver)
Entity Mapping
Entidades sao classes Delphi mapeadas para tabelas do banco via atributos RTTI. As propriedades devem estar na secao published.
uses SimpleAttributes;
type
[Tabela('PEDIDO')]
TPEDIDO = class
private
FID: Integer;
FCLIENTE: String;
FDATAPEDIDO: TDatetime;
FVALORTOTAL: Currency;
procedure SetID(const Value: Integer);
procedure SetCLIENTE(const Value: String);
procedure SetDATAPEDIDO(const Value: TDatetime);
procedure SetVALORTOTAL(const Value: Currency);
public
constructor Create;
destructor Destroy; override;
published
[Campo('ID'), Pk, AutoInc]
property ID: Integer read FID write SetID;
[Campo('NOME')]
property CLIENTE: String read FCLIENTE write SetCLIENTE;
[Campo('DATA')]
property DATAPEDIDO: TDatetime read FDATAPEDIDO write SetDATAPEDIDO;
[Campo('VALOR')]
property VALORTOTAL: Currency read FVALORTOTAL write SetVALORTOTAL;
end;
Quando o nome da property e igual ao nome da coluna no banco, o atributo [Campo] e opcional. Exemplo: property ID mapeia automaticamente para coluna ID.
Regras de Entidade
[Tabela('NOME')]na classe — obrigatorio[Campo('COLUNA')]nas properties — obrigatorio para mapeamento SQL- Exatamente um
[PK]por entidade - Properties na secao
published— RTTI nao enxergapublic - Getter/setter explicitos para cada property
constructor Createedestructor Destroy; overridesempre presentes
Todos os Atributos
Todos os atributos ficam em SimpleAttributes.pas.
Mapeamento
| Atributo | Alvo | Descricao |
|---|---|---|
Tabela('NOME') | Classe | Mapeia classe para tabela do banco |
Campo('NOME') | Property | Mapeia property para coluna do banco |
Chaves e Auto-Incremento
| Atributo | Descricao |
|---|---|
PK | Chave primaria (exatamente uma por entidade) |
FK | Chave estrangeira |
AutoInc | Auto-incremento (excluido do INSERT automaticamente) |
Restricoes
| Atributo | Descricao |
|---|---|
NotNull | Campo obrigatorio (string nao vazia, data nao zero) |
NotZero | Valor numerico nao pode ser zero |
Ignore | Ignora a property em todas operacoes SQL e serializacao |
NumberOnly | Aceita somente numeros |
Validacao
| Atributo | Descricao |
|---|---|
Email | Valida formato de email |
MinValue(n) | Valor numerico minimo |
MaxValue(n) | Valor numerico maximo |
Regex('pattern', 'msg') | Validacao por expressao regular |
Format(maxSize, precision) | Tamanho maximo, tamanho minimo, precisao ou mascara |
Display e Binding
| Atributo | Descricao |
|---|---|
Display('Label') | Nome para exibicao em grids/forms |
Bind('CAMPO') | Liga componente visual a property da entidade |
Relacionamentos
| Atributo | Cardinalidade | Carregamento |
|---|---|---|
HasOne('Entity', 'FK') | 1:1 | Eager (automatico no Find) |
BelongsTo('Entity', 'FK') | N:1 | Eager (automatico no Find) |
HasMany('Entity', 'FK') | 1:N | Lazy (TSimpleLazyLoader) |
BelongsToMany('Entity', 'FK') | M:N | Manual |
Especiais
| Atributo | Descricao |
|---|---|
SoftDelete('CAMPO') | Na classe — ativa exclusao logica (0=ativo, 1=deletado) |
Enumerator('TIPO') | Cast de enum para PostgreSQL |
Inicializacao
var
Conn: iSimpleQuery;
DAOPedido: iSimpleDAO<TPEDIDO>;
begin
// 1. Criar conexao via driver
Conn := TSimpleQueryFiredac.New(FDConnection1);
// 2. Criar DAO tipado
DAOPedido := TSimpleDAO<TPEDIDO>.New(Conn);
// 3. (Opcional) Vincular DataSource e Form
DAOPedido
.DataSource(DataSource1) // dados no DataSet para grids
.BindForm(Self); // bind automatico de componentes
end;
iSimpleQuery abstrai o driver de banco. iSimpleDAO<T> e o DAO generico que executa CRUD sobre qualquer entidade mapeada.
Operacoes CRUD
Insert
// Com objeto
var LPedido: TPEDIDO;
begin
LPedido := TPEDIDO.Create;
try
LPedido.CLIENTE := 'Fulano';
LPedido.DATAPEDIDO := Now;
LPedido.VALORTOTAL := 150.50;
DAOPedido.Insert(LPedido);
finally
LPedido.Free;
end;
end;
// Com Bind (VCL/FMX — pega valores do form automaticamente)
DAOPedido.Insert;
Update
// Com objeto (PK identifica o registro)
var LPedido: TPEDIDO;
begin
LPedido := TPEDIDO.Create;
try
LPedido.ID := 1;
LPedido.CLIENTE := 'Nome Atualizado';
LPedido.DATAPEDIDO := Now;
LPedido.VALORTOTAL := 200.00;
DAOPedido.Update(LPedido);
finally
LPedido.Free;
end;
end;
// Com Bind
DAOPedido.Update;
Delete
// Com objeto (usa PK)
DAOPedido.Delete(LPedido);
// Com Bind (usa PK do form)
DAOPedido.Delete;
// Por campo e valor
DAOPedido.Delete('ID', '1');
// Force Delete (ignora SoftDelete)
DAOPedido.ForceDelete(LPedido);
Find (Select)
// Todos os registros (popula DataSource se vinculado)
DAOPedido.Find;
// Retorna lista de objetos
var LList: TObjectList<TPEDIDO>;
begin
LList := TObjectList<TPEDIDO>.Create;
try
DAOPedido.Find(LList);
for I := 0 to LList.Count - 1 do
Writeln(LList[I].CLIENTE);
finally
LList.Free;
end;
end;
// Busca por ID (retorna objeto — caller deve dar Free)
var LPedido: TPEDIDO;
begin
LPedido := DAOPedido.Find(1);
try
if Assigned(LPedido) then
Writeln(LPedido.CLIENTE);
finally
LPedido.Free;
end;
end;
// Busca por campo/valor
DAOPedido.Find('CLIENTE', 'Fulano');
// Ultimo ID e ultimo registro
DAOPedido.LastID;
DAOPedido.LastRecord;
Fluent SQL Builder
O iSimpleDAOSQLAttribute<T> permite construir queries customizadas via fluent interface:
DAOPedido
.SQL
.Fields('ID, NOME, VALOR') // campos especificos
.Where('VALOR > 100') // clausula WHERE
.OrderBy('NOME') // ordenacao
.GroupBy('CATEGORIA') // agrupamento
.Join('INNER JOIN CLIENTE ON CLIENTE.ID = PEDIDO.ID_CLIENTE')
.Skip(10) // pular N registros
.Take(20) // limitar a N registros
.&End // retorna ao DAO
.Find; // executa
O metodo .&End (com & para escapar a palavra reservada) retorna ao iSimpleDAO<T> para executar a query construida.
Paginacao
Paginacao e suportada via Skip(n) e Take(n) com SQL gerado automaticamente conforme o banco:
// Buscar registros 11 a 30
DAOPedido.SQL.Skip(10).Take(20).&End.Find(LList);
| Banco | SQL Gerado | Posicao |
|---|---|---|
| Firebird | SELECT FIRST 20 SKIP 10 ... | Apos SELECT |
| MySQL | ... LIMIT 20 OFFSET 10 | Fim da query |
| SQLite | ... LIMIT 20 OFFSET 10 | Fim da query |
| Oracle | ... OFFSET 10 ROWS FETCH NEXT 20 ROWS ONLY | Fim da query |
O tipo de banco e definido pelo TSQLType do driver de query.
Validacao
TSimpleValidator valida entidades via atributos RTTI. Lanca ESimpleValidator com mensagens acumuladas.
uses SimpleValidator;
var LCliente: TCliente;
begin
LCliente := TCliente.Create;
try
LCliente.Parse(Self); // preenche via Bind do form
TSimpleValidator.Validate(LCliente); // valida — lanca exception se invalido
DAOCliente.Insert(LCliente);
finally
LCliente.Free;
end;
end;
Atributos de validacao
| Atributo | O que valida |
|---|---|
NotNull | String nao vazia, data nao zero |
NotZero | Numerico diferente de zero |
Email | Formato de email valido |
MinValue(n) | Valor numerico >= n |
MaxValue(n) | Valor numerico <= n |
Regex('pattern', 'msg') | Match contra expressao regular |
Format(max, precision) | Tamanho de string e precisao numerica |
Relacionamentos
HasOne / BelongsTo (Eager Loading)
Carregados automaticamente pelo DAO no Find:
[Tabela('ITEM_PEDIDO')]
TItemPedido = class
published
[Campo('ID'), PK, AutoInc]
property ID: Integer ...;
[Campo('ID_PEDIDO'), FK]
property IdPedido: Integer ...;
[HasOne('PEDIDO', 'ID_PEDIDO')]
property Pedido: TPedido ...;
end;
HasMany (Lazy Loading)
Usa TSimpleLazyLoader<T> de SimpleProxy.pas. Os dados sao carregados na primeira chamada a Count ou ToArray:
uses SimpleProxy;
// No construtor ou sob demanda:
FItens := TSimpleLazyLoader<TItemPedido>.Create(Query, 'ID_PEDIDO', Self.Id);
Soft Delete
Exclusao logica — registros nao sao removidos do banco, apenas marcados.
[Tabela('PRODUTO'), SoftDelete('DELETADO')]
TProduto = class
published
[Campo('ID'), PK, AutoInc]
property ID: Integer ...;
[Campo('DELETADO')]
property Deletado: Integer ...; // 0=ativo, 1=deletado
end;
Comportamento automatico
| Operacao | SQL Gerado |
|---|---|
Delete(entity) | UPDATE PRODUTO SET DELETADO = 1 WHERE ID = :ID |
Find | SELECT ... WHERE DELETADO = 0 (filtro automatico) |
ForceDelete(entity) | DELETE FROM PRODUTO WHERE ID = :ID (ignora soft delete) |
Batch Operations
Operacoes em lote com transacao automatica (StartTransaction/Commit/Rollback):
var LList: TObjectList<TPEDIDO>;
begin
LList := TObjectList<TPEDIDO>.Create;
try
// Preencher lista...
DAOPedido.InsertBatch(LList); // insere todos em transacao
DAOPedido.UpdateBatch(LList); // atualiza todos em transacao
DAOPedido.DeleteBatch(LList); // deleta todos em transacao
finally
LList.Free;
end;
end;
Se qualquer operacao falhar, a transacao faz rollback automaticamente e a exception e re-lancada.
Transacoes
Controle explicito de transacoes via iSimpleQuery:
var LQuery: iSimpleQuery;
begin
LQuery := TSimpleQueryFiredac.New(Connection);
LQuery.StartTransaction;
try
// Operacoes...
LQuery.Commit;
except
LQuery.Rollback;
raise;
end;
end;
| Metodo | Descricao |
|---|---|
StartTransaction | Inicia transacao (verifica se ja esta ativa) |
Commit | Confirma transacao ativa |
Rollback | Desfaz transacao ativa |
InTransaction | Retorna True se transacao esta ativa |
&EndTransaction | Alias para Commit |
Query Logging
Registra SQL, parametros e tempo de execucao:
uses SimpleLogger;
DAOPedido.Logger(TSimpleQueryLoggerConsole.New);
// A partir daqui, toda operacao do DAO sera logada:
// SQL: SELECT ID, NOME FROM PEDIDO WHERE ID = :ID
// Params: ID=1
// Duration: 3ms
TSimpleQueryLoggerConsole envia para OutputDebugString (Windows) ou Writeln (console). Implemente iSimpleQueryLogger para loggers customizados.
Drivers de Banco
Todos implementam iSimpleQuery — troque o driver sem alterar nenhum codigo do DAO.
| Driver | Classe | Conexao | Unit |
|---|---|---|---|
| FireDAC | TSimpleQueryFiredac | TFDConnection | SimpleQueryFiredac.pas |
| UniDAC | TSimpleQueryUnidac | TUniConnection | SimpleQueryUnidac.pas |
| Zeos | TSimpleQueryZeos | TZConnection | SimpleQueryZeos.pas |
| RestDW | TSimpleQueryRestDW | TRESTDWClientSQL | SimpleQueryRestDW.pas |
| Horse (REST) | TSimpleQueryHorse | URL string | SimpleQueryHorse.pas |
Exemplo: trocar driver
// FireDAC (banco local)
LDAO := TSimpleDAO<T>.New(TSimpleQueryFiredac.New(FDConnection1));
// UniDAC (banco local)
LDAO := TSimpleDAO<T>.New(TSimpleQueryUnidac.New(UniConnection1));
// Horse REST (servidor remoto)
LDAO := TSimpleDAO<T>.New(TSimpleQueryHorse.New('http://server:9000'));
// O codigo do DAO e IDENTICO para todos os drivers
Horse Server NEW
Auto-gera 5 rotas CRUD no Horse (ExpxHorse) por entidade com uma unica linha:
uses Horse, SimpleInterface, SimpleQueryFiredac, SimpleHorseRouter;
// Uma linha = 5 rotas REST
TSimpleHorseRouter.RegisterEntity<TPEDIDO>(THorse, LQuery);
THorse.Listen(9000);
Rotas geradas
| Metodo | Rota | Status | Resposta |
|---|---|---|---|
| GET | /pedido?skip=N&take=N | 200 | {"data": [...], "count": N} |
| GET | /pedido/:id | 200/404 | JSON da entidade ou {"error": "Not found"} |
| POST | /pedido | 201 | Entidade criada em JSON |
| PUT | /pedido/:id | 200 | Entidade atualizada em JSON |
| DELETE | /pedido/:id | 204 | Sem corpo |
O path e derivado do atributo [Tabela] lowercased, ou customizado via 3o parametro.
Callbacks
TSimpleHorseRouter.RegisterEntity<TPEDIDO>(THorse, LQuery)
.OnBeforeInsert(
procedure(aEntity: TObject; var aContinue: Boolean)
begin
TSimpleValidator.Validate(aEntity);
aContinue := True; // False retorna 400
end
)
.OnAfterInsert(
procedure(aEntity: TObject)
begin
Writeln('Inserido: ', TPEDIDO(aEntity).CLIENTE);
end
)
.OnBeforeUpdate(
procedure(aEntity: TObject; var aContinue: Boolean)
begin
aContinue := True;
end
)
.OnBeforeDelete(
procedure(aId: string; var aContinue: Boolean)
begin
aContinue := True;
end
);
Horse Client REST NEW
TSimpleQueryHorse implementa iSimpleQuery via HTTP. A unica diferenca em relacao ao acesso direto ao banco e a linha de criacao do query:
// ANTES: acesso direto ao banco
LDAO := TSimpleDAO<TPEDIDO>.New(TSimpleQueryFiredac.New(Connection));
// DEPOIS: via servidor Horse (mesma interface!)
LDAO := TSimpleDAO<TPEDIDO>.New(TSimpleQueryHorse.New('http://server:9000'));
// O resto do codigo e IDENTICO
LDAO.Insert(LPedido);
LDAO.Find(LList);
LDAO.SQL.Skip(0).Take(10).&End.Find(LList);
Autenticacao
// Bearer token
TSimpleQueryHorse.New('http://server:9000', 'meu-token');
// Headers customizados
var LQuery: iSimpleQuery;
begin
LQuery := TSimpleQueryHorse.New('http://server:9000');
TSimpleQueryHorse(LQuery).OnBeforeRequest(
procedure(aHeaders: TStrings)
begin
aHeaders.Values['X-Custom-Header'] := 'valor';
end
);
LDAO := TSimpleDAO<TPEDIDO>.New(LQuery);
end;
Transacoes sao no-ops no driver REST (HTTP e stateless). InTransaction sempre retorna False.
SimpleSerializer NEW
Converte entidades para JSON e vice-versa via RTTI, usando os nomes de [Campo] como chaves JSON:
uses SimpleSerializer;
// Entity -> JSON
var LJSON: TJSONObject;
LJSON := TSimpleSerializer.EntityToJSON<TPEDIDO>(LPedido);
// {"ID": 1, "NOME": "Fulano", "DATA": "2026-03-08T10:00:00", "VALOR": 150.5}
// JSON -> Entity (caller deve dar Free no resultado)
var LPedido: TPEDIDO;
LPedido := TSimpleSerializer.JSONToEntity<TPEDIDO>(LJSON);
// Listas
var LArray: TJSONArray;
LArray := TSimpleSerializer.EntityListToJSONArray<TPEDIDO>(LList);
var LList: TObjectList<TPEDIDO>;
LList := TSimpleSerializer.JSONArrayToEntityList<TPEDIDO>(LArray);
Tipos suportados
| Delphi | JSON |
|---|---|
String | string |
Integer | number |
Int64 | number |
Double/Currency | number |
TDateTime | ISO8601 string |
Boolean | true/false |
Bind de Formularios
O SimpleORM faz bind automatico entre componentes visuais e properties da entidade:
type
TForm1 = class(TForm)
[Bind('CLIENTE')]
Edit1: TEdit;
[Bind('ID')]
Edit2: TEdit;
[Bind('VALORTOTAL')]
Edit3: TEdit;
[Bind('DATAPEDIDO')]
DateTimePicker1: TDateTimePicker;
end;
O atributo [Bind] no componente indica qual property (ou coluna) da entidade ele representa. Com o bind configurado:
// Insert pega os valores dos componentes automaticamente
DAOPedido.Insert;
// Update tambem
DAOPedido.Update;
// Find preenche os componentes com dados do banco
DAOPedido.Find;
Bind funciona com VCL (TEdit, TComboBox, TCheckBox, TDateTimePicker, etc) e FMX. Requer compilacao SEM {$DEFINE CONSOLE}.
API de Interfaces
Todas as interfaces ficam em SimpleInterface.pas.
iSimpleDAO<T>
| Metodo | Retorno | Descricao |
|---|---|---|
Insert(aValue: T) | iSimpleDAO<T> | Insere entidade |
Update(aValue: T) | iSimpleDAO<T> | Atualiza por PK |
Delete(aValue: T) | iSimpleDAO<T> | Deleta por PK (ou soft delete) |
Delete(aField, aValue) | iSimpleDAO<T> | Deleta por campo/valor |
ForceDelete(aValue: T) | iSimpleDAO<T> | Deleta fisicamente (ignora soft delete) |
Find | iSimpleDAO<T> | Busca todos (popula DataSource) |
Find(var aList) | iSimpleDAO<T> | Busca todos em lista de objetos |
Find(aId: Integer) | T | Busca por ID (retorna objeto) |
Find(aKey, aValue) | iSimpleDAO<T> | Busca por campo/valor |
InsertBatch(aList) | iSimpleDAO<T> | Insert em lote com transacao |
UpdateBatch(aList) | iSimpleDAO<T> | Update em lote com transacao |
DeleteBatch(aList) | iSimpleDAO<T> | Delete em lote com transacao |
LastID | iSimpleDAO<T> | Busca ultimo ID inserido |
LastRecord | iSimpleDAO<T> | Busca ultimo registro |
SQL | iSimpleDAOSQLAttribute<T> | Acessa fluent SQL builder |
Logger(aLogger) | iSimpleDAO<T> | Define logger de queries |
DataSource(aDS) | iSimpleDAO<T> | Vincula DataSource |
BindForm(aForm) | iSimpleDAO<T> | Vincula form para bind |
iSimpleDAOSQLAttribute<T>
| Metodo | Descricao |
|---|---|
Fields(aSQL) | Campos do SELECT |
Where(aSQL) | Clausula WHERE |
OrderBy(aSQL) | Ordenacao |
GroupBy(aSQL) | Agrupamento |
Join(aSQL) | JOINs |
Skip(n) | Pular N registros (paginacao) |
Take(n) | Limitar a N registros (paginacao) |
Clear | Limpa todos os filtros |
&End | Retorna ao iSimpleDAO<T> |
iSimpleQuery
| Metodo | Retorno | Descricao |
|---|---|---|
SQL | TStrings | Texto SQL |
Params | TParams | Parametros da query |
ExecSQL | iSimpleQuery | Executa INSERT/UPDATE/DELETE |
DataSet | TDataSet | Resultado do SELECT |
Open(aSQL) | iSimpleQuery | Define SQL e abre |
Open | iSimpleQuery | Abre com SQL ja definido |
StartTransaction | iSimpleQuery | Inicia transacao |
Commit | iSimpleQuery | Confirma transacao |
Rollback | iSimpleQuery | Desfaz transacao |
&EndTransaction | iSimpleQuery | Alias para Commit |
InTransaction | Boolean | True se transacao ativa |
SQLType | TSQLType | Tipo do banco |
Tipos de Banco (TSQLType)
TSQLType = (Firebird, MySQL, SQLite, Oracle);
Definido em SimpleTypes.pas. Afeta a geracao de SQL para paginacao. O tipo e passado no construtor do driver:
// Firebird (padrao)
TSimpleQueryFiredac.New(Conn);
// MySQL
TSimpleQueryFiredac.New(Conn, TSQLType.MySQL);
// SQLite
TSimpleQueryFiredac.New(Conn, TSQLType.SQLite);
// Oracle
TSimpleQueryFiredac.New(Conn, TSQLType.Oracle);
Testes Unitarios (DUnit) NEW
O SimpleORM possui suite completa de testes unitarios usando DUnit, cobrindo atributos, RTTI helpers, geracao SQL, validacao e serializacao.
Estrutura
tests/
SimpleORMTests.dpr -- Test runner console
Entities/
TestEntities.pas -- Entidades de teste decoradas com atributos
TestSimpleAttributes.pas -- Testes dos atributos (22 testes)
TestSimpleRTTIHelper.pas -- Testes dos RTTI helpers (26 testes)
TestSimpleSQL.pas -- Testes de geracao SQL (20 testes)
TestSimpleValidator.pas -- Testes de validacao (21 testes)
TestSimpleSerializer.pas -- Testes de serializacao JSON (10 testes)
Executando os Testes
// 1. Abra tests/SimpleORMTests.dpr no Delphi IDE
// 2. Compile (IDE gera .dproj e .res automaticamente)
// 3. Execute
// Com pausa apos execucao:
SimpleORMTests.exe -pause
Cobertura de Testes
| Unit | Testes | O que cobre |
|---|---|---|
SimpleAttributes | 22 | Construtores, propriedades, GetNumericMask, relacionamentos |
SimpleRTTIHelper | 26 | IsNotNull, IsAutoInc, FieldName, DisplayName, EhChavePrimaria, SoftDelete, ValueHelper |
SimpleSQL | 20 | INSERT, UPDATE, DELETE, SELECT, paginacao (Firebird/MySQL/SQLite/Oracle), SoftDelete |
SimpleValidator | 21 | NotNull, NotZero, Format, Email, MinValue, MaxValue, Regex, exception |
SimpleSerializer | 10 | EntityToJSON, JSONToEntity, listas, roundtrip, campos ignorados |
Adicionando Novos Testes
unit TestSimpleNovaFeature;
interface
uses
TestFramework;
type
TTestNovaFeature = class(TTestCase)
published
procedure TestCenario_ShouldPass;
procedure TestCenario_ShouldFail;
end;
implementation
procedure TTestNovaFeature.TestCenario_ShouldPass;
begin
CheckTrue(True, 'Descricao do teste');
end;
procedure TTestNovaFeature.TestCenario_ShouldFail;
begin
// testar cenario de erro
end;
initialization
RegisterTest('NovaFeature', TTestNovaFeature.Suite);
end.
uses do SimpleORMTests.dpr.
AI Entity Enrichment NEW
O SimpleORM permite enriquecer entidades automaticamente com IA. Basta decorar propriedades com atributos AI e o processamento acontece automaticamente durante Insert/Update.
Atributos AI
| Atributo | Parametros | Descricao |
|---|---|---|
| AIGenerated | promptTemplate: String | Gera conteudo via LLM. Template suporta {PropertyName} para interpolacao |
| AISummarize | sourceProperty: String, maxLength: Integer | Resume o conteudo de outra propriedade |
| AITranslate | sourceProperty: String, targetLanguage: String | Traduz o conteudo de outra propriedade |
| AIClassify | sourceProperty: String, categories: String | Classifica conteudo em categorias separadas por virgula |
| AIValidate | rule: String, errorMessage: String | Valida conteudo contra uma regra. Lanca excecao se invalido |
Exemplo de entidade com atributos AI
[Tabela('PRODUTOS')]
TProduto = class
published
[Campo('NOME')]
property NOME: String read FNOME write SetNOME;
[Campo('DESCRICAO')]
property DESCRICAO: String read FDESCRICAO write SetDESCRICAO;
[Campo('RESUMO'), AISummarize('DESCRICAO', 100)]
property RESUMO: String read FRESUMO write SetRESUMO;
[Campo('CATEGORIA'), AIClassify('DESCRICAO', 'Eletronico,Vestuario,Alimento')]
property CATEGORIA: String read FCATEGORIA write SetCATEGORIA;
[Campo('DESCRICAO_EN'), AITranslate('DESCRICAO', 'English')]
property DESCRICAO_EN: String read FDESCRICAO_EN write SetDESCRICAO_EN;
[Campo('SLOGAN'), AIGenerated('Crie um slogan para {NOME}')]
property SLOGAN: String read FSLOGAN write SetSLOGAN;
end;
Uso com TSimpleDAO (automatico)
// Configurar AI client no DAO
DAOProduto := TSimpleDAO<TProduto>
.New(Conn)
.AIClient(TSimpleAIClient.New('claude', 'sua-api-key'));
// AI processa AUTOMATICAMENTE antes de Insert/Update
DAOProduto.Insert(Produto);
// Propriedades com atributos AI sao preenchidas pelo LLM!
Uso direto do Processor
LAIClient := TSimpleAIClient.New('claude', 'sua-api-key');
LProcessor := TSimpleAIProcessor.New(LAIClient);
try
LProcessor.Process(Produto);
// Propriedades AI foram preenchidas
finally
FreeAndNil(LProcessor);
end;
Providers suportados
| Provider | Modelo padrao | Uso |
|---|---|---|
| claude | claude-sonnet-4-20250514 | TSimpleAIClient.New('claude', 'api-key') |
| openai | gpt-4o-mini | TSimpleAIClient.New('openai', 'api-key') |
O AI client e opcional. Se nao configurado via AIClient(), o DAO funciona normalmente sem processamento AI.
AI Query NEW
O SimpleORM permite fazer perguntas em linguagem natural ao banco de dados, traduzindo automaticamente para SQL via LLM.
Metodos
| Metodo | Input | Output | Descricao |
|---|---|---|---|
| NaturalLanguageQuery | pergunta: String | TDataSet | Traduz para SQL, executa, retorna resultado |
| AskQuestion | pergunta: String | String | Traduz, executa, LLM responde em linguagem natural |
| ExplainQuery | sql: String | String | Explica SQL em linguagem natural |
| SuggestQuery | objetivo: String | String | Sugere SQL baseado no objetivo |
Exemplo de uso
AIQuery := TSimpleAIQuery.New(Query, AIClient);
AIQuery
.RegisterEntity<TCliente>
.RegisterEntity<TPedido>
.RegisterEntity<TProduto>;
// Pergunta em linguagem natural - retorna TDataSet
LDataSet := AIQuery.NaturalLanguageQuery('Top 5 clientes por valor de compras em 2025');
// Resposta em texto natural
LResposta := AIQuery.AskQuestion('Qual o ticket medio de vendas?');
// Explicar SQL existente
LExplicacao := AIQuery.ExplainQuery('SELECT AVG(VALOR) FROM PEDIDOS');
// Sugerir SQL para um objetivo
LSQL := AIQuery.SuggestQuery('Clientes inativos ha mais de 90 dias');
Seguranca
Por seguranca, apenas SQL SELECT e permitido. Keywords como INSERT, UPDATE, DELETE, DROP, ALTER, CREATE, TRUNCATE, EXEC, GRANT e REVOKE sao bloqueados automaticamente. Ponto-e-virgula tambem e bloqueado para prevenir multi-statement injection.
Limitacao de resultados
// Limitar a 50 resultados (padrao: 100)
AIQuery.MaxRows(50);
// A paginacao e aplicada automaticamente baseado no tipo de banco:
// Firebird: SELECT FIRST N ...
// MySQL/SQLite: ... LIMIT N
// Oracle: ... FETCH NEXT N ROWS ONLY
MCP Server (AI Integration) NEW
O SimpleORM inclui um servidor MCP (Model Context Protocol) embutido que permite AI assistants como Claude e Cursor interagirem diretamente com o banco de dados da sua aplicacao, com permissoes granulares e seguranca.
uses SimpleMCPServer, SimpleMCPTypes;
Server := TSimpleMCPServer.New;
Server
.RegisterEntity<TUsuario>(Query, [mcpRead, mcpInsert, mcpUpdate])
.RegisterEntity<TProduto>(Query, [mcpRead, mcpCount])
.EnableRawQuery(Query)
.StartStdio; // ou .Token('secret').StartHttp(9000)
Recursos
- Gera tools MCP automaticamente a partir das entidades registradas (query, get, insert, update, delete, count, describe, ddl)
- Permissoes granulares:
mcpRead,mcpInsert,mcpUpdate,mcpDelete,mcpCount,mcpDDL - Dois transportes: stdio (local) e HTTP via Horse (remoto com Bearer token)
- SQL raw read-only com protecao contra injection
- Validacao via SimpleValidator em insert/update
Rules (Regras) NEW
Regras declarativas na entidade que executam automaticamente durante CRUD. Podem ser deterministicas (expressoes Delphi) ou inteligentes (via AI).
Atributos
| Atributo | Parametros | Descricao |
|---|---|---|
| Rule | expression, action, message | Regra deterministica avaliada contra propriedades da entidade |
| AIRule | description, action | Regra inteligente avaliada pelo LLM |
Exemplo
[Tabela('PEDIDOS')]
[Rule('VALOR > 0', raBeforeInsert, 'Valor deve ser positivo')]
[Rule('STATUS <> ''CANCELADO''', raBeforeUpdate, 'Pedido cancelado nao pode ser alterado')]
[AIRule('Verificar se o endereco e valido', raBeforeInsert)]
TPedido = class
published
[Campo('VALOR')]
property VALOR: Currency;
[Campo('STATUS')]
property STATUS: String;
end;
Rules sao detectadas automaticamente via RTTI. Nenhuma configuracao no DAO e necessaria.
Skills (Habilidades) NEW
Plugins reutilizaveis que se conectam ao DAO via fluent API. Executam antes ou depois de operacoes CRUD. O SimpleORM oferece 19 Skills prontas organizadas em 3 categorias.
Documentacao completa de Skills →
Skills Built-in (SimpleSkill.pas)
8 Skills de infraestrutura para logging, auditoria, validacao e integracao.
| Skill | Descricao |
|---|---|
TSkillLog | Loga operacao via Logger ou OutputDebugString/Writeln |
TSkillNotify | Dispara callback TProc<TObject> |
TSkillAudit | Insere registro em tabela de auditoria |
TSkillTimestamp | Preenche campos de data automaticamente |
TSkillValidate | Chama TSimpleValidator.Validate automaticamente |
TSkillGuardDelete | Bloqueia delete com registros dependentes |
TSkillHistory | Grava snapshot antes de update/delete |
TSkillWebhook | HTTP POST fire-and-forget com JSON |
Skills ERP (SimpleSkill.pas)
4 Skills para ERPs brasileiros: numeracao sequencial, calculo de totais, estoque e parcelas. Inclui atributos [CPF] e [CNPJ] para validacao de documentos.
| Skill | Descricao |
|---|---|
TSkillSequence | Numeracao sequencial via tabela de controle |
TSkillCalcTotal | Calcula total: Qtd * Preco - Desconto |
TSkillStockMove | Registra movimentacao de estoque |
TSkillDuplicate | Gera parcelas financeiras |
Skills AI (SimpleAISkill.pas)
7 Skills baseadas em IA para enriquecimento, validacao/moderacao e analise de sentimento via LLM.
| Skill | Descricao |
|---|---|
TSkillAIEnrich | Gera conteudo via prompt template com {PropertyName} |
TSkillAITranslate | Traducao automatica entre campos |
TSkillAISummarize | Resumo automatico de texto |
TSkillAITags | Geracao automatica de tags/keywords |
TSkillAIModerate | Moderacao de conteudo com bloqueio |
TSkillAIValidate | Validacao com regra em linguagem natural |
TSkillAISentiment | Analise de sentimento (POSITIVO/NEGATIVO/NEUTRO) |
Exemplo
DAO := TSimpleDAO<TPedido>.New(Query)
.Skill(TSkillTimestamp.New('CREATED_AT', srBeforeInsert))
.Skill(TSkillValidate.New(srBeforeInsert))
.Skill(TSkillLog.New('App', srAfterInsert))
.Skill(TSkillCalcTotal.New('VALOR_TOTAL', 'QUANTIDADE', 'PRECO_UNITARIO', 'DESCONTO'))
.Skill(TSkillAISentiment.New('COMENTARIO', 'SENTIMENTO'));
DAO.Insert(Pedido);
Skills sao opcionais. Se nenhum skill for registrado, o DAO funciona normalmente. Para documentacao detalhada com exemplos completos, parametros, DDL e casos de uso, veja a pagina dedicada de Skills.
Agents (Agentes) NEW
Orquestradores que combinam Rules, Skills e operacoes CRUD. Modo reativo (reage a eventos) e proativo (linguagem natural via AI).
Modo Reativo
LAgent := TSimpleAgent.New;
LAgent.When(TPedido, aoAfterInsert)
.Condition(function(aEntity: TObject): Boolean
begin
Result := TPedido(aEntity).VALOR > 5000;
end)
.Execute(TSkillEnviarEmail.New('gerente@empresa.com'))
.Execute(TSkillLog.New('alto-valor'));
DAOPedido := TSimpleDAO<TPedido>.New(Conn).Agent(LAgent);
DAOPedido.Insert(Pedido); // Agent reage automaticamente
Modo Proativo
LAgent := TSimpleAgent.New(Conn, AIClient);
LAgent.RegisterEntity<TPedido>.RegisterEntity<TCliente>;
// Planejar antes de executar (SafeMode)
LPlan := LAgent.Plan('Listar pedidos pendentes de hoje');
Writeln(LPlan.Description);
Writeln(LPlan.Risk); // LOW, MEDIUM ou HIGH
LPlan.Execute;
O modo proativo requer SafeMode (padrao: True). Use Plan() para inspecionar antes de Execute().
Supabase Integration NEW
O SimpleORM suporta conexao direta com o Supabase via PostgREST API, sem necessidade de servidor intermediario. A integracao inclui 3 componentes:
| Componente | Unit | Descricao |
|---|---|---|
| TSimpleQuerySupabase | SimpleQuerySupabase | Driver CRUD implementando iSimpleQuery — INSERT, UPDATE, DELETE, SELECT via REST |
| TSimpleSupabaseAuth | SimpleSupabaseAuth | Autenticacao — SignIn, SignUp, SignOut, JWT auto-refresh, Row Level Security |
| TSimpleSupabaseRealtime | SimpleSupabaseRealtime | Monitoramento em tempo real — callbacks globais e por tabela via polling |
Exemplo rapido
uses SimpleQuerySupabase, SimpleSupabaseAuth;
// Conexao basica
var LQuery := TSimpleQuerySupabase.New(
'https://seu-projeto.supabase.co',
'sua-api-key'
);
// CRUD identico a qualquer outro driver
var LDAO := TSimpleDAO<TProduto>.New(LQuery);
LDAO.Insert(LProduto);
LDAO.Find;
LDAO.Update(LProduto);
LDAO.Delete(LProduto);
// Com autenticacao e Row Level Security
var LAuth := TSimpleSupabaseAuth.New(URL, AnonKey);
LAuth.SignIn('usuario@email.com', 'senha123');
var LQueryAuth := TSimpleQuerySupabase.New(URL, AnonKey, LAuth);
Nota: O driver usa System.Net.HttpClient nativo, sem dependencias externas. Transacoes sao no-ops (REST e stateless).
Documentacao completa do Supabase →
Referencia completa da API, exemplos detalhados de CRUD, Auth, Realtime, Row Level Security, troubleshooting e mais.
GitHub Issue Skill NEW
O SimpleORM pode criar Issues no GitHub automaticamente quando operacoes CRUD falham ou como auditoria de operacoes bem-sucedidas.
| Componente | Descricao |
|---|---|
| TSkillGitHubIssue | Skill que cria Issues via GitHub REST API — fire-and-forget |
| TSkillRunMode | Enum srmNormal (auditoria) ou srmOnError (falhas) |
| OnError | Callback generico no DAO para tratamento de erros |
Exemplo rapido
uses SimpleSkill, SimpleTypes;
// Modo OnError — cria Issue quando Insert falha
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillGitHubIssue.New(
'meuuser/meurepo',
'ghp_token123',
srAfterInsert,
srmOnError
))
.OnError(procedure(aEntity: TObject; E: Exception)
begin
Writeln('Erro: ', E.Message);
end);
// Modo Normal com template — auditoria de Deletes
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillGitHubIssue.New(
'meuuser/meurepo',
'ghp_token123',
srAfterDelete,
srmNormal
).Labels(['audit', 'delete'])
.TitleTemplate('[Audit] {entity} deletado'));
Placeholders para templates
| Placeholder | Descricao |
|---|---|
{entity} | Nome da tabela da entidade |
{operation} | INSERT, UPDATE ou DELETE |
{error} | Mensagem de erro (vazio em modo Normal) |
{timestamp} | Data/hora em formato ISO8601 |
Nota: Fire-and-forget — erros HTTP sao logados mas nunca interrompem o fluxo CRUD. Requer Personal Access Token do GitHub com permissao repo.
Data Migration NEW
O SimpleORM oferece um framework fluent para migracao de dados entre bancos de dados, arquivos CSV e JSON. Permite mapear campos, aplicar transformacoes, definir valores padrao e gerar relatorios detalhados.
| Componente | Descricao |
|---|---|
| TSimpleDataMigration | Classe principal com API fluent para configurar e executar migracoes |
| TFieldMap | Mapeamento de campos com Field, Transform, DefaultValue, Lookup e Ignore |
| TFieldTransform | Transformacoes built-in para manipulacao de dados durante migracao |
| TMigrationReport | Relatorio com TotalRecords, Migrated, Failed, Skipped, ToJSON e ToCSV |
| TTableReport | Relatorio por tabela com lista de erros detalhada |
| TCSVReader / TCSVWriter | Leitor e escritor de CSV com suporte a headers |
Migracao DB-to-DB
uses SimpleDataMigration;
LMigration := TSimpleDataMigration.New
.Source(LQueryOrigem)
.Target(LQueryDestino)
.Map('CLIENTES_ANTIGO', 'CLIENTES')
.Field('NOME_COMPLETO', 'NOME')
.Transform('NOME_COMPLETO', 'NOME', TFieldTransform.Upper)
.DefaultValue('STATUS', '1')
.Field('CPF_CNPJ', 'DOCUMENTO')
.Transform('CPF_CNPJ', 'DOCUMENTO', TFieldTransform.Trim)
.Ignore('CAMPO_OBSOLETO')
.&End
.OnProgress(procedure(aTable: String; aCurrent, aTotal: Integer)
begin
Writeln(SysUtils.Format('%s: %d/%d', [aTable, aCurrent, aTotal]));
end)
.OnError(procedure(aError: TMigrationError; var aSkip: Boolean)
begin
Writeln(SysUtils.Format('Erro em %s registro %d: %s',
[aError.SourceTable, aError.RecordIndex, aError.ErrorMessage]));
aSkip := True;
end);
LReport := LMigration.Execute;
Migracao CSV-to-DB
LMigration := TSimpleDataMigration.New
.Source('clientes_export.csv', mfCSV)
.Target(LQueryDestino)
.Map('', 'CLIENTES')
.Field('nome', 'NOME')
.Transform('nome', 'NOME', TFieldTransform.Upper)
.Field('email', 'EMAIL')
.Transform('email', 'EMAIL', TFieldTransform.Lower)
.DefaultValue('DATA_IMPORT', '2026-03-10')
.&End;
LReport := LMigration.Execute;
Transformacoes Built-in
| Transformacao | Descricao | Exemplo |
|---|---|---|
TFieldTransform.Upper | Converte para maiusculas | 'joao' → 'JOAO' |
TFieldTransform.Lower | Converte para minusculas | 'JOAO' → 'joao' |
TFieldTransform.Trim | Remove espacos no inicio e fim | ' joao ' → 'joao' |
TFieldTransform.Replace('a', 'b') | Substitui substring | 'abc' → 'bbc' |
TFieldTransform.DateFormat('dd/mm/yyyy', 'yyyy-mm-dd') | Converte formato de data | '10/03/2026' → '2026-03-10' |
TFieldTransform.Split(';', 0) | Divide string e retorna parte N | 'a;b;c' → 'a' |
TFieldTransform.Concat(' - ') | Concatena campos com separador | 'campo1' + ' - ' + 'campo2' |
TFieldTransform.Custom(func) | Funcao customizada de transformacao | Qualquer logica personalizada |
Persistencia de Mapeamentos
// Salvar mapeamento para reutilizar
LMigration.SaveToJSON('migracao_clientes.json');
// Carregar mapeamento salvo
LMigration := TSimpleDataMigration.New
.LoadFromJSON('migracao_clientes.json')
.Source(LQueryOrigem)
.Target(LQueryDestino);
LReport := LMigration.Execute;
Relatorio de Migracao
LReport := LMigration.Execute;
try
Writeln('Total: ', LReport.TotalRecords);
Writeln('Migrados: ', LReport.Migrated);
Writeln('Falhas: ', LReport.Failed);
Writeln('Ignorados: ', LReport.Skipped);
Writeln('Duracao: ', LReport.DurationMs, 'ms');
// Exportar relatorio
LJSON := LReport.ToJSON; // retorna TJSONObject (caller deve liberar)
try
Writeln(LJSON.ToString);
finally
LJSON.Free;
end;
LCSVContent := LReport.ToCSV; // retorna String
// Detalhes por tabela
for LTable in LReport.Tables do
begin
Writeln(LTable.SourceTable, ' -> ', LTable.TargetTable, ': ',
LTable.Migrated, ' migrados, ', LTable.Failed, ' falhas');
for LError in LTable.Errors do
Writeln(' Registro ', LError.RecordIndex, ' [', LError.FieldName, ']: ',
LError.ErrorMessage);
end;
finally
LReport.Free;
end;
Nota: A migracao usa transacoes automaticas por tabela. Em caso de erro, o rollback e feito apenas na tabela atual, preservando as tabelas ja migradas com sucesso.