Mudanças entre as edições de "RFWDAO"

De Wiki do Leitão
Ir para: navegação, pesquisa
Linha 578: Linha 578:


</syntaxhighlight>}}
</syntaxhighlight>}}
=== Função DISTINCT ===
A função `DISTINCT` é utilizada para retornar valores únicos, eliminando duplicações. Isso é útil em consultas onde você deseja evitar registros duplicados.
{{java|Exemplo de consulta utilizando DISTINCT|<syntaxhighlight lang="java">
// Exemplo de consulta utilizando DISTINCT para buscar nomes de usuários únicos
RFWField distinctField = RFWField.distinct("name");
// Montando o Meta Objeto para a consulta
RFWMO mo = new RFWMO();
// Realizando a consulta
List<Object[]> resultList = facade.findListEspecial(uuid, UserVO.class, new RFWField[] { distinctField }, mo, null, null, null, null);
</syntaxhighlight>}}
Neste exemplo, o campo `"name"` é consultado com a função `DISTINCT` aplicada, retornando apenas os nomes únicos encontrados no banco de dados.
=== Função MAX ===
@TODO
=== Função MIN ===
@TODO
=== Função AVG ===
@TODO
=== Função UPPER ===
@TODO
=== Função LOWER ===
@TODO
=== Função LENGTH ===
@TODO
=== Função TRIM ===
@TODO
=== Função SUBSTRING ===
@TODO
=== Função ABS ===
@TODO
=== Função ROUND ===
@TODO
=== Função CONCAT ===
@TODO

Edição das 11h58min de 24 de agosto de 2024

A classe RFWDAO é a principal classe do RFW ORM, seu objetivo principal é receber os RFWVOs e persisti-lo (inserindo/atualizando), exclui-lo ou encontra-los no banco de dados.

Abstraindo a questão objeto x relacional do banco de dados, e diminuindo (ou eliminando na grande maioria dos casos) a necessidade de implementação códigos SQL pelo desenvolvedor.


Regras do Funcionamento

  1. Embora utilize os conceitos do banco de dados, não segue os padrões definidos no JPA. Assim, nenhuma annotation do JPA é utilizada nos VOs do RFW.
  1. O RFWDAO manipula os VOs automaticamente. E não aceita nenhum outro tipo de objeto, isto é, para ser persistido é obrigatório que a entidade seja descendente do RFWVO.

Estrutura do Banco de Dados

O RFW suporta múltiplos schemas do banco de dados sendo manipuladas pela mesma instância do banco de dados e aplicação. Assim um mesmo servidor e mesmo banco de dados poderá ser utilizada para gerenciar várias instância do sistema. Para isso o sistema utilizará vários "schemas" (ou catálogos) do banco de dados.

Por exemplo, para cada VO você pode definir qual schame e tabela do banco de dados ele utilizará. E poderá, ainda, fazer com que que um VO acesse um schema diferente para cada usuário. Tendo, desta forma, os dados de cada usuário/sessão/cliente separado por schemas "gêmeos" no banco de dados, para armazenar dados pessoais como por exemplo o extrato bancário de cada um. E ainda assim ter VOs que utilizem um schema centralizado para buscar os dados que são únicos para todos, como uma tabela de CEPs de endereços.


Imagine um sistema para empresas, onde cada empresa é um cliente diferentes com dados particulares, sua divisão poderia ser:

  • kernel - schema onde ficam as tabelas de uso geral do sistema. Como o cadastro das empresas presentes e nome dos seus schemas particulares, dos usuários de sistema, e outras tabelas de dados comuns a todas as empresas, como tabelas de CEP, e outros dados que não pertencem a uma única empresa.
  • <prefixo>_<empresa> - um schema para cada empresa gerenciada pelo sistema. Isto é, para cada empresa um novo schema é criado para armazenar os dados específicos da empresa, como itens/produtos sendo vendidos, documentos fiscais, extratos, etc.


Configuração das FK

Ao criar a associação entre as tabelas do banco de dados devemos seguir as seguintes regras para o bom funcionamento do RFWDAO:


Relacionamento tipo Associação

  1. No relacionamento não obrigatório devemos colocar as opções ON DELETE SET TO NULL (nos relacionamentos 1:1 ou 1:N) e ON DELETE CASCADE (nos relacionamentos N:N, para que se exclua o registro na tabela de join).
    O RFWDAO não faz essa atualização para NULL para desmanchar o relacionamento antes de excluir o objeto. A ausência dessa configuração fará com que o objeto não possa ser excluído pelo RFW quando utilizado por outro objeto.
  2. No relacionamento obrigatório devemos colocar as opções ON DELETE RETRICT. Nos relacionamentos N:N podemos colocar para "restringir" de um lado mas para "cascata" de acordo com a condição de obrigatoriedade do relacionamento.
    Como o relacionamento é obrigatório (dependente) para a contra parte, a deleção deve ser restringida de propósito. Isso porque o RFWDAO não procura objetos que dependam deste antes de excluir. O Banco de Dados fica responsável por restringir essa operação.


Relacionamento tipo Composição

  1. No caso de composição devemos sempre definir a opção ON DELETE CASCADE já que o objeto filho não existe sem o pai e não deve restringir a exclusão do pai.


