TMS Aurelius – Novo ORM para Delphi

January 18, 2012

Antigamente, quando o Delphi estava a anos-luz de outras plataformas (hoje felizmente a distancia já diminuiu para alguns meses-luz) eu sempre dizia que primeiro a Embarcadero tinha que implementar coisas como RTTI e generics para que só então o Delphi começasse a ganhar alguns brinquedos legais que existem por exemplo em Java e .Net, “brinquedos” como bons frameworks ORM que não interfiram demasiadamente no design das classes de domínio. Dito e feito, hoje temos o DORM que já foi objeto de um post aqui e, mais recentemente, o TMS Aurelius.

Licenciamento e instalação

Infelizmente o Aurelius é uma ferramenta comercial, diferentemente do DORM, mas achei o seu custo bem acessível. Uma licença para um desenvolvedor sai por 156 euros até o dia 29/02, que na cotação de hoje (18/01/2011) dá aproximadamente R$ 356,00

Além disso, também é possível baixar um trial para experimentar. No trial a maioria das units não vem com os fontes, mas todas as funcionalidades do framework estão disponíveis. Sobre quanto por quanto tempo o trial permanece funcional, não encontrei nada a respeito.

A instalação é muito simples, é só baixar e executar um instalador NNF e assunto encerrado.

Documentação

Em relação à documentação, o Aurelius parece ser muito bem documentado. O pacote acompanha um PDF de 90 páginas que parece cobrir com um bom nível de detalhamento cada aspecto do framework.

Conectividade

Nesta versão, o Aurelius pode se conectar com DB2, Firebird, Interbase, SQL Server, MySQL, NexusDB, Oracle, PostgreSQL e SQLite, através de diferentes suites de componentes, como por exemplo AnyDac, dbExpress, SQLDirect, etc. Além disso, a documentação alega que o framework pode ser facilmente extendido para implementar suporte a outros SGBD e/ou componentes de acesso.

Um detalhe que achei bem interessante é que a documentação do Aurelius lista exatamente quais versões de SGBD e dos componentes de acesso foram utilizadas nos testes, bem como quais combinações entre eles. É apenas um detalhe, mas me passou uma boa sensação de profissionalismo por tras do framework.

Classes de domínio e mapeamento

Como eu já disse na introdução, o Aurelius interfere bem pouco no design das suas classes.
Quando falo sobre interferir no design das classes, me refiro a quando um framework exige que as suas classes herdem de alguma classe ou implementem alguma interface do framework, que as propriedades persistentes sejam todas published, ou que ao invés de usar os tipos pre-definidos do Delphi você tenha que utilizar o próprio sistema de tipos do framework.

Por exemplo, esta classe está pronta para o Aurelius:

type
  [Entity]
  [Automapping]
  TPerson = class
  private
    FId: integer;
    FLastName: string;
    FFirstName: string;
    FEmail: string;
  public
    property Id: integer read FId;
    property LastName: string read FLastName write FLastName;
    property FirstName: string read FFirstName write FFirstName;
    property Email: string read FEmail write FEmail;
  end;

O atributo Automapping é o que diz ao framework para utilizar convenções de mapeamento naquela classe, mas na minha opinião o comportamento deveria ser o contrário: O mapeamento por convenções ficaria sempre habilitado e, caso o programador por algum motivo não queira isso, então adiciona um atributo como por exemplo [NoAutomapping]. Mas isso é um detalhe menor.

De qualquer forma, como a documentação deixa bem claro, o mapeamento por convenções não é “tudo ou nada”, você pode usar o [Automapping] e ainda assim mapear algumas coisas explicitamente, sobreescrevendo as convenções.

O mapeamento manual, isto é, sem utilizar as convenções, pelo que entendi só pode ser feito via atributos. Ou seja, até onde pude ver não há mapeamento por arquivo externo como acontece no DORM ou no Hibernate. Eu provavelmente não usaria este recurso, mas ainda assim acho que é algo importante de se ter. Algumas pessoas preferem não utilizar atributos para não poluir o código, para que as classes de domínio fiquem menos acopladas ao ORM, ou por terem algum outro impedimento.

