267
268
Até aqui somente conhecemos uma forma de atualizar a base de dados de nossa aplicação: as transações.
Como sabemos, um...
269
270
271
272
Por default a propriedade Business Component estará apagada. Isso significará que a lógica de negócio dada
pela transa...
273
274
Observe que se consideram alguns membros da estrutura da variável &bill, Business Component, e depois se
executa o mét...
275
Antes de executar o método Save do Business Component, o valor de BillId na estrutura de &bill será vazio.
Portanto, u...
276
277
Para gerenciar os erros terá que definir uma variável de tipo de dados SDT pré-definido (vem com a KB) Messages
(colle...
278
Aqui completaríamos o processo de geração de recibos.
Observemos primeiramente um fato importante: um data provider nã...
279
Adiantamos aqui em apresentar outro tipo de objeto interativo que estudaremos um pouco mais adiante, a Web
Panel, most...
280
Vamos supor que cada vez que se insere um novo país no sistema, é necessariamente devido a que se tem
um novo cliente ...
281
Necessitamos que a inserção do país na tabela COUNTRY e a inserção do cliente na tabela CUSTOMER se
realizam dentro de...
282
283
Se existe uma regra que chama um objeto que possui interface, isto é, form, essa regra não é incluída no BC.
Existe um...
284
285
A modificação de dados da base de dados se realiza em forma implícita: não tem um comando
específico de atualização.
P...
286
Vamos supor que temos o seguinte diagrama de Bachman genérico:
E no Source de um procedimento fazemos:
For each
C = &C...
287
Realizar um “blocking” nas operações de atualização da BD significa armazená-las em
memória e enviá-las em grupo ao DB...
288
Para eliminar dados se utiliza o comando Delete dentro do comando For each.
O comando Delete elimina o registro que es...
289
Vamos supor que queremos implementar um procedimento que faça o seguinte: para o produto cujo
código é recebido por pa...
290
O caso mais comum será ter o comando new dentro de um for each, visto quem em geral se quer
inserir registros na base ...
291
Na sintaxe apresentada, bloque_asignaciones1 é um bloque de código composto na sua maioria por
sucessivos comandos de ...
292
Nos procedimentos o único controle de integridade que se realiza automaticamente é o controle de
duplicados. O control...
Próximos SlideShares
Carregando em…5
×

12 db atualizacao-curso-gxxbr

477 visualizações

Publicada em

0 comentários
0 gostaram
Estatísticas
Notas
  • Seja o primeiro a comentar

  • Seja a primeira pessoa a gostar disto

Sem downloads
Visualizações
Visualizações totais
477
No SlideShare
0
A partir de incorporações
0
Número de incorporações
2
Ações
Compartilhamentos
0
Downloads
15
Comentários
0
Gostaram
0
Incorporações 0
Nenhuma incorporação

Nenhuma nota no slide

12 db atualizacao-curso-gxxbr

  1. 1. 267
  2. 2. 268 Até aqui somente conhecemos uma forma de atualizar a base de dados de nossa aplicação: as transações. Como sabemos, uma transação determina a ou as tabelas requeridas para armazenar sua informação. Por sua vez, ao ser gerada, se converte no programa que implementa a lógica de inserção, eliminação e modificação de dados nessas tabelas, de forma totalmente transparente para o programador. Também a transação permite definir todas as regras de negócio que os dados associados deverão cumprir. O programa gerado se encarrega de executá-las. Assim, se em uma transação temos uma ou várias regras Error, que se disparam ao satisfazer determinadas condições, essas regras estarão incluídas no código gerado, e não se permitirá atualizar a base de dados até as condições que disparam essas regras deixarem de satisfazer para os dados que estão manipulando nessa oportunidade. Assim mesmo, como vimos, as transações asseguram o controle de integridade referencial, muito importante para assegurar a consistência da base de dados. Mas as transações não cobrem todas as necessidades referente a atualização de dados. Também será necessário uma forma não interativa, batch, para realizar atualizações sobre as tabelas. Para isso existem duas alternativas: atualização utilizando o que se conhece como Business Component, absolutamente relacionado com as transações, ou utilizar comandos específicos para tal fim dentro de objetos de tipo Procedure. Depois veremos que os Business Components permitem grande flexibilidade, visto que permitem atualizar a base de dados de qualquer forma: tanto interativa como não interativa. Em seguida introduziremos os Business Components e depois os comandos de atualização direta dentro dos Procedimentos.
  3. 3. 269
  4. 4. 270
  5. 5. 271
  6. 6. 272 Por default a propriedade Business Component estará apagada. Isso significará que a lógica de negócio dada pela transação, somente será utilizada dentro da própria transação, e não ao se criar um tipo de dados com o mesmo nome da transação, Bill, que permita encapsular a lógica da transação para utilizá-la a partir de outros objetos.
  7. 7. 273
  8. 8. 274 Observe que se consideram alguns membros da estrutura da variável &bill, Business Component, e depois se executa o método Save (inexistente em SDTs). Isto é equivalente ao que ocorria de forma interativa se o usuário final insere esses mesmos valores nos atributos correspondentes do form da transação, e depois pressionar o botão ‘Confirm’. Portanto, da mesma forma que ocorre com a transação, deverão ser disparadas as regras e serem realizadas os controles de integridade referencial. O que acontecerá com esse conjunto de dados se tentar gravar mediante a transação? A gravação é impedida, devido que está cumprindo a condição da segunda regra de error. Por outro lado, se não existisse na tabela CUSTOMER um cliente com identificador 3456, a integridade referencial vai falhar e assim tampouco deixaria de ingressar o registro na tabela BILL da base de dados.
  9. 9. 275 Antes de executar o método Save do Business Component, o valor de BillId na estrutura de &bill será vazio. Portanto, uma vez que se execute o Save, como o atributo BillId era autonumber na tabela BILL, se executa a propriedade a nível da tabela e o registro será inserido com o número seguinte ao último dado. No caso de BillDate, da mesma forma, como não se atribui valor na estrutura de &bill, antes do Save estará vazio, mas quando este se execute, será disparada a lógica da transação, e em particular a regra Default( BillDate, &today ) declarada. Portanto no registro inserido na base de dados, o valor do atributo BillDate corresponderá a data de hoje. Para BillAmount poderíamos fazer a mesma análise. Mas a diferença do caso anterior, não tem regra que atribui valor, pela qual se insere o registro com valor vazio neste atributo. Se atribuir valor para &bill.CustomerName não será considerado na hora da inserção, visto que CustomerName é um atributo inferido, e não tem regra Update que permita modificá-lo. O mesmo acontece para atributos definidos a nível da estrutura da transação mas que foram fórmulas. Isto é, são atributos virtuais na transação, e também são a nível do Business Component. Seu valor é disparado.
  10. 10. 276
  11. 11. 277 Para gerenciar os erros terá que definir uma variável de tipo de dados SDT pré-definido (vem com a KB) Messages (collection). Quando se executam os métodos: Save, Check, Load, Delete se disparam e carregam as mensagens gerados automaticamente por GeneXus assim como as regras Msg e Error definidos na transação. Se recomenda que sempre se recupere a lista destas mensagens e se faça uma “gerência de erros”. As mensagens mais comuns geradas automaticamente por GeneXus são: As regras Msg e Error aceitam em sua definição além do parâmetro com a mensagem a mostrar, um segundo parâmetro que define o Identificador da mensagem. O objetivo é que quando se execute a transação como Bussiness Component, e se obtenha a lista de mensagens ocorridas depois de executar uma ação sobre a base de dados, se tenha de cada mensagem, além da mensagem em si, seu identificador, sendo possível assim avaliar o identificador da mensagem para codificar o comportamento como conseqüência: Msg|Error(<mensaje>, <Id do mensaje>) Exemplos de <Id do mensaje>: "1", "2", "Error1", "Error2" ou uma descrição como ser "CustomerNameCannotBeEmpty", etc. Se não especificar um identificador para a mensagem,, terá que perguntar pelo texto da mensagem. Nota: Para as mensagens geradas automaticamente por GeneXus, o Id é sempre em Inglês (independentemente do idioma selecionado no modelo).
  12. 12. 278 Aqui completaríamos o processo de geração de recibos. Observemos primeiramente um fato importante: um data provider não somente pode devolver um SDT simples ou coleção, mas também um BC simples ou coleção. Importante: o BC devolvido somente poderá inserir na base de dados. Isto é, um Data Provider somente permite satisfazer sua estrutura, para depois fazer uma operação de INSERT sobre a base de dados. Não será possível fazer uma atualização ou eliminação com um BC carregado via Data Provider. Observe por outro lado como foi definido a variável &message de tipo de dados Messages.Message, correspondente a os itens do SDT pré-definido Messages devolvido pelo método GetMessages do BC. Uma vez que obtemos e ingressamos na tabela BILL todos os recibos correspondentes ao faturamento, deveríamos percorrer as faturas consideradas, e modificar o valor de seu atributo InvoicePendingFlag, passando-o para False. Lembremos que o valor deste atributo se utiliza para não processar duas vezes uma mesma fatura. Lembremos que este valor se utiliza no Data Provider no cálculo do elemento BillAmount: sum( InvoiceAmount, InvoiceDate >= &start and InvoiceDate <= &end and InvoicePendingFlag ) Deixaremos pendente esta última parte para quando estudarmos, umas páginas mais adiante, as formas de atualização direta, dentro de procedimentos exclusivamente, dos registros das tabelas.
  13. 13. 279 Adiantamos aqui em apresentar outro tipo de objeto interativo que estudaremos um pouco mais adiante, a Web Panel, mostramos um objeto deste tipo em execução. Sua função será pedir ao usuário final um par de valores, que armazenem nas variáveis correspondentes de tipo Date (&startDate e &endDate) e depois, quando o usuário pressione o botão associado ao Evento Enter da Web Panel, se execute seu código. Observe que o código é idêntico ao do procedimento visto antes. Com este exemplo pretendemos mostrar que mediante um Business Component pode se atualizar a base de dados a partir de qualquer objeto GeneXus.
  14. 14. 280 Vamos supor que cada vez que se insere um novo país no sistema, é necessariamente devido a que se tem um novo cliente desse país. Vamos supor que como requerimento, além disso, não deve ficar abaixo nenhuma circunstância ingressado um país sem pelo menos um cliente associado. Tal como temos implementada a aplicação, isto não se está controlando. Como se insere um país e um cliente para o mesmo? O usuário deve executar a transação Country, ingressar o país. Depois abrir a transação Customer, e ingressar os dados do cliente e confirmar. Mas o que acontece se cair o sistema quando se está ingressando o cliente? Ficará o país ingressado (lembrar que cada transação por default faz commit ao final).
  15. 15. 281 Necessitamos que a inserção do país na tabela COUNTRY e a inserção do cliente na tabela CUSTOMER se realizam dentro de uma mesma UTL. Como temos visto, as operações efetuadas por 2 transações não podem ser incluídas numa única UTL. Por esta razão, necessitaremos fazer a inserção dentro da mesma transação. Como? Dentro da transação Country, definimos uma variável &customer, de tipo business component Customer (para isso, teremos previamente que ter setado a propriedade Business Component da transação Customer, de tal maneira que se crie este tipo de dados na KB). Umaa vez efetuado isto, simplesmente inserindo no form a variável (da mesma forma que acontecia com um SDT), GeneXus colocará controles para cada um dos membros da estrutura: &customer.CustomerId, &customer.CustomerName, &customer.CustomerAddress,etc. As variáveis por default nas transações são de saída, terá que especificar regra Accept para que possam ser escritas em execução e o possa ingressar valores para as mesmas. O Save do customer deverá realizar-se necessariamente depois de inserir o país em sua tabela (do contrário a integridade referencial falha). Portanto nas regras: &customer.Save() on AfterInsert; Sabemos que neste momento o registro correspondente ao país já terá sido gravado, e depois neste Save... na continuação, o commitautomático.
  16. 16. 282
  17. 17. 283 Se existe uma regra que chama um objeto que possui interface, isto é, form, essa regra não é incluída no BC. Existe uma forma de especificar que uma regra declarada na transação não seja aplicada quando se executa a transação, mas somente quando se executa o Business Component associado: é qualificando a regra com [BC]. Exemplo: [BC] Default( BillDate, &today); Se quiser qualificar de uma só vez um conjunto de regras: [BC] { regla1; regla2; ... regran; } O mesmo vale para eventos. Analogicamente, existem qualificadores para indicar que uma regra somente se execute se estiver correndo a transação com seu form web: [WEB].
  18. 18. 284
  19. 19. 285 A modificação de dados da base de dados se realiza em forma implícita: não tem um comando específico de atualização. Para atualizar um ou vários atributos de uma tabela se utiliza o comando For each, e dentro do mesmo o comando de atribuição Podem ser realizadas várias atualizações dentro do mesmo For each, podendo estes pertencer tanto a própria tabela base como a tabela estendida. O exemplo que apresentamos completa o que havíamos iniciado a respeito ao processo de geração de recibos. Observe que aqui se está percorrendo a tabela INVOICE, filtrando por InvoiceDate, por InvoicePendingFlag, assim como por CustomerId (na listagem de navegação se observa que mesmo não tendo sido especificado order, ao receber no atributo CustomerId, que é foreing key, se ordena por este atributo para otimizar). Dentro do for each, para cada fatura que cumpre as condições, se atualiza o valor do atributo InvoicePendingFlag. Aqui poderiam atualizar-se não somente esse atributo, mas qualquer da própria tabela INVOICE, ou de sua estendida, com as exceções indicadas na continuação.
  20. 20. 286 Vamos supor que temos o seguinte diagrama de Bachman genérico: E no Source de um procedimento fazemos: For each C = &C E = &E D = &D Endfor Aqui a tabela base do For each será claramente a de chave primária A e dentro do For each estamos atualizando tanto atributos da própria tabela base como da estendida. Em que momento ocorre efetivamente a atualização dos registros envolvidos? A atualização não ocorre quando encontra um comando de atribuição dentro do For each, mas sim depois que se encontram todos, para cada instancia da tabela base, isto é,quando se chega ao Endfor para cada iteração. Como vimos antes, não podemos atualizar dentro do comando For each atributos da chave primária. Todavia poderíamos querer atualizar um atributo que não está definido como chave primária, está definido como chave candidata (mediante um índice unique). Se o atributo é chave candidata, deve controlar-se que não se dupliquem seus valores, caso encontre duplicado, não permite fazer a atualização. Se desejar tomar uma ação no caso disso ocorrer, o comando For each agrega a cláusula when duplicate. Somente tem sentido se existe alguma chave candidata para esse For each.
  21. 21. 287 Realizar um “blocking” nas operações de atualização da BD significa armazená-las em memória e enviá-las em grupo ao DBMS. Ao invés de interagir com o DBMS em cada operação de atualização, a interação tem lugar somente cada N operações de atualização, onde N é o número que se estabelece na cláusula Blocking. Não será o caso de nosso exemplo, mas o que aconteceria se estiver fazendo uma atualização massiva que inclui algum atributo chave candidata da tabela, e se encontram duplicados para alguns dos registros do grupo de 1000 que se está processando? Nesse caso, uma vez chegado o buffer com as 1000 atualizações, ao enviar ao BD o comando UPDATE do grupo, saltará o error de duplicados e se iterará sobre o grupo, realizando um comando UPDATE individual de BD, um por um.
  22. 22. 288 Para eliminar dados se utiliza o comando Delete dentro do comando For each. O comando Delete elimina o registro que estiver posicionado no momento dado. É por isso, que não pode aparecer “solto” dentro do Source. Deve estar dentro de um comando For each, cuja tabela base seja a tabela que se quer eliminar registros. Somente se eliminam os registros da tabela base, não da estendida. Sei desejamos eliminar todas as faturas anteriores a uma data informada, podemos programar um procedimento: For each where InvoiceDate <=&date For each defined by InvoiceDetailQuantity DELETE // se eliminam as linhas Endfor DELETE //depois de eliminar as linhas se elimina o cabeçalho Endfor Para uma eliminação mais eficiente, dependendo do número de registros da base de dados, convêm agregar cláusula Blocking com um fator de bloqueo, N, adequado. Por exemplo, se agregar “Blocking 1000” a eliminação física não é realizada em cada iteração, mas sim a cada 1000 vezes que se chegue ao Endfor, se eliminarão o grupo de 1000 registros em um único acesso a Base de Dados (ao invés de 1000).
  23. 23. 289 Vamos supor que queremos implementar um procedimento que faça o seguinte: para o produto cujo código é recebido por parâmetro, insere um novo preço (também recebido por parâmetro) em sua lista de preços, para a data correspondente ao dia em que se executa o procedimento. Este deve criar um novo registro na tabela PRODUCTPRICELIST, que está composta pelos atributos ProductId, ProductPriceListDate e ProductPriceListPrice, sendo sua chave primária composta, conformada por ProductId e ProductPriceListDate. Para isso se utiliza o comando new que escrevemos acima. Observemos que dentro do mesmo aparecem comandos de atribuição, onde se dá valor aos atributos da tabela que se quer inserir o registro. Cada vez que GeneXus encontra um new, deve determinar a tabela na qual se realizará a inserção (tabela base do new). É determinada a partir dos atributos que aparecem dentro do comando new, do lado esquerdo numa atribuição. O único controle que realiza, é o de duplicados. Em nosso caso, se existir um registro com os valores de ProductId e ProductPriceListDate, não se realizará a inserção. Se programar a cláusula when duplicate, se atualiza o registro encontrado. Observemos que para realizar a atualização do atributo, a atribuição deve estar dentro de um comando For each. Se não colocamos o For each não se realizará a atualização do preço para esse registro, isto é, é como se não tivesse incluído cláusula when duplicate. Se quiser inserir um registro para o qual exista chave candidata duplicada, também se controla e não se realizará a inserção. Em caso de existir cláusula when duplicate, se executa.
  24. 24. 290 O caso mais comum será ter o comando new dentro de um for each, visto quem em geral se quer inserir registros na base e cálculos efetuados em função de outros. O mesmo processo de geração de recibos que havíamos resolvido anteriormente utilizando Data Provider e Business Component, poderíamos realizá-lo com um procedimento que utilize o comando new de inserção. Qual alternativa você escolheria? Lembre que com os comandos de atualização dentro de procedimentos, o único controle que será realizado é o de duplicados. Se as inserções são muitas, assim como vimos para o caso das atualizações, dispomos da cláusula blocking para ir guardando em um buffer e depois fazer a inserção de todos os registros uma vez. Em nosso caso não falhará a inserção, devido que a chave primária é autonumber, e que não temos chaves candidatas (através de índices unique) na tabela de recibos (BILL). Mas se não for o caso, como as inserções vão sendo realizadas no buffer até chegar a 1000, não é possível até completar o buffer saber se alguma operação falhará. Quando se executa o INSERT massivo da base de dados, ali se pode falhar alguma inserção. Nesse caso, se itera nos elementos do buffer, executando o INSERT simples, um por um. Se existe cláusula When duplicate no New, então pelos registros que falhem, se executa a cláusula.
  25. 25. 291 Na sintaxe apresentada, bloque_asignaciones1 é um bloque de código composto na sua maioria por sucessivos comandos de atribuição (aqui se atribui valor aos atributos da tabela na que vai inserir o registro, podem também atribuir-se valores a variáveis). A cláusula Defined By opcional, se incorpora os mesmos efeitos que no comando For each: ajudar a determinar a tabela base. A tabela base do new se obtêm dos atributos do Defined By e os que apareçam do lado esquerdo de atribuições dentro do bloque_asignaciones1. Aqui não se utiliza a tabela estendida. No caso de que o comando new esteja dentro de uma cláusula repetitiva (ex. For each), é possível reduzir o número de acessos a base de dados usando a cláusula blocking. O comando new realiza um controle de duplicados, de tal maneira que não se permite inserir um registro que já exista na tabela. A cláusula when duplicate do comando permite programar a ação em caso de que o registro já exista na tabela base (tanto por chave primária como por chave candidata). Normalmente, ao ocorrer o que acabamos de falar, se quer atualizar alguns dos atributos de dito registro. Para isso, em bloque_asignaciones2 se realizam tais atribuições, mas como o que se faz é atualizar um registro (e não inserir um novo), estas atribuições aparecem entre “For each – Endfor”, como vimos, as atualizações somente podem ser realizadas dentro de um For each. Se a cláusula when duplicate para um new não for especificada, se o registro que se quer inserir se encontra duplicado não se realizará nenhuma ação e a execução continuará no comando seguinte. Isto é, como não pode inserir o registro porque já existe um, não faz nada e segue adiante com o próximo comando.
  26. 26. 292 Nos procedimentos o único controle de integridade que se realiza automaticamente é o controle de duplicados. O controle de integridade referencial fica por conta do programador, o que não ocorre nas transações (em um Business Component). Fica claro da numeração mostrada acima que os Business Components oferecem todas as garantias e constituirão a forma de atualização privilegiada. Cabe perguntar-se então: quando atualizar utilizando estes comandos? A resposta é: quando a performance for um problema. Um exemplo discutível é o que utilizamos para alterar o valor do atributo InvoicePendingFlag a False. Não havia nenhuma regra que o implicara, não envolvia integridade referencial, nem duplicados e podia envolver milhares de registros. Qual seria a alternativa? Prender a propriedade Business Component da transação Invoice, e no procedimento MarkInvoiceAsNotPending definir variável &invoice com esse tipo de dados e depois: for each where InvoiceDate >= &startDate where InvoiceDate <= &endDate where InvoicePendingFlag &invoice.Load ( InvoiceId) &invoice.InvoicePendingFlag = False &invoice.Save() endfor Mas esta solução será mais ineficiente que a atualização direta utilizando “blocking factor”, sabendo que milhares de faturas deverão ser atualizadas.

×