Note 64.png
ON UPDATE CASCADE
Em caso de update, sempre vamos utilizar a opção do 'CASCADE'. Embora o RFW não faça alteração do ID dos objetos, pode ser necessário corrigir algum ID diretamente no banco de dados (migração, bug, merge, etc).


Note 64.png
Relacionamentos N:N UniqueIndex
Normalmente em relacionamentos N:N as colunas do relacionamento são a própria PrimaryKey da tabela, o que já evita a inserção de múltiplas entradas. É importante que sejam definidas como PrimaryKey ou coloque uma constante de UniqueIndex para que em caso de inserção em duplicidade de associação termine em erro. Permitindo que o desenvolvedor detecte e corrija.


Note 64.png
Objeto Em Uso e Não pode ser Excluído
Quando o RFW recebe o erro de "falha de constraint" do banco de dados, ele assume que não pode ser excluído porque o objeto está em uso por algum outro objeto e retorna uma exception específica para este tipo de erro: uma exception do tipo Critica com o código de erro "RFW_ERR_000006". Essa exception deve ser tratada quando se espera que esse tipo de coisa possa acontecer, como por exemplo, um objeto que pode ser excluído desde que ainda não esteja em uso. Apesar de ter uma mensagem padrão no Bundle para este código, é recomendável criar uma mensagem para cada caso específico.


Relacionamento Entre Objetos

Os relacionamentos entre objetos serão declarados de acordo com o "tipo do relacionamento" constituído entre eles para que o RFWDAO saiba como proceder quando for atualizar, inserir ou excluir os objetos. Entender o relacionamento entre os dois define a boa arquitetura ao criar os objetos:

Associação

O tipo de relacionamento Associação é utilizando quando ambos os objetos existem separadamente, mesmo que não tenham vínculos. Por exemplo:

  • Aluno x Curso - Tanto Aluno quando Curso existem estando o aluno matriculado no curso ou não. Um curso pode existir sem ter nenhum aluno matriculado, bem como um Aluno pode existir sem estar matriculado em nenhum curto.

Para o RFWDAO, esse tipo de relacionamento exige que a contra-parte já exista no banco de dados, ou seja já tenha um ID. A contra parte nunca é manipulada junto com este objeto, ou seja:

  • Exclusão - O objeto da contra-parte não é excluído junto, apenas a associação é desfeita.
  • Inclusão - Em caso de inclusão, o RFWDAO espera que os objetos associados já tenham um ID. Pois eles não serão incluídos junto, apenas será criada uma associação.
  • Alteração - Em caso de alteração o objeto será comparado com o atual no banco de dados. Objetos que não estejam mais presentes no objeto sendo persistido, terão sua associação removida, novos objetos terão associação criada. A contra-parte nunca será excluída, e nem criada.

Composição

O relacionamento do tipo composição indica que o objeto associação é uma "extensão" deste objeto. Ou seja, ele faz parte do objeto principal, e como tal será manipulando junto com o objeto pai. Por exemplo:

  • Pessoas x Endereços da Pessoa - Pessoas pode existir sem ter um endereço, já o endereço da pessoa não existe sem a pessoa. Neste caso, "Endereços da Pessoa" é parte do objeto pai e será manipulado junto com ele.

Para o RFWDAO a contra-parte pode ou não existir no banco de dados nos casos de alteração do objeto pai, mas no caso de inclusão do pai o objeto filho não deve ter ID (ou seja, não existe no banco).

Mesmo que Pessoas diferentes tenham o mesmo Endereço, o RFW não permitirá compartilhar o mesmo objeto. Uma vez que a definição de composição não permite que o "reaproveitamento" do objeto. Para isso Endereço de Pessoa teria de ser uma entidade autônoma (existir sem o Pessoas) e o relacionamento deveria ser alterado para Associação. Neste caso a contra-parte é manipulada junto com o pai:

  • Exclusão - Quando o objeto pai é excluído os objetos filhos serão excluídos junto.
  • Inclusão - Em caso de inclusão, os objetos filhos serão incluídos juntos e automaticamente associados ao objeto pai.
  • Alteração - Em caso de alteração do objeto pai, novos objetos filhos serão incluídos e persistidos, objetos filhos que foram removidos do objeto serão excluídos automaticamente. Bem como as informações dos objetos filhos persistentes também serão atualizadas em cascata, isto é, caso esse objeto também tenha outros de composição ou associação, eles também serão atualizados.

Annotation @RFWDAOAnnotation

Toda entidade deve ter a Class Annotation @RFWDAOAnnotation para configurar a tabela a qual o objeto se refere.

Java 256.png Exemplo @RFWRFW
@RFWDAAnnotationO(schemaType = Schema.KERNEL, table = "k_company")
public class RFWCompanyVO extends RFWVO {
   ...
}

A annotation tem dois parâmetros:

  • schemaType - Indica se a qual schema a tabela pertence.
  • table - Indica o nome da tabela deste objeto.


DAOResolver

O DAOResolver é uma interface disponibilizada pelo FrameWork para permitir uma intervenção na operação do RFWDAO de forma dinâmica.


Por exemplo, imagine o caso de uso em que o sistema tem diversas empresas para atender, e os dados de cada empresa é separado em um Schema do banco de dados diferentes, mas com a mesma estrutura. Digamos que sejam schemas com os nomes 'empresaA', 'empresaB', etc. Dependendo da sessão do usuário, queremos obter os dados de um Schema específico (aquele da sua empresa). Utilizando apenas a annotation @RFWDAOAnnotation não conseguiríamos definir esses diferentes schemas para o mesmo objeto. Assim, nasce o DAOResolver!