Especialmente em se tratando do Delphi, há ainda um outro problema: É que devido á forma como as DCU são geradas e tratadas internamente, se por um acaso você alterar qualquer coisa na interface de uma unit e recompila-la, então você precisará recompilar qualquer unit que dependa desta (Este é o motivo por que componentes compilados para uma versão do Delphi não servem em outra versão). Então modificar uma unit para adicionar os atributos de mapeamento pode gerar um pouco de transtorno em alguns casos mais específicos.

Herança

O Aurelius suporta o mapeamento de herança e queries polimórficas. A herança pode ser mapeada de duas formas:

  • Em uma única tabela. Neste caso, todos os campos de todas as subclasses ficam mapeados numa mesma tabela, e há ainda uma coluna extra que permite especificar qual a classe correspondente a cada registro desta tabela.
  • Com tabelas específicas para as subclasses, realizando joins quando necessário

ID

Ao que me parece, o Aurelius não tem muitas restrições sobre o identificador dos objetos (a chave primária para o mundo relacional), os campos podem ser de diversos tipos e você pode ter mais de um campo como id na classe, para o caso de chaves compostas.

Ele pode ainda tratar chaves sequenciais automaticamente, utilizando sequences/generators ou campos auto-incremento dependendo do SGBD, mas pelo que observei esta é a única opção para geração automática de identificadores. E também não vi nenhum meio para que eu possa implementar meu próprio gerador de identificadores. No projeto que estou trabalhando atualmente, tenho tabelas cuja chave é um GUID, então neste caso eu mesmo terei que gerar e atribuir o id manualmente sempre que for persistir um objeto para estas tabelas.

Blob

Campos blob são suportados utilizando o tipo TBlob ou TArray nas classes. Acho que TStream seria uma boa opção aqui, possui várias vantagens sobre um TArray e evita que as classes de domínio tenham mais um ponto de acoplamento (TBlob) com o ORM.

Acredito que deve ter havido algum motivo (indisponibilidade de recursos [tempo, dinheiro] ou algum impedimento técnico) para isso, pois acho improvavel que o uso de streams como blobs não tenha passado pela cabeça de ninguém da equipe.

Lazy

No meu post sobre o DORM eu disse que existem alguns impedimentos técnicos para implementar lazy loading de forma completamente transparente em Delphi (O Hibernate consegue, mas envolve manipulação de bytecode), e acho que o pessoal da TMS conseguiu chegar o mais próximo possível. Veja o exemplo:

type
  TMediaFile = class
  private
    [Association([TAssociationProp.Lazy], [])]
    [JoinColumn('ID_ALBUM', [])]
    FAlbum: Proxy<TAlbum>;
    function GetAlbum: TAlbum;
    procedure SetAlbum(const Value: TAlbum);
  public
    property Album: TAlbum read GetAlbum write SetAlbum;
  end;

implementation

function TMediaFile.GetAlbum: TAlbum;
begin
  Result := FAlbum.Value;
end;

procedure TMediaFile.SetAlbum(const Value: TAlbum);
begin
  FAlbum.Value := Value;
end;

Quando um objeto TMediaFile é carregado, o atributo Album não é carregado automaticamente. Ele só será carregado quando a propriedade Album for acessada (Mais especificamente, quando a propriedade Value do Proxy for acessada).

Entretanto, acho que este Proxy poderia ter algum outro nome que deixe explicito para quem estiver lendo a classe que ele está lá para permitir que determinado atributo seja lazy.. Talvez LazyProxy<>

Bugs

Ao executar o demo MusicLibraryVCL, tudo funcionou perfeitamente num primeiro momento. Então decidi compilar o projeto com o FastMM e descobri que há um bug relacionado a gerenciamento de memória, ao que parece envolvendo um objeto sendo liberado da memória duas vezes. Se o problema é no Aurelius ou neste demo em específico, não sei dizer.

Também encontrei um vazamento de memória ao tentar implementar um pouco de código utilizando Aurelius.

