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

Todos os Atributos

Todos os atributos ficam em SimpleAttributes.pas.

Mapeamento

AtributoAlvoDescricao
Tabela('NOME')ClasseMapeia classe para tabela do banco
Campo('NOME')PropertyMapeia property para coluna do banco

Chaves e Auto-Incremento

AtributoDescricao
PKChave primaria (exatamente uma por entidade)
FKChave estrangeira
AutoIncAuto-incremento (excluido do INSERT automaticamente)

Restricoes

AtributoDescricao
NotNullCampo obrigatorio (string nao vazia, data nao zero)
NotZeroValor numerico nao pode ser zero
IgnoreIgnora a property em todas operacoes SQL e serializacao
NumberOnlyAceita somente numeros

Validacao

AtributoDescricao
EmailValida 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

AtributoDescricao
Display('Label')Nome para exibicao em grids/forms
Bind('CAMPO')Liga componente visual a property da entidade

Relacionamentos

AtributoCardinalidadeCarregamento
HasOne('Entity', 'FK')1:1Eager (automatico no Find)
BelongsTo('Entity', 'FK')N:1Eager (automatico no Find)
HasMany('Entity', 'FK')1:NLazy (TSimpleLazyLoader)
BelongsToMany('Entity', 'FK')M:NManual

Especiais

AtributoDescricao
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);
BancoSQL GeradoPosicao
FirebirdSELECT FIRST 20 SKIP 10 ...Apos SELECT
MySQL... LIMIT 20 OFFSET 10Fim da query
SQLite... LIMIT 20 OFFSET 10Fim da query
Oracle... OFFSET 10 ROWS FETCH NEXT 20 ROWS ONLYFim 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

AtributoO que valida
NotNullString nao vazia, data nao zero
NotZeroNumerico diferente de zero
EmailFormato 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

OperacaoSQL Gerado
Delete(entity)UPDATE PRODUTO SET DELETADO = 1 WHERE ID = :ID
FindSELECT ... 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;
MetodoDescricao
StartTransactionInicia transacao (verifica se ja esta ativa)
CommitConfirma transacao ativa
RollbackDesfaz transacao ativa
InTransactionRetorna True se transacao esta ativa
&EndTransactionAlias 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.

DriverClasseConexaoUnit
FireDACTSimpleQueryFiredacTFDConnectionSimpleQueryFiredac.pas
UniDACTSimpleQueryUnidacTUniConnectionSimpleQueryUnidac.pas
ZeosTSimpleQueryZeosTZConnectionSimpleQueryZeos.pas
RestDWTSimpleQueryRestDWTRESTDWClientSQLSimpleQueryRestDW.pas
Horse (REST)TSimpleQueryHorseURL stringSimpleQueryHorse.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

MetodoRotaStatusResposta
GET/pedido?skip=N&take=N200{"data": [...], "count": N}
GET/pedido/:id200/404JSON da entidade ou {"error": "Not found"}
POST/pedido201Entidade criada em JSON
PUT/pedido/:id200Entidade atualizada em JSON
DELETE/pedido/:id204Sem 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

DelphiJSON
Stringstring
Integernumber
Int64number
Double/Currencynumber
TDateTimeISO8601 string
Booleantrue/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>

MetodoRetornoDescricao
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)
FindiSimpleDAO<T>Busca todos (popula DataSource)
Find(var aList)iSimpleDAO<T>Busca todos em lista de objetos
Find(aId: Integer)TBusca 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
LastIDiSimpleDAO<T>Busca ultimo ID inserido
LastRecordiSimpleDAO<T>Busca ultimo registro
SQLiSimpleDAOSQLAttribute<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>

MetodoDescricao
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)
ClearLimpa todos os filtros
&EndRetorna ao iSimpleDAO<T>

iSimpleQuery

MetodoRetornoDescricao
SQLTStringsTexto SQL
ParamsTParamsParametros da query
ExecSQLiSimpleQueryExecuta INSERT/UPDATE/DELETE
DataSetTDataSetResultado do SELECT
Open(aSQL)iSimpleQueryDefine SQL e abre
OpeniSimpleQueryAbre com SQL ja definido
StartTransactioniSimpleQueryInicia transacao
CommitiSimpleQueryConfirma transacao
RollbackiSimpleQueryDesfaz transacao
&EndTransactioniSimpleQueryAlias para Commit
InTransactionBooleanTrue se transacao ativa
SQLTypeTSQLTypeTipo 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