Para utilizado é preciso criar uma implementação da interface DAOResolver. Seu método getSchema(...) será chamado toda vez que o RFWDAO precisar do Schema do objeto. Assim, no objeto que utiliza diversos schemas pode ser utilizado uma constante como "_COMPANYSCHEMA", e dentro do resolver, ao detectar esta constante verificar a sessão do usuário e retornar o schema da empresa adequado para a sessão.


Note 64.png
RFWDAOFactory
Como a inicialização do RFWDAO deve ser algo constante na aplicação, e passar o Resolver e instância-lo com frequência pode ser custoso e deixar o código descentralizado, considera a criação de uma classe de factory para retornar o RFWDAO instanciado já com todos os atributos de acordo com suas definições;


O DAOResolver tem atualmente 4 métodos:


getEntityType(...)

Este método permite que a classe da entidade seja substituída. A necessidade da implementação deste método é rara, e servirá para poquísimos casos. Mas imaginemos algum objeto (RFWVO) que seja fornecido pelo próprio framework. Como os VOs do serviço de Location que já oferecem componentes para a tela e outras funcionalidades. Mas por algum motivo a aplicação precisa de mais informações do que as fornecidas pelo objeto padrão do framework.

Assim, imagine que para isso os objetos Location do framework foram extendidos e a aplicação tenha seus próprios objetos. Quando algum componente do framework solicitar o objeto no RFWDAO, este método permitirá que a classe do objeto possa ser substituída por outra. Uma vez trocada, o RFWDAO passará a utilizar a nova classe daquele ponto em diante.


Stop 256.png
Repercursões
Este método só deve ser utilizado se entender o funcionamento do RFWDAO, pois a troca poderá criar inconsistências entre objetos e links. Só se deve trocar por classes que sejam descendentes da classe solicitada ou se for o objeto raiz da consulta. Em resumo, utilize este método só em último caso.


getSchema(...) e getTable(...)

Por padrão o RFWDAO procura o Schema e a Tabela do objeto através da annotation @RFWDAOAnnotation escrita na classe. Mas em alguns momentos podemos querer um conteúdo mais dinâmico (trocando o schema ou a tabela conforme sessões e condições dos sistema, por exemplo). Ou mesmo informar o schema e a tabela a serem utilizados para objetos que não tem a annotation (como VOs do próprio RFW).

Para isso basta identificar o objeto sendo solicitado e retornar os valores desejados. Se algum valor for retornado ele será utilizado pelo RFWDAO, se sobrepondo as informações da annotation. Caso seja retornado nulo o RFWDAO seguirá com sua implementação padrão.


createInstance(...)

Este método intervêm na instanciação dos objetos pelo RFWDAO. Se implementado, este método será chamado sempre que o RFWDAO precisar instanciar um novo objeto para alocar o conteúdo do banco de dados. Ele pode ser útil para que seja possível a criação de objetos sem um construtor padrão (sem argumentos), ou caso haja a necessidade de instanciar uma classe descendente, e assim por diante.


getMetaRelationColumn(...), getMetaRelationColumnMapped(...) e getMetaRelationJoinTable(...)

Este três métodos permitem interferir nos atributos column, columnMapped e joinTable da RFWMetaRelationshipField encontrada nos elementos de persistência. Sua funcionalidade se assemelha as descritas anteriormente.

Ao persistir algum objeto que já seja fornecido pelo framework, e sem a possibilidade de fazer a definição diretamente no objeto, podemos utilizar estes métodos para corrigir/terminar a configuração da definição do relacionamento no banco de dados.

Ao implementar estes métodos deve-se verificar se é um objeto "de interesse" e retornar o atributo desejado. Caso não deseje realizar nenhuma intervenção basta retornar nulo para que o RFWDAO assuma sua implementação padrão (buscando dentro da Annotation).

Relacionamento Atributo do Objeto x Coluna do BD

Para que um atributo do objeto seja relacionado corretamente com uma coluna da tabela (especificada na Annotation @RFWDAOAnnotation) é necessário que o atributo tenha uma @RFWMetaAnnotations. Nela constarão várias informações sobre o atributo, incluindo a coluna. Caso o nome da coluna não seja especificado, o RFWDAO assume que o nome da coluna é exatamente o mesmo nome do atributo, ou seja, caso o mesmo nome seja utilizado na coluna do banco quando no atributo do objeto, não é necessário especificar o atributo coluna na Annotation. Isso deixa o código menos poluído.

Mapeamento do Relacionamento

O mapeamento de todos os relacionamentos são feitos através da @RFWMetaRelationshipField.

Associação 1:1

Associação 1:N

Associação OneToMany entre RFWCompanyVO e RFWFileVO


A associação 1:N do exemplo é unilateral, já que só o FileVO tem conhecimento do vínculo. A descrição do mapeamento feito no FileVO é a mesma utilizada em uma associação 1:1.


Java 256.png FileVO
  @RFWMetaRelationshipField(column = "k_company_id", relationship = RelationshipTypes.ASSOCIATION, ...)
  private RFWCompanyVO companyVO = null;


