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.
Exists NEW
Verifica se um registro existe no banco de dados sem carregar o objeto completo. Retorna Boolean diretamente, otimizado com SELECT COUNT.
Uso basico
uses SimpleDAO, SimpleInterface;
var LDAO := TSimpleDAO<TCliente>.New(LQuery);
// Verificar por campo e valor
if LDAO.Exists('EMAIL', 'usuario@email.com') then
Writeln('Email ja cadastrado');
// Verificar por PK
if LDAO.Exists('ID', 42) then
Writeln('Cliente com ID 42 existe');
// Verificar com multiplos criterios via SQL
if LDAO.SQL
.Where('CPF = :pCPF')
.Where('ATIVO = :pAtivo')
.&End
.Exists then
Writeln('Cliente ativo com este CPF existe');
| Metodo | Parametros | Retorno | Descricao |
|---|---|---|---|
Exists(aField, aValue) | Nome do campo, valor a buscar | Boolean | Verifica existencia por campo/valor |
Exists | Nenhum (usa WHERE do SQL builder) | Boolean | Verifica existencia com filtros do SQL fluent |
Nota: Internamente usa SELECT COUNT(*) com LIMIT 1 para performance maxima. Nao carrega dados da entidade.
Automapping NEW
Convencao sobre configuracao: com o atributo [Automapping], o SimpleORM infere automaticamente o nome da tabela e das colunas a partir dos nomes da classe e propriedades, eliminando a necessidade de [Tabela] e [Campo] em cada property.
Convencoes
| Elemento | Convencao | Exemplo |
|---|---|---|
| Tabela | Nome da classe sem prefixo T, uppercase | TCliente → CLIENTE |
| Coluna | Nome da property, uppercase | NomeCompleto → NOMECOMPLETO |
| PK | Property chamada Id e automaticamente PK + AutoInc | property Id → [PK][AutoInc] |
| FK | Property com sufixo Id e tipo inteiro | CategoriaId → [FK] |
Exemplo
uses SimpleAttributes;
type
[Automapping]
TProduto = class
private
FId: Integer;
FNome: String;
FPreco: Double;
FCategoriaId: Integer;
published
property Id: Integer read FId write FId; // PK + AutoInc automatico
property Nome: String read FNome write FNome; // Campo NOME
property Preco: Double read FPreco write FPreco; // Campo PRECO
property CategoriaId: Integer read FCategoriaId write FCategoriaId; // FK automatico
end;
// Equivalente a escrever manualmente:
// [Tabela('PRODUTO')]
// [Campo('ID')][PK][AutoInc] property Id
// [Campo('NOME')] property Nome
// [Campo('PRECO')] property Preco
// [Campo('CATEGORIAID')][FK] property CategoriaId
Nota: Atributos explicitos ([Campo], [PK], etc.) tem prioridade sobre Automapping. Voce pode usar [Automapping] na classe e ainda sobrescrever campos individuais com atributos explicitos.
Event Bus NEW
Sistema de eventos publish/subscribe que permite observar operacoes CRUD globalmente ou por entidade especifica. Desacoplamento total entre produtor e consumidor de eventos.
Tipos de evento
| Evento | Descricao | Momento |
|---|---|---|
seBeforeInsert | Antes de inserir | Pre-INSERT |
seAfterInsert | Apos inserir com sucesso | Pos-INSERT |
seBeforeUpdate | Antes de atualizar | Pre-UPDATE |
seAfterUpdate | Apos atualizar com sucesso | Pos-UPDATE |
seBeforeDelete | Antes de deletar | Pre-DELETE |
seAfterDelete | Apos deletar com sucesso | Pos-DELETE |
seOnError | Quando ocorre erro em qualquer operacao | Exception |
Exemplo
uses SimpleEventBus;
// Observer global — recebe eventos de TODAS as entidades
TSimpleEventBus.Subscribe(seAfterInsert,
procedure(aEntity: TObject; aEvent: TSimpleEvent)
begin
Writeln('Inserido: ', aEntity.ClassName);
end);
// Observer especifico — apenas para TProduto
TSimpleEventBus.Subscribe(seAfterInsert, TProduto,
procedure(aEntity: TObject; aEvent: TSimpleEvent)
begin
var LProduto := TProduto(aEntity);
Writeln('Produto inserido: ', LProduto.Nome);
end);
// Observer de erro
TSimpleEventBus.Subscribe(seOnError,
procedure(aEntity: TObject; aEvent: TSimpleEvent)
begin
Writeln('Erro em ', aEntity.ClassName, ': ', aEvent.ErrorMessage);
end);
// Remover observers
TSimpleEventBus.Unsubscribe(seAfterInsert); // remove global
TSimpleEventBus.Unsubscribe(seAfterInsert, TProduto); // remove especifico
TSimpleEventBus.Clear; // remove todos
Nota: O Event Bus e thread-safe e fire-and-forget. Excecoes nos observers sao capturadas e logadas via iSimpleQueryLogger, nunca interrompendo o fluxo CRUD principal.
Bulk Insert NEW
Insercao em massa otimizada que gera um unico SQL com multiplos VALUES, significativamente mais rapido que InsertBatch (que executa INSERT individual para cada registro).
Exemplo
uses SimpleDAO, SimpleInterface;
var
LDAO: iSimpleDAO<TProduto>;
LProdutos: TObjectList<TProduto>;
begin
LProdutos := TObjectList<TProduto>.Create;
try
// Criar 1000 produtos
for var I := 1 to 1000 do
begin
var LProd := TProduto.Create;
LProd.Nome := 'Produto ' + IntToStr(I);
LProd.Preco := Random * 100;
LProdutos.Add(LProd);
end;
LDAO := TSimpleDAO<TProduto>.New(LQuery);
// Bulk Insert — 1 SQL com 1000 VALUES
LDAO.BulkInsert(LProdutos);
// vs InsertBatch — 1000 SQLs individuais (mais lento)
// LDAO.InsertBatch(LProdutos);
finally
LProdutos.Free;
end;
end;
| Metodo | SQL Gerado | Performance |
|---|---|---|
BulkInsert(aList) | INSERT INTO T (A,B) VALUES (1,'x'),(2,'y'),(3,'z') | Rapido — 1 roundtrip |
InsertBatch(aList) | INSERT INTO T (A,B) VALUES (:A,:B) x N vezes | Lento — N roundtrips |
Nota: BulkInsert usa transacao automatica. Campos [AutoInc] sao excluidos. Para Firebird, a sintaxe INSERT INTO ... SELECT ... FROM RDB$DATABASE UNION ALL e usada automaticamente.
FindAs (DTO) NEW
Permite executar SQL customizado e mapear o resultado para uma classe DTO (Data Transfer Object) que nao precisa ser uma entidade mapeada com [Tabela]. Ideal para relatorios, agregacoes e consultas complexas.
Exemplo
uses SimpleDAO, SimpleInterface;
type
// DTO simples — nao precisa de [Tabela] nem [Campo]
TRelatorioProduto = class
private
FCATEGORIA: String;
FQUANTIDADE: Integer;
FTOTAL: Double;
published
property CATEGORIA: String read FCATEGORIA write FCATEGORIA;
property QUANTIDADE: Integer read FQUANTIDADE write FQUANTIDADE;
property TOTAL: Double read FTOTAL write FTOTAL;
end;
var
LDAO: iSimpleDAO<TProduto>;
LLista: TObjectList<TRelatorioProduto>;
begin
LDAO := TSimpleDAO<TProduto>.New(LQuery);
LLista := TObjectList<TRelatorioProduto>.Create;
try
LDAO.RawSQL(
'SELECT CATEGORIA, COUNT(*) AS QUANTIDADE, SUM(PRECO) AS TOTAL ' +
'FROM PRODUTOS GROUP BY CATEGORIA ORDER BY TOTAL DESC'
).FindAs<TRelatorioProduto>(LLista);
for var LItem in LLista do
Writeln(LItem.CATEGORIA, ': ', LItem.QUANTIDADE, ' itens, R$ ',
FormatFloat('#,##0.00', LItem.TOTAL));
finally
LLista.Free;
end;
end;
| Metodo | Descricao |
|---|---|
RawSQL(aSQL) | Define SQL customizado para execucao |
FindAs<T>(aList) | Executa o SQL e mapeia resultados para a lista de DTOs |
Nota: O mapeamento e feito por nome: o alias do campo no SELECT deve corresponder ao nome da published property do DTO. Properties nao encontradas no DataSet sao ignoradas.
Natural Language Query NEW
Consulte seu banco de dados usando linguagem natural em portugues ou ingles. O SimpleORM converte a pergunta em SQL usando AI, executa com seguranca e retorna o resultado formatado.
Via DAO (modo simples)
uses SimpleDAO, SimpleAIClient;
var LAI := TSimpleAIClient.New('sua-api-key', 'gpt-4');
var LDAO := TSimpleDAO<TProduto>.New(LQuery);
// Pergunta direta — retorna texto formatado
var LResposta := LDAO.AIClient(LAI).Ask('quais os 5 produtos mais caros?');
Writeln(LResposta);
// Resultado:
// | NOME | PRECO |
// |----------------|----------|
// | Notebook Pro | 8999.90 |
// | iPhone 15 | 7499.00 |
// ...
Via TSimpleNLQuery (modo avancado)
uses SimpleNLQuery, SimpleAIClient;
var LNLQuery := TSimpleNLQuery.New(LQuery, LAI)
.RegisterEntity(TypeInfo(TProduto))
.RegisterEntity(TypeInfo(TCliente))
.RegisterEntity(TypeInfo(TPedido))
.MaxRows(50);
// Pergunta com contexto de multiplas entidades
var LResposta := LNLQuery.Ask('clientes que mais compraram no ultimo mes');
Writeln(LResposta);
// Obter o SQL gerado (para debug)
Writeln('SQL: ', LNLQuery.LastSQL);
| Metodo | Descricao |
|---|---|
AIClient(aAI) | Configura o cliente AI no DAO para consultas NL |
Ask(aPergunta) | Envia pergunta em linguagem natural e retorna resposta formatada |
RegisterEntity(TypeInfo) | Registra entidade para contexto de schema (NLQuery) |
MaxRows(N) | Limita quantidade de resultados |
LastSQL | Retorna o ultimo SQL gerado pela AI |
Seguranca: Apenas SELECT e permitido. Keywords perigosas (INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, EXEC, GRANT) sao bloqueadas automaticamente antes da execucao.
Auto Swagger NEW
Gera documentacao Swagger/OpenAPI 3.0 automaticamente a partir das entidades registradas no TSimpleHorseRouter. Zero configuracao manual — basta habilitar e acessar /swagger.json.
Exemplo
uses SimpleHorseRouter, SimpleQueryFiredac;
var LQuery := TSimpleQueryFiredac.New(LConn);
// Registrar entidades normalmente
TSimpleHorseRouter.RegisterEntity<TProduto>(THorse, LQuery);
TSimpleHorseRouter.RegisterEntity<TCliente>(THorse, LQuery);
TSimpleHorseRouter.RegisterEntity<TPedido>(THorse, LQuery);
// Habilitar Swagger — gera GET /swagger.json automaticamente
TSimpleHorseRouter.EnableSwagger(THorse);
// Customizar metadados (opcional)
TSimpleHorseRouter.EnableSwagger(THorse)
.Title('Minha API')
.Version('1.0.0')
.Description('API gerada pelo SimpleORM');
THorse.Listen(9000);
// Acesse: http://localhost:9000/swagger.json
O que e gerado
- Paths para cada entidade: GET (list + by id), POST, PUT, DELETE
- Schemas com propriedades baseadas nos atributos
[Campo] - Parametros de paginacao (
skip,take) em endpoints GET - Tipos de dados inferidos das properties Delphi (string, integer, number, boolean)
- Respostas com status codes (200, 201, 204, 400, 404, 500)
Nota: O JSON gerado e compativel com Swagger UI, Postman e qualquer ferramenta OpenAPI 3.0. Nenhum JavaScript e adicionado ao servidor — apenas o endpoint JSON.
Smart Seeder NEW
Gera dados de teste realistas usando inteligencia artificial. O Seeder analisa a estrutura da entidade (tipos, nomes de campos, validacoes) e solicita a AI para gerar dados coerentes e contextualizados.
Exemplo
uses SimpleSeeder, SimpleAIClient;
var LAI := TSimpleAIClient.New('sua-api-key', 'gpt-4');
var LSeeder := TSimpleSeeder.New(LAI);
// Gerar 50 produtos realistas e inserir no banco
LSeeder.Query(LQuery).Seed<TProduto>(50);
// Gerar sem inserir — retorna lista para inspecao
var LProdutos := LSeeder.Generate<TProduto>(10);
try
for var LProd in LProdutos do
Writeln(LProd.Nome, ' - R$ ', FormatFloat('#,##0.00', LProd.Preco));
finally
LProdutos.Free;
end;
// Seed com contexto customizado
LSeeder.Context('Loja de eletronicos no Brasil, precos em Reais')
.Query(LQuery)
.Seed<TProduto>(100);
| Metodo | Descricao |
|---|---|
TSimpleSeeder.New(aAI) | Cria instancia com cliente AI |
Query(aQuery) | Define conexao para persistir dados gerados |
Context(aText) | Adiciona contexto de negocio para dados mais realistas |
Seed<T>(aCount) | Gera e insere N registros no banco |
Generate<T>(aCount) | Gera N registros e retorna lista (sem persistir) |
Nota: Respeita validacoes da entidade ([NotNull], [Format], [MinValue], [MaxValue], [Email]). Campos [AutoInc] sao ignorados. Usa BulkInsert quando disponivel para performance.
AI Auto-Index NEW
Analisa a estrutura das entidades e o historico de queries para sugerir indices de banco de dados que melhoram a performance. Usa AI para identificar padroes de acesso e recomendar indices compostos.
Exemplo
uses SimpleAutoIndex, SimpleAIClient;
var LAI := TSimpleAIClient.New('sua-api-key', 'gpt-4');
var LAnalyzer := TSimpleAutoIndex.New(LAI)
.RegisterEntity(TypeInfo(TProduto))
.RegisterEntity(TypeInfo(TCliente))
.RegisterEntity(TypeInfo(TPedido))
.SQLType(TSQLType.MySQL);
// Analisar e obter sugestoes
var LSugestoes := LAnalyzer.Analyze;
try
for var LSugestao in LSugestoes do
begin
Writeln('Tabela: ', LSugestao.TableName);
Writeln('Indice: ', LSugestao.IndexName);
Writeln('Campos: ', LSugestao.Fields);
Writeln('SQL: ', LSugestao.CreateSQL);
Writeln('Motivo: ', LSugestao.Reason);
Writeln('---');
end;
finally
LSugestoes.Free;
end;
// Aplicar indices automaticamente (com confirmacao)
LAnalyzer.Apply(LQuery); // executa CREATE INDEX para cada sugestao
| Metodo | Descricao |
|---|---|
RegisterEntity(TypeInfo) | Registra entidade para analise de estrutura |
SQLType(aType) | Define dialeto SQL para gerar DDL correto |
Analyze | Retorna lista de sugestoes de indices |
Apply(aQuery) | Executa CREATE INDEX das sugestoes no banco |
Nota: Analisa automaticamente: campos PK/FK, campos usados em WHERE (via historico do Logger), relacionamentos HasOne/BelongsTo, e campos com alta cardinalidade.
AI Query Optimizer NEW
Otimiza queries SQL usando inteligencia artificial. Analisa o SQL, identifica problemas de performance e sugere reescrita otimizada, mantendo compatibilidade com o dialeto do banco.
Uso direto
uses SimpleQueryOptimizer, SimpleAIClient;
var LAI := TSimpleAIClient.New('sua-api-key', 'gpt-4');
var LOptimizer := TSimpleQueryOptimizer.New(LAI);
var LResultado := LOptimizer.Optimize(
'SELECT * FROM PEDIDOS WHERE CLIENTE_ID IN (SELECT ID FROM CLIENTES WHERE CIDADE = ''SP'')',
TSQLType.MySQL
);
Writeln('SQL Original: ', LResultado.OriginalSQL);
Writeln('SQL Otimizado: ', LResultado.OptimizedSQL);
Writeln('Explicacao: ', LResultado.Explanation);
Writeln('Melhoria estimada: ', LResultado.EstimatedImprovement);
Via Skill no DAO
uses SimpleSkill, SimpleQueryOptimizer;
// Skill que otimiza automaticamente antes de executar
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillQueryOptimizer.New(LAI));
// Todas as queries passam pelo optimizer antes da execucao
LDAO.Find; // SQL e otimizado transparentemente
| Metodo | Descricao |
|---|---|
Optimize(aSQL, aSQLType) | Analisa e retorna SQL otimizado |
TSkillQueryOptimizer.New(aAI) | Skill para otimizacao automatica no pipeline do DAO |
Nota: O optimizer sugere JOINs em vez de subqueries, eliminacao de SELECT *, uso de indices, e reescrita de clausulas WHERE para melhor seletividade.
Telegram/Discord NEW
Skills para enviar notificacoes automaticas para Telegram e Discord quando operacoes CRUD ocorrem. Ideal para monitoramento, alertas e auditoria em tempo real.
Telegram
uses SimpleSkill, SimpleTypes;
// Notificar no Telegram apos cada Insert
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillTelegram.New(
'bot_token_123456', // Token do BotFather
'-1001234567890', // Chat ID (grupo ou usuario)
srAfterInsert
));
// Com template customizado
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillTelegram.New(
'bot_token_123456',
'-1001234567890',
srAfterInsert
).MessageTemplate('Novo produto cadastrado: {entity} em {timestamp}'));
Discord
uses SimpleSkill, SimpleTypes;
// Notificar no Discord via Webhook apos erros
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillDiscord.New(
'https://discord.com/api/webhooks/123/abc', // Webhook URL
srAfterInsert
));
// Com modo OnError — notifica apenas falhas
LDAO := TSimpleDAO<TProduto>.New(LQuery)
.Skill(TSkillDiscord.New(
'https://discord.com/api/webhooks/123/abc',
srAfterInsert,
srmOnError
));
| Skill | Parametros | Descricao |
|---|---|---|
TSkillTelegram | Token, ChatId, RunPoint | Envia mensagem via Telegram Bot API |
TSkillDiscord | WebhookURL, RunPoint [, RunMode] | Envia mensagem via Discord Webhook |
Nota: Ambas as skills sao fire-and-forget — erros de envio sao logados mas nunca interrompem o fluxo CRUD. Suportam os mesmos placeholders de template: {entity}, {operation}, {error}, {timestamp}.
Google Sheets Export NEW
Exporta listas de entidades diretamente para uma planilha do Google Sheets. Headers sao gerados automaticamente a partir dos atributos [Campo] e os dados sao escritos via Google Sheets API v4.
Exemplo
uses SimpleExportSheets;
var LExporter := TSimpleExportSheets.New('seu-oauth-token')
.SpreadsheetId('1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgVE2upms')
.Sheet('Produtos');
// Exportar lista de entidades
var LProdutos: TObjectList<TProduto>;
LDAO.Find(LProdutos);
LExporter.Export<TProduto>(LProdutos);
// Exportar com opcoes
LExporter
.ClearBefore(True) // Limpar planilha antes de exportar
.IncludeHeaders(True) // Incluir headers (padrao: True)
.StartCell('A1') // Celula inicial
.Export<TProduto>(LProdutos);
| Metodo | Descricao |
|---|---|
TSimpleExportSheets.New(aToken) | Cria instancia com OAuth2 token do Google |
SpreadsheetId(aId) | ID da planilha Google Sheets |
Sheet(aName) | Nome da aba (sheet) destino |
ClearBefore(aValue) | Limpar dados existentes antes de exportar |
IncludeHeaders(aValue) | Incluir linha de headers com nomes dos campos |
StartCell(aCell) | Celula inicial para escrita (ex: 'A1', 'B3') |
Export<T>(aList) | Exporta lista de entidades para a planilha |
Nota: Requer OAuth2 token com escopo https://www.googleapis.com/auth/spreadsheets. Headers sao gerados a partir de [Campo] ou [Display] quando disponivel. Campos [Ignore] sao excluidos da exportacao.