Se alguem quiser reproduzir, este foi o código que utilizei:

type
  [Entity]
  [Automapping]
  TAddress = class
  private
    FId: Integer;
    FStreet: String;
    FCity: String;
  public
    property City: String read FCity write FCity;
    property Street: String read FStreet write FStreet;
  end;

  [Entity]
  [Automapping]
  TPerson = class
  private
    FId: Integer;
    FName: String;
    FAddress: TAddress;
  public
    property Name: String read FName write FName;
    property Address: TAddress read FAddress;
  end;

...

var
  Con: IDBConnection;
  DM: TDatabaseManager;
  OM: TObjectManager;
  P: TPerson;
begin
  ReportMemoryLeaksOnShutdown := True;
  if FileExists('Data.bin') then
    DeleteFile('Data.bin');

  Con := TSQLiteNativeConnectionAdapter.Create('Data.bin');
  DM := TDatabaseManager.Create(Con);
  try
    DM.BuildDatabase;
  finally
    DM.Free;
  end;

  OM := TObjectManager.Create(Con);
  try
    P := TPerson.Create;
    P.Free;
  finally
    OM.Free;
  end;
end;

Já reportei ambos os casos para o Wagner Landgraf, responsável pelo Aurelius.

Update: Segundo o Wagner, o primeiro caso é uma falha especifica do demo em questão, e não do Aurelius propriamente dito, mas que obviamente será corrigida de qualquer forma.
E o segundo caso de fato é um memory leak que surge quando se utiliza automapping com relacionamentos, a causa do problema já foi identificada e o leak foi corrigido. Devo dizer que gostei da velocidade com que a situação foi tratada!

Queries

Para executar queries, a única opção é o uso da API de Criteria. Uma outra opção (não suportada pelo Aurelius) seria o uso de OQL (Object Query Language, algo como um SQL para objetos), como a HQL do Hibernate.

Um exemplo de uso da API de criteria do Aurelius é o seguinte:

var
  Results: TObjectList<TCustomer>;
begin
  Results := Manager1.CreateCriteria<TCustomer>
    .Add(TExpression.Eq('Name', 'Mia Rosenbaum'))
    .List;

Uma situação onde acho Criteria excelente é quando você tem filtros dinamicos definidos pelo usuário. Você teria algo assim:

var
  Criteria: TCriteria<TCustomer>;
  Results: TObjectList<TCustomer>;