Sendo os atributos:

  • relationship - Definido como ASSOCIATION para indicar a associação (tanto para 1:N quanto 1:1).
  • column - nome da coluna que tem o ID da entidade associada. Utilizado somente se a coluna com o ID estiver na tabela desta entidade.
  • columnMapped - nome da coluna que tem o ID do objeto da contra-parte. Utilizado somente se a coluna com o ID desta entidade (FK) estiver na tabela da entidade associada.
  • targetRelationship - Classe do objeto utilizado na contra-parte. Normalmente a mesma classe passada no "generics" da coleção. Quando não é uma coleção de objeto, este atributo pode ser omitido e será usado o próprio tipo do Field declarado na classe. Neste caso o RFWCompanyVO.

Associação N:N

Associação ManyToMany entre RFWCompanyVO e RFWUserVO

A associação N:N (Many To Many) exige uma tabela intermediária para gravar as associações. Nos objetos o relacionamento é feito da seguinte maneira:


Java 256.png RFWCompanyVO
  @RFWMetaRelationshipField(relationship = RelationshipTypes.MANY_TO_MANY, joinTable = "k_user_company", column = "k_company_id", columnMapped = "k_user_id", targetRelationship = RFWUserVO.class, ...)
  private List<RFWUserVO> users = null;


Java 256.png RFWUserVO
  @RFWMetaRelationshipField(relationship = RelationshipTypes.MANY_TO_MANY, joinTable = "k_user_company", column = "k_user_id", columnMapped = "k_company_id", targetRelationship = RFWCompanyVO.class, ...)
  private List<RFWCompanyVO> companies = null;


Sendo os atributos:

  • relationship - Em ambas as entidades definido como MANY_TO_MANY para indicar a associação de N:N.
  • joinTable - Nome da tabela de join entre as tabelas dos objetos.
  • column - nome da coluna que tem o ID da entidade onde está a declaração.
  • columnMapped - nome da coluna que tem o ID do objeto da contra-parte.
  • targetRelationship - Classe do objeto utilizado na contra-parte. Normalmente a mesma classe passada no "generics" da coleção.


Note 64.png
Schema da Tabela de Join
A tabela de join é procurada dentro do mesmo schema da entidade pai. Ou seja a entidade que declara o RFWMetaRelationshipField.

Quando há relacionamento entre tabelas do schema da empresa e do schema do kernel, a entidade do kernel não conhece o relacionamento, logo ela não declara o relacionamento.

Além disso, o relacionamento entre tabelas da empresa só pode estar dentro do schema da empresa, caso contrário a tabela de join teria de ter uma coluna para cada tabela de schema diferente. Completamente impossível.

Composição 1:1

Composição OneToOne entre FileVO e FileContentVO


A composição 1:1 tem basicamente a função de separar uma coleção de dados do objeto principal, podendo deixar o objeto com os dados principais mais leve. Ou separar uma coleção de dados não obrigatórios. No exemplo abaixo, separamos o conteúdo do arquivo (FileContentVO) dos dados do arquivo (FileVO). Assim quando precisamos recuperar as informações sobre os arquivos do banco não temos de obter todo o conteúdo do arquivo deixando uma conexão pesada e muitos dados na memória.


Java 256.png FileVO
  @RFWMetaRelationshipField(columnMapped = "k_file_id", relationship = RelationshipTypes.COMPOSITION, ...)
  private FileContentVO fileContentVO = null;


Sendo os atributos:

  • relationship - Definido como COMPOSITION para indicar a composição (tanto para 1:N quanto 1:1).
  • column - nome da coluna que tem o ID da entidade associada. Utilizado somente se a coluna com o ID estiver na tabela desta entidade.
  • columnMapped - nome da coluna que tem o nosso ID na tabela da contra-parte. Utilizado somente se a coluna com o ID desta entidade (FK) estiver na tabela da entidade associada.
  • targetRelationship - Classe do objeto utilizado na contra-parte. Normalmente a mesma classe passada no "generics" da coleção. Quando não é uma coleção de objeto, este atributo pode ser omitido e será usado o próprio tipo do Field declarado na classe. Neste caso o FileContentVO.


Java 256.png FileContentVO
  @RFWMetaRelationshipField(relationship = RelationshipTypes.PARENT_ASSOCIATION, column = "k_file_id")
  private FileVO fileVO = null;


Sendo os atributos:

  • relationship - Definido como PARENT_ASSOCIATION para indicar que esta entidade é a filha de uma composição (objeto submisso) e que este field se relaciona com o objeto pai.
  • column - nome da coluna que tem o ID da entidade associada. Utilizado somente se a coluna com o ID estiver na tabela desta entidade.
  • columnMapped - nome da coluna que tem o nosso ID na tabela da contra-parte. Utilizado somente se a coluna com o ID desta entidade (FK) estiver na tabela da entidade associada.
  • targetRelationship - Classe do objeto utilizado na contra-parte. Normalmente a mesma classe passada no "generics" da coleção. Quando não é uma coleção de objeto, este atributo pode ser omitido e será usado o próprio tipo do Field declarado na classe. Neste caso o FileVO.

Composição 1:N

Composição N:N

O RFW não permite esse tipo de composição. Isso porque esse relacionamento indica o reaproveitamento dos objetos filhos por outros pais. Neste caso o objeto filho deve ter sua "autonomia" e viver independente do pai. Casos como esses devem ser transformados em Associação N:N.