UnitTestesO que cobre
SimpleAttributes22Construtores, propriedades, GetNumericMask, relacionamentos
SimpleRTTIHelper26IsNotNull, IsAutoInc, FieldName, DisplayName, EhChavePrimaria, SoftDelete, ValueHelper
SimpleSQL20INSERT, UPDATE, DELETE, SELECT, paginacao (Firebird/MySQL/SQLite/Oracle), SoftDelete
SimpleValidator21NotNull, NotZero, Format, Email, MinValue, MaxValue, Regex, exception
SimpleSerializer10EntityToJSON, 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.
Regra: Toda nova feature implementada no SimpleORM DEVE incluir testes unitarios DUnit. Adicione a unit de teste no 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

AtributoParametrosDescricao
AIGeneratedpromptTemplate: StringGera conteudo via LLM. Template suporta {PropertyName} para interpolacao
AISummarizesourceProperty: String, maxLength: IntegerResume o conteudo de outra propriedade
AITranslatesourceProperty: String, targetLanguage: StringTraduz o conteudo de outra propriedade
AIClassifysourceProperty: String, categories: StringClassifica conteudo em categorias separadas por virgula
AIValidaterule: String, errorMessage: StringValida 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

ProviderModelo padraoUso
claudeclaude-sonnet-4-20250514TSimpleAIClient.New('claude', 'api-key')
openaigpt-4o-miniTSimpleAIClient.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

MetodoInputOutputDescricao
NaturalLanguageQuerypergunta: StringTDataSetTraduz para SQL, executa, retorna resultado
AskQuestionpergunta: StringStringTraduz, executa, LLM responde em linguagem natural
ExplainQuerysql: StringStringExplica SQL em linguagem natural
SuggestQueryobjetivo: StringStringSugere 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

Documentacao completa do MCP Server →

Rules (Regras) NEW

Regras declarativas na entidade que executam automaticamente durante CRUD. Podem ser deterministicas (expressoes Delphi) ou inteligentes (via AI).

Atributos

AtributoParametrosDescricao
Ruleexpression, action, messageRegra deterministica avaliada contra propriedades da entidade
AIRuledescription, actionRegra 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.

SkillDescricao
TSkillLogLoga operacao via Logger ou OutputDebugString/Writeln
TSkillNotifyDispara callback TProc<TObject>
TSkillAuditInsere registro em tabela de auditoria
TSkillTimestampPreenche campos de data automaticamente
TSkillValidateChama TSimpleValidator.Validate automaticamente
TSkillGuardDeleteBloqueia delete com registros dependentes
TSkillHistoryGrava snapshot antes de update/delete
TSkillWebhookHTTP 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.

SkillDescricao
TSkillSequenceNumeracao sequencial via tabela de controle
TSkillCalcTotalCalcula total: Qtd * Preco - Desconto
TSkillStockMoveRegistra movimentacao de estoque
TSkillDuplicateGera parcelas financeiras

Skills AI (SimpleAISkill.pas)

7 Skills baseadas em IA para enriquecimento, validacao/moderacao e analise de sentimento via LLM.

SkillDescricao
TSkillAIEnrichGera conteudo via prompt template com {PropertyName}
TSkillAITranslateTraducao automatica entre campos
TSkillAISummarizeResumo automatico de texto
TSkillAITagsGeracao automatica de tags/keywords
TSkillAIModerateModeracao de conteudo com bloqueio
TSkillAIValidateValidacao com regra em linguagem natural
TSkillAISentimentAnalise 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:

ComponenteUnitDescricao
TSimpleQuerySupabaseSimpleQuerySupabaseDriver CRUD implementando iSimpleQuery — INSERT, UPDATE, DELETE, SELECT via REST
TSimpleSupabaseAuthSimpleSupabaseAuthAutenticacao — SignIn, SignUp, SignOut, JWT auto-refresh, Row Level Security
TSimpleSupabaseRealtimeSimpleSupabaseRealtimeMonitoramento 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.

ComponenteDescricao
TSkillGitHubIssueSkill que cria Issues via GitHub REST API — fire-and-forget
TSkillRunModeEnum srmNormal (auditoria) ou srmOnError (falhas)
OnErrorCallback 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

PlaceholderDescricao
{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.

ComponenteDescricao
TSimpleDataMigrationClasse principal com API fluent para configurar e executar migracoes
TFieldMapMapeamento de campos com Field, Transform, DefaultValue, Lookup e Ignore
TFieldTransformTransformacoes built-in para manipulacao de dados durante migracao
TMigrationReportRelatorio com TotalRecords, Migrated, Failed, Skipped, ToJSON e ToCSV
TTableReportRelatorio por tabela com lista de erros detalhada
TCSVReader / TCSVWriterLeitor 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

TransformacaoDescricaoExemplo
TFieldTransform.UpperConverte para maiusculas'joao''JOAO'
TFieldTransform.LowerConverte para minusculas'JOAO''joao'
TFieldTransform.TrimRemove 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 transformacaoQualquer 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.