begin
  Criteria := Manager1.CreateCriteria<TCustomer>;
  if chFiltrarPorNome.Checked then
    Criteria.Add(TExpressions.Eq('Name', EdNome.Text));
  if chFiltrarPorCidade.Checked then
    Criteria.Add(TExpressions.Eq('City', 'EdCidade.Text));

  Results := Criteria.List;

Já para a maioria dos casos, eu iria preferir fazer algo como:

var
  Results: TObjectList<TCustomer>;
begin
  Criteria := Manager1.ExecuteQuery<TCustomer>('SELECT c FROM Customer c WHERE c.Name = "Mia Rosenbaum"');

Eu penso que criteria e OQL ambos tem o seu espaço no mundo, cada um podendo ser utilizado em determinada situação, e espero que em uma futura versão isso possa ser implementado.

Entretanto, sei que é um trabalho complexo: Envolve todo um trabalho de definir uma linguagem de consulta (provavelmente um sql adaptado), parser para esta linguagem, análise sintatica, tradução para sql nativo, etc.

Também seria interessante a possibilidade de executar queries diretamente em SQL. Você perde um pouco de abstração, mas em alguns casos onde você tenha um gargalo de performance, isto poderia ser a salvação.

Considerações finais

Ainda há alguns pontos para serem melhorados e, embora eu não tenha realizado testes mais profundos envolvendo grafos de objetos mais complexos ou análises de performance, o Aurelius me parece ser uma ferramenta promissora e madura para uso comercial.

Update 19/01/2011:

  • Fui informado que alguns dos pontos que levantei aqui já estavam sendo cogitadas, incluindo mapeamento por arquivos, TStream para blobs e mais opções de geração de identificadores (guid e outros).
  • Reescrevi a seção sobre queries para explicar melhor a api de criteria

34 Responses to “TMS Aurelius – Novo ORM para Delphi”

  1. Encontrei algo estranho nesse post: Parece gente grande escrevendo. :P

    Excelente post, parabéns!

  2. Valeu cara!

  3. Coloca um feed no seu blog. Parabéns pelo blog!

  4. Parabéns pelo post.
    Eu já estava montando uma equipe aqui na empresa onde trabalho para desenvolver o nosso, visto que o Persiphi foi fechado e não soube mais notícias e não gostei muito do DORM.
    Então estou testando o Aurelius e até agora tudo bem, exceto por um detalhe, ele não consegue mapear uma classe cujos “set” sejam procedures.
    Exemplo:

    private
    FEmail: string;

    property Email: string read FEmail write FEmail;

    Assim funciona perfeitamente, mas se usar como:

    private
    FEmail: string;
    procedure SetEmail(Value: string);

    property Email: string read FEmail write SetEmail;

    procedure SetEmail(Value: string);
    begin
    FEmail := Value;
    end;

    Não sei se é porque utilizei o “[Automapping]“, vou tentar sem.
    Como você tem um canal direto com o Wagner Landgraf, poderia ver isso com ele, por favor. Até onde testei e li no manual me deixou tendencioso a adquirir as licenças, mas ainda precisamos de mais testes.

  5. Estranho fiz um teste e funcionou perfeitamente mesmo tendo setter. Na verdade o aurelius pelo que vi persiste pelos fields e nao pelas properties entao em tese pouco importa como sao definidas as suas propriedades. Eu te aconselho a montar um aplicativo de teste e mandar por email para a tms explicando o problema, provavelmente sera direcionado para o wagner ou outra pessoa capaz de resolver a situação

  6. Valeu! Será providenciado

  7. O problema era que eu havia esquecido de definir os atributos do Enum:

    [Enumeration(TEnumMappingType.emChar, 'M,F')]
    TSexo = (seMasculino, seFeminino);

    Feito isso funcionou corretamente. Vou continuar os testes.
    Abraços

  8. Tambem é possivel definir nas configurações globais um flag para enums sempre serem mapeados automaticamente, sem precisar utilizar este atributo

  9. Magno e Marcio, só pra esclarecer: o Aurelius pode mapear fields e properties, sem problemas. OaAutomapping considera apenas fields, mas com mapeamento manual você pode mapear apenas properties. Inclusive as propriedades mapeadas não precisam nem mesmo estar relacionadas diretamente a um field, por exemplo, você pode ter uma propriedade onde o getter dela pega o valor do registry, ou de um arquivo, qualquer coisa, mesmo assim ela pode ser mapeada. Obrigado pelo interesse e suporte de ambos!

  10. estava dando uma olhada no demo e percebi um problema. Ele não está validando os fields… como assim? Na Tabela “Artist” por exemplo a Coluna “ARTIST_NAME” está com as seguintes validações.

    [Column('ARTIST_NAME', [TColumnProp.Required], 100)]
    property ArtistName: string read GetArtistName write SetArtistName;

    mas o que eu achei estranho foi que eu adicionei um Artista sem o nome e ainda com mais de 100 Caracteres e ele salvou e persistiu tranquilo.

  11. Allan,
    Talvez estes parametros (size e required) sejam utilizados apenas para fins de geração de schema.. Eu mesmo não me recordo de ter visto em nenhum lugar que o Aurelius realiza validação de objetos, embora seja muito útil se ele puder fazer isso!

    Mas vamos esperar que o Wagner veja sua dúvida e possa dar algum esclarecimento

  12. Exatamente, os dados dos atributos são pra geração de schema e não validação. É similar a você fazer um Field.AsString := ValorQualquer e a variável ValorQualquer tem um valor maior do que a capacidade do campo. O Delphi vai salvar normalmente, apenas vai truncar. Lembre-se que estamos trabalhando com objetos, uma opção seria você fazer um getter e um setter da propriedade e fazer as validações ali mesmo – ou na sua tela de edição.

  13. To pensando em colocar isso no controle da classe no método Save para pegar informações dos atributos usando a RTTI.
    mas infelizmente ainda não sei como posso pegar as informações dos atributos com as validações.
    Ex: o Atributo é “ArtistName” e eu pegar a validação de schema “TColumnProp.Required”

    e isso valendo para todas as propriedades.

    se Alguém tiver uma dica, estou disposto a escutar.

    Valeu Galera.

  14. Allan, não sei se entendi bem, mas a sua dúvida é como utilizar RTTI para ler os atributos presentes nas suas classes/campos/propriedades?

    Se for isso, é bem simples, basta utilizar o método GetAttributes presente em todas as classes como TRttiType, TRttiMethod, TRttiProperty, etc. Isto vai retornar um array de TCustomAttribute que representa todos os atributos que aquele elemento possui, daí é só iterar neste array e procurar instancias do atributo que você quer.

  15. Magno, é basicamente isso que eu quero fazer mesmo até essa parte eu sei.
    O que eu não sei se esse método ele retorna as
    (custom properties)* da (propiedade)**.

    * – “[Column('ARTIST_NAME',[TColumnProp.Required], 100)]
    ** – “ArtistName”

    Valeu.

  16. O Aurelius disponibiliza as informações de mapeamento em runtime. Por exemplo, se você fizer isso:

    uses Aurelius.Mapping.RttiExplorer, Aurelius.Mapping.Metadata;
    var C: TColumn;
    for C in TRttiExplorer.GetInstance.GetColumns(TArtist, true) do
    if TColumnProp.Required in C.Properties then …

    você consegue obter essa informação.
    Atenção! Esses métodos não estão documentados porque ainda não sei se fechei eles nessa formato, ou seja, em uma versão futura talvez você precise alterar esse código. Mas sabendo disso, você pode usá-lo assim como falei.

  17. Interessante este metadata de mapeamento. Uma possibilidade é utilizar isso para montar um validador de schema, seria útil por exemplo para detectar quando o sistema foi atualizado mas esqueceram de incluir um campo novo na base de dados do cliente.

    O Aurelius possui algo assim, ou ao menos tem algo planejado?

  18. Magno, o TMS Data Modeler é mais indicado pra fazer isso. Nele tem toda a lógica de fazer engenharia reversa, comparação do schema, e eventualmente gerarção do script de atualização. Colocar isso no Aurelius seria aumentá-lo demais. Geralmente o que se faz (e eu pessoalmente já fiz) é usar uma tabela contendo a versão do banco de dados. Assim você detecta se a base está de acordo com o esperado comparando o número da versão do banco (nessa tabela) com a versão que a sua aplicação exige. Fazer a engenharia reversa toda vez que a aplicação inicia acho que não é muito eficiente.

  19. O esquema de guardar o número de versão no banco e comparar com a a aplicação é o que eu já faço. A idéia de comparar o schema era apenas para a aplicação validar se a estrutura do banco está de acordo com o esperado pela aplicação, e notificar o usuário/fechar o sistema caso não esteja, pois eventualmente uma atualização mal feita pode ter deixado o schema inconsistente. Não acho que seja um processo tão pesado, de forma que se o aplicativo em questão for por exemplo um PDV que é aberto apenas no inicio do dia, não vejo tanto problema.

    Esse Data Modeler não conhecia (apenas ouvi falar), vou procurar mais informações

  20. Muito Obrigado Wagner.
    era exatamente isso que eu queria, agora só basta no Save eu percorrer as colunas da tabela verificar se ela é requirida e se o não for passado nenhum valor aí eu levantar logo uma mensagem.

    Valeu. Este blog tá de parabéns. ;D

  21. Caros,

    queria saber a melhor forma de popular uma classe numa grid mas que fique genérica para qualquer outra tabela ou o LiveBinding da VCL alguem tem alguma sugestão ?

    e tambem popular um ClientDataSet e Trazer de um ClienteDataSet que vc só passe o CDS e a Classe que aí ele popula um conforme o outro. alguma idéia ?

  22. Allan, uma futura versão do Aurelius (provavelmente uma das duas próximas) terá um componente TDataset que faz isso que você falou – ele recebe objetos e os disponibiliza via dataset. Assim você pode obter uma lista de objetos e através do TDataset mostrá-los em um grid da vcl ou no FM através dos live bindings. ClientDataset também será suportado já que este funciona com qualquer dataset como provider. Caso você queria fazer alguma coisa bem manual e específica, daí acho que a única solução seria usar algum TDataset em memória que tem disponível no mercado (alguns são open source). Aí você teria que popular o dataset na mão mesmo.

  23. Galera com a ajuda do Wagner consegui fazer um método que valida se os campos foram digitados corretamente.

    {Lanca uma excecao caso tenha uma mensagem. foi preciso
    a Exceção para não ter que add a unit Dialogs da VCL.}
    procedure ShowMessageContain(aMessage:string);
    begin
    if aMessage ” then
    begin
    raise Exception.Create(aMessage);
    end;
    end;

    {Percorre as colunas para validar, infelizmente ainda só fiz para o tipo string e com requirido ou tam. Maximo}
    function IsValidValues(Table:TObject): string;
    var
    C: TColumn;
    Value: Variant;
    begin
    for C in TRttiExplorer.GetInstance.GetColumns(Table.ClassType, true, True) do
    begin
    Value := TRttiExplorer.GetInstance.GetColumnDbValue(Table ,C);
    case C.FieldType of
    ftString:
    begin
    if (TColumnProp.Required in C.Properties) and (Value = ”) then
    Result := ‘Valor em Branco’;
    if (C.Length < Length(Value)) then
    Result := 'Maximo excedido';
    end;
    end;
    end;
    end;

    {Método do SaveAlbum do exemplo do MediaLibary e um exemplo de como usar as duas funçoes acima que pode servir pra qualquer Tabela.}
    procedure TEditAlbumController.SaveAlbum(Album: TAlbum);
    begin
    ShowMessageContain(IsValidValues(Album));
    if not FManager.IsAttached(Album) then
    FManager.SaveOrUpdate(Album);
    FManager.Flush;
    end;

    é importante usar isso não no controle e sim da tela. caso for fazer o projeto com DataSnap. fazer no lado cliente para que não seja trafegado dados na rede em vão. Valeu Galera.

  24. Excelente post. Parabéns Magno!!!

    Wagner, FANTÁSTICO “terá um componente TDataset que faz isso que você falou – ele recebe objetos e os disponibiliza via dataset. Assim você pode obter uma lista de objetos e através do TDataset mostrá-los em um grid da vcl ou no FM através dos live bindings. ”

    =D

  25. Valeu, Delesposte.. Bom te ver por aqui

  26. @Allan, interessante a sua solução, e muito legal que você tenha se preocupado em fazer de forma a não ficar atrelado à VCL.

    Alias, isso veio em um momento oportuno, já que um dos posts que tenho no forno é justamente para discutir idéias sobre validação de objetos em Delphi. Seria muito interessante algo como o Beans Validation do Java (http://blog.caelum.com.br/java-ee-6-comecando-com-bean-validation/). Alguem sabe de algo assim para Delphi?

  27. Cara que existe ainda não sei, mas atualmente to criando algo parecido com isso que vai servir pra validar e pra converter para um ClientDataSet para os dados serem mostrados na tela pelo LiveBiding.

    Criei uma Custom Property: [Display(DisplayLabel, Lenght, Visible)]

    DisplayLabel: é um caption que seria amostrado na Grid por exemplo
    Lenght: O tamanho dele;
    Visible: Se ele é um campo visível na grid

    outro seria para validar o que o foi digitado

    [Validate(Required)] e um método Validar passando o valor da propriedade.
    //Os outros herdam de Validate
    [ValidateString(Required, lenght)]
    [ValidateExtended(Required, lenght, Precision, min, max)]
    [ValidateInteger(Required, min, max)]
    [ValidateDate(Required, min, max)]

    e no método save vc percorreria todas as propriedades pegaria o valor da propriedade percorreria as CustomProperties e fazendo as validações;

  28. Tambem estou testando este ORM, porem fiz o seguinte teste conectei em um banco FB, criei o schema conforme o exemplo foi criado no banco a entidade PERSON ate ai blz, mas imagina o cenario, cliente em producao ai alteramos esta entidade criamos um campo novo ex: fone;
    type
    [Entity]
    [Automapping]
    TPerson = class
    private
    FId: integer;
    FLastName: string;
    FFirstName: string;
    FEmail: string;
    FFone: string;
    public
    property Id: integer read FId;
    property LastName: string read FLastName write FLastName;
    property FirstName: string read FFirstName write FFirstName;
    property Email: string read FEmail write FEmail;
    property Fone: string read FFone write FEmail;
    end;

    Ao tentar criar novamente o SCHEMA e informado que ja existe . Existe algo que checar os campos e cria os que necessita?

  29. Marcos,
    O esquema do Aurelius de criar o Schema eu acredito que seja considerando um banco vazio, onde ele pode ir criando tudo do zero sem se preocupar em atualizar coisas que já existem.

    De qualquer forma, existem ferramentas que fazem automaticamente esta atualização de schema, comparando a base atual com uma de modelo, mas eu não recomendo usar isso em produção. Muitas vezes a atualização não envolve apenas o schema, tem que atualizar os dados também. Imagina que você pegou um campo de uma classe e jogou para outra, nenhuma ferramenta vai migrar isso para você preservando os dados já existentes.

    Como eu disse anteriormente em algum comentario aqui, eu faço isso manualmente: Durante o desenvolvimento, vou guardando em um script todas as mudanças que faço na estrutura do banco de dados, e na hora de soltar uma versão este script fica compilado como recurso no executavel.
    Quando a aplicação inicia, o executavel compara a sua versão com um número de versão armazenado no banco, e se estiver desatualizado ele pega todos os scripts necessarios e os executa ordenadamente. Nestes scripts eu posso colocar comandos de DDL (criar/alterar/apagar tabelas/campos/etc) e DML (manipular dados)

  30. Marcos, o TMS Data Modeler é destinado a isso. Ele é uma ferramenta de modelagem de banco, com ele você pode fazer engenharia reversa do banco, salvar as alterações em versões (check-ins) e comparar as versões, gerando scripts de atualização entre as versões. O Data Modeler é vendido como um opcional integrado ao Aurelius justamente pra isso. Colocar esses recursos no Aurelius seria inchá-lo com uma série de coisas fora do escopo do ORM, afinal esse tipo de rotina é complexa. Estamos avaliando incluir futuramente no Aurelius uma opção de atualização do banco, mas seriam coisas bem simples como inclusão de campo e de novas tabelas. Mas como o Magno comentou, o “buraco é mais embaixo” pois os deltas entre os bancos podem ser bem complexos, e pra isso as ferramentas específicas como o Data Modeler e outras.

  31. Caros, como faço para controlar transação no Aurelius ?

  32. @Allan, o IDBConnection tem um método BeginTransaction que retorna um IDBTransaction. Esse por sua vez tem os métodos Commit e Rollback.

  33. Comecei a estudar essa ferramenta e fiz um exemplo para estudo e percebi que em tabelas com autoincremento utilizando Triggers e Generator, ao montar o sql de insert ele cria a verificação do generator com o nome SEQ_CLIENTE e no meu banco tava com o nome GEN_CLIENTE_ID e dar erro, no caso, eu tenho que mudar o nome dos meus generator ou tem como mudar no codigo do aurelius?

    grato,

    Vinicius Silva

  34. Vinicius, de acordo com a documentação, você pode anotar a classe com o atributo Sequence, que permite especificar o nome do generator/sequence que será utilizado

Leave a Reply

Spam protection by WP Captcha-Free