Implementação

Quando o RFWDAO tem seus métodos chamados, ele analisa a classe da entidade e a primeira coisa que ele faz é criar um objeto de mapeamento - o DAOMap.

O DAOMap cria uma estrutura de mapeamento entre a entidade e a tabela, e em seguida entre os atributos da entidade e as colunas das tabelas. Ele também analisa os atributos marcamos dom a RFWMetaRelationshipField e cria a estrutura de mapeamento entre as tabelas e as FKs, o que normalmente, posteriormente, é traduzido em Left Joins ou Inserts/Updates/Deletes a medida que a entidade é persistida.


Note 64.png
Visualização do Mapeamento
Durante o processo de Debug, é possível visualizar a tabela desse mapeamento através do método:
RFWDAO.dumpDAOMap(map)

O conteúdo retornado é semelhando ao exibido abaixo neste documento, e pode ser verificado usando a inspeção de variável de dentro do RFWDAO. O conteúdo retornado pode ser colado no editor de texto para uma melhor visualização.


Aqui temos um exemplo de mapeamento criado ao tentar buscar um objeto RFWUserVO incluindo as empresas RFWCompanyVO:

Associação ManyToMany entre RFWCompanyVO e RFWUserVO


MAPEAMENTO DAS ENTIDADES
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Path                                    Schema.Table                  Alias     Column              Join                JoinColumn          Entity
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                        rfw_kernel.k_user             t0                                                                    br.com.rfw.kernel.services.security.vo.RFWUserVO
.companies                              rfw_kernel.k_user_company     t1        idk_user            t0                  id                  null
companies                               rfw_kernel.k_company          t2        id                  t1                  idk_company         br.com.rfw.kernel.vo.RFWCompanyVO
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

MAPEAMENTO DOS ATRIBUTOS
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Path                                    Field                                   Schema.Table                                                Column
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                        id                                      (t0) rfw_kernel.k_user                                      id
                                        user                                    (t0) rfw_kernel.k_user                                      user
                                        password                                (t0) rfw_kernel.k_user                                      password
                                        fullName                                (t0) rfw_kernel.k_user                                      fullName
                                        nickname                                (t0) rfw_kernel.k_user                                      nickname
                                        email                                   (t0) rfw_kernel.k_user                                      email
                                        validatedKey                            (t0) rfw_kernel.k_user                                      validatedKey
                                        resetPasswordKey                        (t0) rfw_kernel.k_user                                      resetPasswordKey
companies                               id                                      (t2) rfw_kernel.k_company                                   id
companies                               dbSchema                                (t2) rfw_kernel.k_company                                   dbSchema
companies                               name                                    (t2) rfw_kernel.k_company                                   name
companies                               fullName                                (t2) rfw_kernel.k_company                                   fullName
companies                               tradingName                             (t2) rfw_kernel.k_company                                   tradingName
companies                               cnpj                                    (t2) rfw_kernel.k_company                                   cnpj
companies                               addressStreet                           (t2) rfw_kernel.k_company                                   addressStreet
companies                               addressNumber                           (t2) rfw_kernel.k_company                                   addressNumber
companies                               addressComplement                       (t2) rfw_kernel.k_company                                   addressComplement
companies                               neighborhood                            (t2) rfw_kernel.k_company                                   neighborhood
companies                               cep                                     (t2) rfw_kernel.k_company                                   cep
companies                               cityVO                                  (t2) rfw_kernel.k_company                                   idk_locationCity
companies                               phone                                   (t2) rfw_kernel.k_company                                   phone
companies                               email                                   (t2) rfw_kernel.k_company                                   email
companies                               ieType                                  (t2) rfw_kernel.k_company                                   ieType
companies                               regimeICMS                              (t2) rfw_kernel.k_company                                   regimeICMS
companies                               ie                                      (t2) rfw_kernel.k_company                                   ie
companies                               taxsystem                               (t2) rfw_kernel.k_company                                   taxsystem
companies                               spedPerfil                              (t2) rfw_kernel.k_company                                   spedPerfil
companies                               spedActivityType                        (t2) rfw_kernel.k_company                                   spedActivityType
companies                               contName                                (t2) rfw_kernel.k_company                                   contName
companies                               contCPF                                 (t2) rfw_kernel.k_company                                   contCPF
companies                               contCRC                                 (t2) rfw_kernel.k_company                                   contCRC
companies                               contCNPJ                                (t2) rfw_kernel.k_company                                   contCNPJ
companies                               contAddressStreet                       (t2) rfw_kernel.k_company                                   contAddressStreet
companies                               contAddressNumber                       (t2) rfw_kernel.k_company                                   contAddressNumber
companies                               contAddressComplement                   (t2) rfw_kernel.k_company                                   contAddressComplement
companies                               contNeighborhood                        (t2) rfw_kernel.k_company                                   contNeighborhood
companies                               contCep                                 (t2) rfw_kernel.k_company                                   contCep
companies                               contLocationCityVO                      (t2) rfw_kernel.k_company                                   idContk_locationCity
companies                               contPhone                               (t2) rfw_kernel.k_company                                   contPhone
companies                               contEmail                               (t2) rfw_kernel.k_company                                   contEmail
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

No mapeamento acima podemos ver duas sessões: a primeira que cria o mapeamento entre as entidades e as tabelas e a segunda que cria o mapeamento dos atributos e das colunas.

A primeira tabela mapeada é a tabela da entidade para a qual o RFWDAO foi criado. Neste caso a entidade RFWUserVO, que utiliza a tabela k_user do schema rfw_kernel. Ambas as informações retiradas das Annotation @RFWDAOAnnotation da entidade. A primeira tabela sempre recebe o alias t0. As próximas tabelas receberam o alias tN, onde N é um número incremental, conforma as novas conexões forem criadas no map.


Considera a linha 3 agora, a linha 2 será explicada na sequência: As entidade RFWUserVO e RFWCompanyVO tem uma relação entre si e o RFWDAO precisa buscar seu relacionamento para completar o objeto. Como partimos do Objeto RFWUserVO, e as empresas estão na propriedade "companies" do objeto, a tabela do objeto RFWCompanyVO recebe o valor "companies" na coluna de "Path". Como não é a tabela principal, este mapeamento recebeu também os valores "Column", "Join" e "JoinColumn", que são usadas para saber como conectar uma tabela na outra, sendo:

  • Column - Coluna da própria tabela que é utilizada para realizar a conexão com a coluna da outra tabela
  • Join - Nome da outra tabela à qual essa será associada.
  • JoinColumn - Coluna da tabela de "join" que é utilizada para realizar a conexão.

Escrevendo como se fosse em uma condição do SQL seria algo como "... LEFT JOIN <Schema.Table> <Alias> ON <Alias>.<Column> = <Join>.<JoinColumn> ..."


Voltando à linha 2: No nosso exemplo é necessário notar que outro mapeamento de tabela foi criado. Isso ocorre porque o relacionamento entre RFWUserVO e RFWCompanyVO é N:N, e demanda uma "tabela de Join". Sempre em relacionamentos de N:N por existir uma tabela intermediária, esse mapeamento é gerado automaticamente. Ele recebe o mesmo "Path" que a tabela final receberia, mas é precedido de um '.'. Esse '.' tem a finalidade de avisar todos que forem utilizar o mapeamento (como na hora de montar o retorno do banco de dados) que essa tabela não tem uma entidade relacionada, e que seu conteúdo (suas colunas) não são atribuídas em nenhuma entidade.


Na segunda parte do mapeamento é possível verificar os atributos da entidade e as colunas da tabela. note que o primeiro bloco tem o "Path" vazio. Isso indica que o "Field" pertence diretamente à entidade principal. No segundo bloco temos mapeamentos cujo "Path" está definido como "companies", isso quer dizer que do objeto principal, temos que recuperar o objeto dentro do atributo "companies" (que pode ser uma lista ou map) e então colocar os valores dos campos. A medida que objetos vão sendo relacionados em cadeia, o atributo Path receberá o caminho completo até chegar no objeto correto para criar as condições ou realizar as operações no banco de dados.


Caso um objeto tenha mais de um relacionamento com a mesma tabela, a mesma tabela aparecerá mais de uma vez nos mapeamentos mas com Alias diferentes, e daí para frente seus conteúdos não se misturam mesmo entando fisicamente na mesma tabela. Por isso no mapeamento de atributos, não temos apenas as tabelas, mas também o Alias à qual este atributo está associado.


RFWMetaCollection

A RFWMetaCollection é utilizada quando temos uma coleção de valores simples, isto é, que não são outros objetos (VO). Como uma lista de Strings, Longs, etc. Seja em uma lista ordenada ou não, ou uma map.

Nestes casos temos uma tabela auxiliar para colocar a coleção de valores, bem como o índice de ordem ou a chave da Map, como no caso do SchedulerVO e suas propriedades. A tabela para salvar os dados ficou como na imagem abaixo:


Exemplo das tabelas para salvar a RFWMetaCollection do SchedulerVO


Uma vez criada as tabelas e declarado os objetos, o mapeamento criado pelo RFWDAO fica da seguinte maneira:

MAPEAMENTO DAS ENTIDADES
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Path                                    Schema.Table                  Alias     Column              Join                JoinColumn          Entity
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                        rfw_kernel.k_scheduler        t0                                                                    br.com.rfw.kernel.services.scheduler.vo.SchedulerVO
@properties                             rfw_kernel.k_schedulerPropertyt1        idk_scheduler       t0                  id                  null
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

MAPEAMENTO DOS ATRIBUTOS
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Path                                    Field                                   Schema.Table                                                Column
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                                        id                                      (t0) rfw_kernel.k_scheduler                                 id
                                        properties@                             (t1) rfw_kernel.k_schedulerProperty                         property
                                        properties@fk                           (t1) rfw_kernel.k_schedulerProperty                         idk_scheduler
                                        properties@keyColumn                    (t1) rfw_kernel.k_schedulerProperty                         key
                                        idKey                                   (t0) rfw_kernel.k_scheduler                                 idKey
                                        taskClass                               (t0) rfw_kernel.k_scheduler                                 taskClass
                                        scheduleTime                            (t0) rfw_kernel.k_scheduler                                 scheduleTime
                                        lateExecution                           (t0) rfw_kernel.k_scheduler                                 lateExecution
                                        lastExecution                           (t0) rfw_kernel.k_scheduler                                 lastExecution
                                        repeatFrequency                         (t0) rfw_kernel.k_scheduler                                 repeatFrequency
                                        timeToRepeat                            (t0) rfw_kernel.k_scheduler                                 timeToRepeat
                                        recurrence                              (t0) rfw_kernel.k_scheduler                                 recurrence
                                        monthlyRepeatByDayOfMonth               (t0) rfw_kernel.k_scheduler                                 monthlyRepeatByDayOfMonth
                                        stopDate                                (t0) rfw_kernel.k_scheduler                                 stopDate
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Note que foi criado o mapeamento da tabela da collection mas o Path recebeu um prefixo de "@". Similar ao prefixo "." das tabelas de N:N o "@" tem a finalidade indicar que a tabela não respeita as regras das tabelas de entidades RFWVO (como ter obrigatóriamente a PK como BigInt e chamada "id").

O restante das informações se referem as mesmas informações criadas para as tabelas de outras entidades. E será utilizada para realizar os Joins e cadastros na tabela.


Já na sessão de atributos, podemos agora notar mais diferenças. Primeiro, na linha 2, o atributo "properties" que refere-se à lista de valores recebeu um sufixo "@". Esse sufixo tem a finalidade de indicar que é um atributo de lista, que receberá uma coleção/map de valores. Note que o Path continua "" já que o atributo pertente ao objeto raiz, apenas o sufixo "@" é que indica que teremos a MetaCollection. Porém, apesar do path, note que o atributo está mapeado na tabela auxiliar e não na tabela do objeto raiz.

Na linha 3 observamos outro atributo auxiliar. Trata-se do mesmo atributo "properties" mas desta vez com um sufixo "@fk". Este sufixo tem a finalidade apenas de mapear a coluna da tabela auxiliar onde encontramos o ID do objeto pai, para que possamos monta-lo adequadamente.

Temos ainda na linha 4 o mesmo atributo "properties" mas agora com o sufixo "@keyColumn". Também tem apenas a finalidade de mapear a coluna da tabela auxiliar que será utilizada como chave da Map.

Por fim, caso não seja uma Map, mas sim uma lista ordenada podemos ter ao invés da "@keyColumn" o prefixo "@sortColumn". E terá a finalidade de mapear a coluna da tabela auxiliar onde encontramos o índice do objeto para saber onde posiciona-lo na MetaCollection.


Utilização

Para utilizar o RFWDAO basta instanciar a classe passando seus atributos:

  • DataSource responsável por entregar conexões sob demanda. Você deve criar o datasource de acordo com seu projeto, sendo J2EE ou uma aplicação stand alone.
  • dialect Defina o dialeto de acordo com o banco de dados que esteja utilizando.
  • resolver interface utilizada para que sejam requisitadas informações pelo RFWDAO para a aplicação. Por exemplo, solicita através desta interface o schema que deverá ser utilizado para cada objeto. Veja mais detalhes na documentação da interface.


Vejamos um exemplo:

Java 256.png Criando o RFWDAO
  RFWDAO<MyVO> myDAO = new RFWDAO<MyVO>(MyVO.class, null, dataSource, SQLDialect.MySQL, daoResolver);


Com esse objeto RFWDAO temos acesso a manipular as entidades MyVO:

Java 256.png Criando o RFWDAO
  MyVO myVO = myDAO.findUniqueMatch(myMO, null); //Busca uma ocorrência única de um objeto

  ...

  MyVO myVO = myDAO.persist(myVO); //Persiste o objeto, inserindo se ele não tiver ID, atualizando se tiver ID.


Criando o DAOResolver

Vamos agora criar um exemplo de instância do RFWDAO passando uma instância do DAOResolver que para determinados VOs define um Schema de acordo com a sessão do usuário:

Java 256.png Criando o RFWDAO
  RFWDAO<MyVO> myDAO = new RFWDAO<MyVO>(MyVO.class, null, dataSource, SQLDialect.MySQL, (entityType, entityDAOAnn) -> {
    switch (entityDAOAnn.schema()) {
      case "_sessionSchema":
        return getUserSessionSchema(); // Método da aplicação para obter o nome do schema correto para essa sessão
      default:
        return entityDAOAnn.schema(); // Se não encontrou uma constante conhecida, utiliza o próprio schema definido no VO.
    }
  });


Vejamos agora o exemplo da definição dentro dos VO:

Definição do LocationVO:

Java 256.png LocationVO utilizando o schema próprio
  @RFWDAOAnnotation(schema = "myDefaultSchema", table = "locationTable")
  public class LocationVO extends RFWVO {
    ...
  }


Definição do AccountVO:

Java 256.png AcountVO utilizando o schema da sessão
  @RFWDAOAnnotation(schema = "_sessionSchema", table = "accountTable")
  public class AccountVO extends RFWVO {
    ...
  }


Note que nos VOs acima, o LocationVO utilizou um schema próprio, chamado de myDefaultSchema, enquanto que o AccountVO utilizou a definição batizada de _sessionSchema.

Até este momento não há nenhuma diferença no que o RFWDAO fará com essa informação. Para o LocationVO o RFWDAO procurará a tabela locationTable dentro do schema myDefaultSchema, e para o AccountVO, procurará a tabela accountTable dentro do schema _sessionSchema. Só que _sessionSchema não é um schema real no banco de dados, é apenas uma constante para que o DAOResolver encontre e troque pelo schema do usuário realizando a solicitação. Desta forma, a mesma instância da aplicação e memo VO acessa a cada solicitação dados exclusivos.


Recomendação da Factory

Como a instanciação do RFWDAO pode exigir muitas linhas de código além da chamada de alguns métodos particulares, como a obtenção de DataSource e a instância do DAOResolver, a recomendação é que a aplicação tenha uma classe de Factory para centralizar esse código e simplificar o código sempre que precisar criar um RFWDAO.


Veja a seguir o esqueleto de uma Factory sugerida:

Java 256.png Sugestão de Factory para Instanciação do RFWDAO
  public class DAOFactory {

    /**
     * Construtor privado para classe estática
     */
    private DAOFactory() {
    }

    /**
     * Cria um RFWDAO que utiliza o Resolver padrão da aplicação para definir o Schema.<br>
     *
     * @param <VO> Definição genérica do objeto que extende RFWVO para a qual este RFWDAO estará focado.
     * @param voClass Classe do objeto que extende RFWVO para a qual este RFWDAO estará focado.
     * @return RFWDAO instanciado
     * @throws RFWException Lançado em caso de falha na operação.
     */
    public static <VO extends RFWVO> RFWDAO<VO> createDAO(Class<VO> voClass) throws RFWException {
      // Implementação de um DAOResolver direto com um switch vazio para adaptação da aplicação.
      // Note que se não tiver a necessidade de schemas dinâmicos na sua aplicação, você pode passar 'null' no DAOResolver.
      // Se nenhum DAOResolver for passado para o RFWDAO, por padrão, ele utiliza a definição de schema do VO.
      return new RFWDAO<VO>(voClass, null, getDataSource(), SQLDialect.MySQL, (entityType, entityDAOAnn) -> {
        switch (entityDAOAnn.schema()) {
          default:
            return entityDAOAnn.schema();
        }
      });
    }

    private static DataSource getDataSource() throws RFWException {
      // TODO Implementar criação do DataSource
      return null;
    }
  }

Note que este é só um exemplo e que deve ser adaptado as necessidades da aplicação.

Outras Estruturas

RFWField

O objeto `RFWField` é uma classe fundamental dentro do framework para manipulação de campos de entidades em operações de banco de dados. Ele facilita a definição de campos de maneira dinâmica, permitindo operações como filtros, ordenações e agregações em consultas. Seu uso é comum em cenários onde se deseja manipular ou consultar dados no banco de forma flexível, sem a necessidade de consultas SQL escritas manualmente.

Com o `RFWField`, é possível realizar consultas utilizando funções do banco de dados como `SUM`, `DISTINCT`, `COALESCE`, entre outras, permitindo também consultas e operações mais livres, fora do modelo estrutural rígido do RFWDAO.


Função 'COUNT'

Para realizar uma consulta utilizando a função `COUNT` com o `RFWField`, você pode seguir o exemplo abaixo:

Java 256.png Consulta com COUNT
// Defina o campo utilizando a função COUNT
RFWField[] fields = new RFWField[] {
    RFWField.count()
};

// Crie um MatchObject (MO) para filtrar os dados conforme necessário
RFWMO mo = new RFWMO();
mo.equal(UserVO_._user, "admin");

// Realize a consulta
List<Object[]> result = facade.findListEspecial(uuid, UserVO.class, fields, mo, null, null, null, null);

// O resultado conterá o total de registros que satisfazem as condições do MO
Long totalCount = (Long) result.get(0)[0];

Função 'SUM'

Para realizar uma consulta utilizando a função `SUM` com o `RFWField`, você precisa especificar o campo a ser somado. Veja o exemplo abaixo:

Java 256.png Consulta com SUM
// Defina o campo utilizando a função SUM, passando o nome da coluna que deseja somar
RFWField[] fields = new RFWField[] {
    RFWField.sum("total")
};

// Realize a consulta sem filtros específicos
List<Object[]> result = facade.findListEspecial(uuid, SellVO.class, fields, null, null, null, null, null);

// O resultado conterá a soma de todos os valores da coluna especificada
BigDecimal totalSum = (BigDecimal) result.get(0)[0];


Função DISTINCT

A função `DISTINCT` é utilizada para retornar valores únicos, eliminando duplicações. Isso é útil em consultas onde você deseja evitar registros duplicados.

Java 256.png Exemplo de consulta utilizando DISTINCT
// Exemplo de consulta utilizando DISTINCT para buscar nomes de usuários únicos

RFWField distinctField = RFWField.distinct("name");

// Montando o Meta Objeto para a consulta
RFWMO mo = new RFWMO();

// Realizando a consulta
List<Object[]> resultList = facade.findListEspecial(uuid, UserVO.class, new RFWField[] { distinctField }, mo, null, null, null, null);

Neste exemplo, o campo `"name"` é consultado com a função `DISTINCT` aplicada, retornando apenas os nomes únicos encontrados no banco de dados.


Função MAX

@TODO

Função MIN

@TODO

Função AVG

@TODO

Função UPPER

@TODO

Função LOWER

@TODO

Função LENGTH

@TODO

Função TRIM

@TODO

Função SUBSTRING

@TODO

Função ABS

@TODO

Função ROUND

@TODO

Função CONCAT

@TODO