O nosso próximo Caso de Uso não irá se encaixar em nenhum padrão do jcompany Patterns & Methods e esta é uma situação de se esperar.



Documentos relacionados
Como já foi muito bem detalhado no Capítulo IV, o jcompany Developer Suite pode ser

Conteúdo. Disciplina: INF Engenharia de Software. Monalessa Perini Barcellos. Centro Tecnológico. Universidade Federal do Espírito Santo

Orientação a Objetos

Manual do Sistema "Vida em Mão - Controle Financeiro Para PALM" Editorial Brazil Informatica

Aula 5. Carlos Eduardo de Carvalho Dantas

Módulo 5 JPATransaction Camadas Turma Turma TurmaBC TurmaBC TurmaBC TurmaBC

Lição 1 - Criação de campos calculados em consultas

Engenharia de Software III

ATRIBUTOS PRIVADOS 6. ENCAPSULAMENTO MÉTODOS PRIVADOS MÉTODOS PRIVADOS

Figura C13.1. Diagrama de Casos de Uso para UC004 Registrar Proventos e Descontos!.

UFG - Instituto de Informática

MICROSOFT EXCEL AVANÇADO

CAPÍTULO 3 - TIPOS DE DADOS E IDENTIFICADORES

PROGRAMAÇÃO ORIENTADA A OBJETOS -TRATAMENTO DE EXCEÇÕES. Prof. Angelo Augusto Frozza, M.Sc. frozza@ifc-camboriu.edu.br

1.6. Tratamento de Exceções

SUMÁRIO Acesso ao sistema... 2 Atendente... 3

Gestão de Ativos. Manual do Usuário. Treinamento Fase 1 (TRN 01)

FERRAMENTAS DE COLABORAÇÃO CORPORATIVA

Microsoft Access XP Módulo Um

1. Tela de Acesso pg Cadastro pg Abas de navegação pg Abas dados cadastrais pg Aba grupo de usuários pg.

MANUAL TISS Versão

TUTORIAL MRV CORRETOR

2 Diagrama de Caso de Uso

8VDQGR5HSRUW0DQDJHUFRP&ODULRQH3RVWJUH64/ -XOLR&HVDU3HGURVR 8VDQGRSDUkPHWURV

Guia Site Empresarial

Análise de Ponto de Função

Tutorial contas a pagar

Validando dados de páginas WEB

Software. Gerenciamento de Manutenção

Guia de Fatores de Qualidade de OO e Java

Manual de Rotinas para Usuários. Advogados da União. Procuradoria da União no Estado do Ceará PU/CE SAPIENS. Sistema da AGU de Inteligência Jurídica

Primeiros Passos para o Simulador de Ações do FinanceDesktop. Parte A INICIANDO E CONFIGURANDO (5 passos)

Manual Operacional SIGA

Trecho retirando do Manual do esocial Versão 1.1

Pag: 1/20. SGI Manual. Controle de Padrões

Curso de Licenciatura em Informática

Tópicos em Engenharia de Computação

Agendamento para Importação de Notas Fiscais

MANUAL DO SISTEMA. Versão 6.04

EXEMPLO DE COMO FAZER UMA MALA DIRETA

INF 2125 PROJETO DE SISTEMAS DE SOFTWARE Prof. Carlos J. P. de Lucena

Treinamento de. Linx Pos

UNIVERSIDADE FEDERAL DO PARANÁ. CURSO: Ciência da Computação DATA: / / 2013 PERÍODO: 4 o.

MANUAL DE UTILIZAÇÃO

Manual de Cobrança. Código Nome De Até 1 Jose da Silva a Jz 2 Ana Maria k Pz 3 Marcelo q zz

Reuso com Herança a e Composiçã

TRIBUNAL DE JUSTIÇA DO PARANÁ PROJUDI REFORMULAÇÃO DE CUMPRIMENTOS - MANDADOS

Manual SAGe Versão 1.2 (a partir da versão )

Está apto a utilizar o sistema, o usuário que tenha conhecimentos básicos de informática e navegação na internet.

CRIANDO BANCOS DE DADOS NO SQL SERVER 2008 R2 COM O SQL SERVER MANAGEMENT STUDIO

15/03/2010. Análise por pontos de função. Análise por Pontos de Função. Componentes dos Pontos de Função. Componentes dos Pontos de Função

UFG - Instituto de Informática

1 Inicie um novo. Guia de Referência Rápida de Gerenciamento de Projeto para o Project projeto

Procedimentos para Reinstalação do Sisloc

Este Procedimento Operacional Padrão define as etapas necessárias de como fazer o Cadastro de Avisos Automáticos no Sistema TOTVS RM.

Objetivos. Página - 2

8. Outros tipos de Transação (Modo de Transação de Autoconfirmação e Modo Implícito)

Sistema de Controle de Solicitação de Desenvolvimento

MANUAL DE UTILIZAÇÃO SISTEMA DE CADASTRO INTRANET

MODULO DE GESTÃO MANUTENÇÃO DE MATRÍCULA. O módulo de Gestão tem por objetivo gerenciar as atividades que ocorrem durante um ano letivo.

Introdução a Java. Hélder Nunes

CONVENÇÃO DE CÓDIGO JAVA

NOVIDADES DO JAVA PARA PROGRAMADORES C

Arquitetura de Rede de Computadores

Controle do Arquivo Técnico

Parte I. Demoiselle Mail

SISTEMA TYR DIAGRAMAS DE CLASSE E SEQUÊNCIA Empresa: Academia Universitária

Manual de Utilização

SISTEMA DE PRODUTOS E SERVIÇOS CERTIFICADOS. MÓDULO DO CERTIFICADOR MANUAL DE OPERAÇÃO Versão 2.4.6

Manual de Utilização

MANUAL PAPELETA MOTORISTA Criado em: 15/02/2013 Atualizado em: 12/11/2014

WF Processos. Manual de Instruções

UNIVERSIDADE FEDERAL DO PARANÁ UFPR Bacharelado em Ciência da Computação

Especificação de Requisitos

Manual de Utilização Sisamil - Sistema Integrado de Saúde Amil Manual de Utilização 1 54

Tutorial para atividades do Trabalho Prático da disciplina DCC 603 Engenharia de Software

Manual do Ambiente Moodle para Professores

2013 GVDASA Sistemas Cheques 1

PROGRAMAÇÃO AVANÇADA -CONCEITOS DE ORIENTAÇÃO A OBJETOS. Prof. Angelo Augusto Frozza, M.Sc. frozza@ifc-camboriu.edu.br

PAINEL GERENCIADOR DE S

Livro Caixa. Copyright ControleNaNet

MANUAL ITCMD - DOAÇÃO

MANUAL DE UTILIZAÇÃO MASTER VENDAS

CRIANDO TEMPLATES E LEGENDAS

MODULO DE GESTÃO MANUTENÇÃO DE MATRÍCULA. O módulo de Gestão tem por objetivo gerenciar as atividades que ocorrem durante um ano letivo.

Feature-Driven Development

Manual do Google agenda. criação e compartilhamento de agendas

Ajuda do Sistema Aquarius.

UML Aspectos de projetos em Diagramas de classes

ProJuris 8: Manual de Integração com Provedores de Recortes

Resolução da lista de exercícios de casos de uso

Tabela e Gráficos Dinâmicos Como estruturar dinamicamente dados no Excel

Manual Geral do OASIS

Transcrição:

A6Regras de Negócio & Batch Capítulo 17 Implementação do Caso de Uso UC005 Calcular Folha de Pagamento! - Analisando a Especificação Caso de Uso Principal O nosso próximo Caso de Uso não irá se encaixar em nenhum padrão do jcompany Patterns & Methods e esta é uma situação de se esperar. Toda aplicação possuirá algum percentual de demandas deste tipo - não generalizáveis por frameworks horizontais, ou porque são Casos de Uso que não possuem cenários previsíveis, repetitivos, passíveis de padronização; ou porque são, fundamentalmente, agrupamentos de regras de negócio (como é o nosso caso). Ainda assim reutilizaremos diversos serviços úteis do jcompany FS Framework. Afinal, como vimos no capítulo 1, o jcompany Developer Suite é muito mais que os geradores do jcompany IDE, utilizados para Casos de Uso Padrões. Figura E17.1. Diagrama de Caso de Uso para UC005 Calcular Folha de Pagamento!. Como não é um Caso de Uso Padrão, esta especificação requer maiores explicações. Note que este é um Caso de Uso disparado por algum mecanismo de temporização, ou de escalonamento, simbolizado pelo Ator com a palavra reservada Tempo. Nestes casos, a interatividade em si do Ator com o Caso de Uso é trivial, sendo o cenário basicamente definido por um passo de disparo e um segundo passo de reação da aplicação. A descrição de cenário para este Caso de Uso UC005 está, portanto, definida da seguinte forma no repositório da ferramenta CASE: 1. O escalonador automático dispara o Cálculo da Folha, todo dia 3 de cada mês, a partir de 01h00min da madrugada. 2. A aplicação recupera todos os funcionários e executa cálculo para cada um, segundo regras definidas em REQ001 - Cálculo de Folha.

Capítulo E17 Veja que o cálculo da folha de pagamento em si é definido em um requisito à parte. O cálculo que iremos exemplificar é bem simplificado e altamente fictício, mas suficiente para nossos propósitos. Ele é reproduzido abaixo: Para cada funcionário com horas trabalhadas informadas para o período de referência, imediatamente anterior ao corrente (último mês), o cálculo deve ser feito da seguinte forma: 1. Pegar o último salário base de seu histórico funcional. 2. Calcular o salário inicial tendo como base 22 dias úteis, e realizando cálculo proporcional (regra de três) com o número de dias efetivamente trabalhados informados em provento do tipo DT. 3. Em seguida calcular o salário bruto somando os proventos gerais e subtraindo os descontos gerais, dos lançamentos de Proventos e Descontos para o mês. Exceções a tratar: 3.1. Se o número de dias trabalhados informado for mais que 22, gerar erro 'Numero de dias ultrapassa o possível para o período'. 3.2. Se o salário bruto for negativo, gerar erro 'Cálculo de salário negativo para funcionário [nome - cpf]'. 3.3 Se o cálculo estiver correto, gravar ocorrência em ProventoDesconto com natureza SL, com o valor calculado. 4. Calcular o IR com base no salário bruto, descontando 15% após deduzir 200,00 para cada dependente. Ex: Para um funcionário com salário de 1.400,00 com dois dependentes, o IR deverá ser 15% de 1.000,00, ou seja, 150,00. 4.1. Se a dedução for maior que o salário, gravar IR com zero. 4.2. Gravar ocorrência em ProventoDesconto com natureza IR. 5. Calcular salário liquido final, subtraindo o IR do salário bruto e gravando-o em Provento com tipo SL. 6. Se alguma exceção ocorrer durante um cálculo especifico, ele deve ser interrompido e o erro enviado por e-mail para folha@acme.com.br, além de gravado em log de erro. O processamento deve continuar para os demais. 7. Ao final, se todos os funcionários possuírem o salário calculado, a aplicação gera um fechamento de período, atualizando anomesfechamento. Uma mensagem de sucesso também deve ser enviada para o log. Foi definida uma Colaboração com o estereótipo plcbatch, que não nos traz muita informação, diferentemente das Colaborações Padrões anteriores. A marcação indica apenas que deveremos utilizar um disparo escalonado para esta rotina e implementar sua orquestração a partir de uma classe de negócio/domínio dedicada a processamento de cálculos, chamada CalculoFolha. O grafo de entidades envolvido costuma ser grande em programações batch. Em nosso exemplo, ele está representado pela Figura E17.2. Figura E17.2. Grafo de Entidades, envolvido no Caso de Uso UC005 Calcular Folha de Pagamento!.

Regras de Negócio e Batch Uma nova classe FolhaPagamento foi definida para abrigar o valor do último período de fechamento. Esta classe foi estereotipada como plcprefaplicacao, pois contém somente um objeto e é um padrão definido pelo jcompany Patterns & Methods. O jcompany IDE, inclusive, pode gerar um Caso de Uso Padrão para sua manutenção interativa, se preciso for não é o nosso caso, já que iremos gravar o anomesultimofechamento programaticamente. Note que modelamos a referência à classe FolhaPagamento em ProventoDesconto como uma variável de classe dinâmica. Esta é uma conceituação opcional, mas, fazendo deste modo, conseguimos rastrear a classe ProventoDesconto com FolhaPagamento. Finalmente uma parte do grafo de Funcionário foi incluída pois o número de seus dependentes e seu último salário serão ambos utilizados no cálculo. É uma boa prática de modelagem explicitar claramente o grafo de Entidades envolvidas no Caso de Uso. - Analisando a Especificação Extensão Existe uma Extensão definida para nosso Caso de Uso, utilizada por usuários com papel FolhaPagamento, que permite a estes usuários informarem um período não encerrado (somente o mês corrente ou o anterior, por exemplo) e dispararem o mesmo cálculo que roda em batch, de forma interativa. Os requisitos são os mesmos, mas há uma restrição nova, já que o período agora será informado: RES01. Verifica Período. O sistema verifica se o período é válido, ou seja, se é maior que o 'anomesfechamento'. Se não for, impede o cálculo e exibe mensagem 'Não é permitido fazer cálculo para este período, pois ele já foi encerrado'. O formulário a ser utilizado é, também, fora do padrão, apresentando botão de disparo ao lado do campo de período e as mensagens abaixo do botão de disparo. As mensagens estão em cores distintas, utilizando verde para o número de calculados com sucesso e vermelho para o número com problemas e mensagens de erro. As mensagens de erro, além de exibidas para o usuário, também devem ser enviadas por e-mail e log. A Colaboração que irá implementar a extensão possui estereótipo plcontrole, conhecido também como Controle Simples, que é na verdade um padrão livre do jcompany, se é que se pode dizer assim. É uma Colaboração utilizada quando se deseja realizar implementações livremente, a partir da camada Controle, mas ainda utilizando leiautes Facelets e possivelmente anotações de metadados para acionar reúso de recursos do framework. Veremos a implementação desta extensão no próximo capítulo. - Obtendo a Entidade em Java Vamos criar a única Entidade nova FolhaPagamento manualmente através do Eclipse, conforme a Figura E17.3. 1. Crie um novo pacote folhapagamento, abaixo de entidade em rhtutorial_commons, e digite o código abaixo. Figura E17.3. Novo pacote folhapagamento, abaixo de entidade e classe FolhaPagamento. - Gerando Mapeamento para Classe - Objeto-Relacional VI 1. Edite a classe FolhaPagamento e aperte Control+N para acionar as opções de plugins. Selecione 01 Mapeamento Objeto-Relacional. As informações importantes para o caso do mapeamento O-R de FolhaPagamento se encontram na Figura E17.4.

Capítulo E17 Figura E17.4. Mapeamento Objeto-Relacional para "FolhaPagamento". Modifique apenas a indicação de lookup (para usar anomesultimofechamento no tostring da Classe) e tamanho 8 (desnecessário para o JPA, mas pode ser útil se formos gerar formulários que contenham esta data). 2. Após a geração, edite a classe FolhaPagamentoEntity e ajuste o método "tostring" para exibir a data com máscara MM/yyyy, utilizando o SimpleDateFormat, por exemplo. Figura E17.5. Ajuste na exibição da Data em FolhaPagamentoEntity. Note que o jcompany sempre gera uma NamedQuery buscando o "Object-Id + Propriedades de Lookup", com sufixo padrão querysellookup. A query nomeada será útil em tempo de programação, para recuperarmos esta entidade e formatarmos mensagens de erro com a data. - Implementando Classes de Negócio Programação de Camada Modelo I Vamos realizar então nossa primeira programação em camada Modelo, utilizando classes típicas para Regras de Domínio/Negócio. Crie a classe CalculaFolha no projeto rhtutorial_model definindo um novo sub-pacote folhapagamento abaixo de modelo. Figura E17.6. Criação de classe de CalculaFolha na camada Modelo. #1. Serviços de negócio ficam no projeto rhtutorial_model.

Regras de Negócio e Batch #2. Ao criar a classe, após posicionar no pacote com.empresa.rhtutorial.model e acionar New Java Class com o clique direito do mouse, pode-se complementar o pacote origem com extensões como.folhapagamento, para que esta extensão seja criada em conjunto com a classe. #3. Digite o nome da classe, conforme especificado. 2. Implemente o algoritmo principal de orquestração da Regra, conforme a Figura E17.7. Figura E17.7. Codificação do método principal de orquestração. #1. Caso não se esteja utilizando um ambiente que proveja rastreabilidade entre requisitos e código (como uma suíte de ALM), a inclusão do identificador no Javadoc é recomendada. Na camada Modelo, em regras do negócio, o Javadoc é especialmente importante. #2. Definimos um objeto de exceção especializado para encapsular exceções internas geradas em nosso cálculo. Vamos discuti-los no próximo tópico. #3. Perceba que definimos o método como protected! Como somente iremos acioná-lo via escalonador, esta é uma restrição útil. Veremos a técnica de disparo mais a frente. Poderíamos também não receber argumentos, inferindo o mês de referência dentro de nosso método que seria o mês anterior ao que estamos. Porém, deste modo o tornamos menos reutilizável. Deixaremos pré-condições relacionadas à temporalidade em si para a tarefa de escalonamento, que descobrirá o período e nos informará. Além do mais, já sabemos que haverá um disparo interativo informando mês de referência para cálculo, o que reforça esta nossa estratégia. #4. Vamos adiar a recuperação da lista de funcionários, por enquanto, para nos concentrarmos no algoritmo de orquestração em si. Mas uma coisa é certa: iremos precisar de um serviço de persistência que traga uma coleção de subgrafos de Funcionario contendo somente aqueles que possuem DT (Dias Trabalhados) informados para o período. Benefício do JPA: em nossas regras de negócio não iremos usar Result Set JDBC ou qualquer outro conjunto relacional como fonte de dados, mas nosso modelo de Entidades de Domínio, expresso pela declaração do tipo de retorno como List<Funcionario>. Isso tornará nossas regras mais legíveis e manuteníveis, um dos grandes benefícios do JPA. #5. Iremos totalizar os funcionários calculados corretamente, conforme requisitado.

Capítulo E17 #6. Manteremos uma lista de exceções geradas em cálculos individuais de funcionários para encapsular as causas de erro associadas a alguns dados do funcionário. #7. Os elementos de programação tipicamente encontrados em orquestradores de regras de negócio serão laços e condicionais. #8. É uma boa prática subdividir métodos de orquestração para mantê-los compreensíveis e reutilizáveis. Na sequência, continuaremos a desenvolver nosso raciocínio procedimental, para cada um dos métodos faltantes. Neste caso, teremos um método em separado para o cálculo do pagamento de cada funcionário. #9. O método que calcula salário para cada funcionário, caso encontre algum dos erros apontados na especificação, irá disparar a exceção CalculoFolhaFuncionarioException, que neste tratamento será acumulada, conforme solicitado. #10. Em nosso caso, como recomendado para lógicas de atualização em lote, não devemos encerrar todas as gravações em uma única ULT (Unidade Lógica de Transação ou commit ). Correríamos o risco, neste caso, de comprometer recursos do SGBD excessivamente, já que transações muito longas (conhecidas como long running transactions ) aumentam perigosamente o tamanho e esforço do SGBD para manutenção de áreas de log/redo, dentre outros problemas. Além disso, um erro ao final do processo nos faria perder todos os cálculos corretos que fizemos até ali. Por estes motivos, é recomendável gerenciarmos transações manualmente em programações batch de alto volume ainda assim reutilizando alguns serviços do framework. No nosso caso, vamos efetuar um commit a cada 100 funcionários calculados corretamente, chamando o serviço commit() disponível na implementação de DAO do jcompany FS Framework. Ao final, também faremos uma chamada adicional, para garantir a confirmação dos restantes, abaixo de 100. #11. Se, ao final do processamento, houver algum erro de cálculo individual, nosso método irá encerrar disparando uma exceção principal que irá encapsular as várias exceções individuais e também o total calculado ok. Estas são informações que projetamos para que o método chamador (que chamou o atual) possa tratar o erro e apresentá-lo conforme requisitado. #12. Precisaremos também de uma função que verifique se o total de cálculos efetuados até aqui é igual ao universo de cálculo potencial. Isso porque, segundo a especificação, neste caso devemos atualizar a data do último fechamento. #13. Se a função não disparar uma exceção, ela deve devolver o total de funcionários cujo salário foi calculado para exibição da mensagem de confirmação, no padrão solicitado. - Implementando Objetos de Exceção (Exception) Programação de Camada Modelo II Como teremos que devolver muitas informações em caso de exceções (não somente uma mensagem), nós decidimos utilizar objetos de Exceção especiais para encapsulá-las. Ao decidirmos por disparar uma exceção em nosso método, delegamos a responsabilidade de finalizar o tratamento de erros para um método cliente, mas continuamos responsáveis por prover todas as informações necessárias para tanto, encapsuladas nos objetos de Exceção. As classes de Exceção devem ser criadas na camada ortogonal comuns em rhtutorial_commons, pois objetos deste tipo podem ser disparados entre camadas. Além disso devem especializar a classe java.lang.exception. 1. Siga as orientações das Figura E17.8 e Figura E17.9 para criar as classes de Exceção que utilizaremos.

Regras de Negócio e Batch Figura E17.8. Exceção que encapsula problemas em cálculos individuais de funcionários. #1. Crie a classe com nome CalculoFolhaFuncionarioException abaixo de com.empresa.rhtutorial.commons", em rhtutorial_comons. #2. Esta classe deve herdar de java.lang.exception, ancestral para exceções controladas. #3. Declare as propriedades "messagekey", nomefuncionario e cpffuncionario. #4. Note que não foram criados setters para as novas propriedades. Deste modo, elas somente podem ser incluídas através de um novo construtor que também recebe a mensagem do problema em si. Figura E17.9. Exceção que encapsula todos os problemas de um cálculo. #1. A segunda classe de Exceção deve ser criada com nome CalculoFolhaException no mesmo diretório da anterior e também herdando de java.lang.exception.

Capítulo E17 #2. Ela irá encapsular uma coleção de exceções individuais e também o total de calculados ok. #3. Da mesma forma (é um padrão para classes Exception), as novas variáveis somente são recebidas em construtores, imutáveis. - Implementando Data Access Objects (DAO) Programação de Persistência I Vamos agora escrever o nosso primeiro serviço de persistência específico, na Classe FuncionarioDAO, que provê serviços de acesso a funcionários. 1. Codifique o método "recuperacomdtinformado" em "FuncionarioDAO". 2. A primeira coisa que métodos de DAO tipicamente conterão é uma cláusula try-catch para garantir tratamento contra problemas externos inesperados. No caso específico destas classes, presentes em uma camada considerada de integração, existem diversos externos possíveis, como problemas de JDBC, Pool de Conexões, SGBD-R, Rede, etc. Utilize o tratamento padrão pauta mínima do jcompany simplesmente digitando try + Control + Space. Um snippet (trecho padrão de código) pré-configurado no Eclipse pelo jcompany gera todo o código necessário. Figura E17.10. Digitação do try-catch padrão do jcompany. 3. Utilize "control+shift+o" para importar dependências, especialmente a exceção padrão para problemas inesperados, "PlcException". Note também que uma classe de log (do framework Log4j) é utilizada no disparo da exceção, mas não é preciso declará-la, pois já está injetada pelo CDI no ancestral "PlcBaseJPADAO". Figura E17.11. Tratamento de try-catch padrão gerado via snippet e classe de log declarada. 4. Vamos agora obter uma sessão de persistência. Se estivéssemos utilizando EJB 3.1 (que está fora do escopo deste livro) poderíamos usar CDI para injetar o EntityManager como variável de instância do FuncionarioDAO. Mas como estamos com POJOs, podemos obter a sessão de persistência com o método "getentitymanager" como abaixo: @Inject PlcBaseContextVO context; EntityManager entitymanager = getentitymanager(context); Código E17.1. Obtenção de sessão de persistência para fábrica default.

Regras de Negócio e Batch O context é um objeto que segue o Design Pattern "Context Param". Trata-se de um POJO que encapsula informações de contexto na camada de Modelo, tais como perfil do usuário corrente, fábrica JPA, definição do grafo de entidades (metadados), dentre alguns outros Este objeto pode ser modificado para a obtenção de acesso a outras fábricas, mas em nosso caso basta recebê-lo por injeção e repassá-lo. 5. O método de persistência utilizando NamedQuery fica conforme abaixo: Figura E17.12. Método típico de DAO. #1. Podemos, ao final da cláusula createquery ou "createnamedquery", incluir quantos setparameter aninhados desejarmos para passar os valores dos argumentos. No nosso caso, somente temos um. #2. Um último comando getresultlist, ao final de todo o comando, retorna uma coleção do tipo List<Object[]>, com cada posição do array interno de objetos trazendo o valor de retorno de uma propriedade na ordem em que aparecem na cláusula select. 6. A query em si deve ser declarada na Entidade, tendo assim sua sintaxe verificada em tempo de inicialização da fábrica de persistência. Figura E17.13. NamedQuery em sintaxe JPA-QL #1. Um primeiro objetivo foi trazer, em uma única cláusula, todas as informações do grafo de consulta que precisamos incluindo o Object-Id (identificador interno do Funcionário), o nome e cpf, além do valor de Dias Trabalhados em ProventoDesconto, o total de dependentes e seu salário atual (imaginando, de forma simplificada, que podemos inferir o salário atual como o maior ). Note que funções de agregação, expressões, subqueries e outras técnicas consagradas pelo SQL estão presentes em JPAQL.

Capítulo E17 #2. Nossa classe pivô escolhida foi ProventoDesconto, pois dela consegue-se partir para todas as demais! Este é um ponto importante em JPAQL: Se partíssemos de Funcionario não chegaríamos em ProventoDesconto via navegação OO, já que não temos um OneToMany de Funcionario para ProventoDesconto *. Veja o grafo percorrido no diagrama da Figura E17.14, possibilitado pelas setas de navegação. O símbolo de composição sempre indica uma navegação possível, no caso de ProventoDesconto -> Funcionario. Mas não temos seta de Funcionario -> ProventoDesconto. Figura E17.14. Grafo percorrido no modelo de classes, por nossa cláusula JPAQL. #3. A navegação (junção com join ) de ProventoDesconto para Funcionario é obrigatória. De fato, pela associação, um ProventoDesconto deve ter no mínimo 1 (um) Funcionario. #4. Já a navegação de funcionário para seus dependentes é explicitamente codificada como left join. Em nosso caso, sabemos que existem funcionários que não possuem dependentes (veja cardinalidade mínima 0!). Portanto, esta é a estratégia correta - deste modo, funcionários que não tenham dependentes também serão recuperados. #5. A navegação de funcionário com seu histórico, por sua vez, é obrigatória novamente já que a multiplicidade mínima é também 1 (um). Importante: Perceba que todas as associações de join são definidas utilizando o alias pd, ou seja, partindo da classe pivô e navegando com notação de pontos pelo grafo de objetos. #6. Na cláusula where utilizamos dois tipos de filtros: - Para a enumeração naturezaproventodesconto, recuperada através da constante DT, podemos simplesmente utilizar valores alfanuméricos dentro de aspas simples, como no SQL (e como se seu tipo fosse um String); - Já para o anomesreferencia será enviado um argumento, nomeado de forma idêntica à propriedade (poderia ser qualquer nome desejado). #7. Como estamos utilizando funções de agregação count e max juntamente com valores não agregados, precisamos colocar as demais propriedades ausentes nas agregações, na cláusula group by, tal como faríamos em SQL. - Aprimorando cláusulas de JPAQL Programação de Persistência II Poderíamos nos dar por satisfeitos com a versão atual de nosso método de DAO: o Ele trata exceção apropriadamente, utilizando logging para arquivo, envio de e-mail e exibição de problemas inesperados para usuários. * Também seria possível, a despeito do mapeamento, realizar joins relacionais entre estas classes mas, deste modo, a solução fica mais extensa e menos elegante. Note que um modelo de classes que também traga as roles das associações (propriedades de navegação) explicitadas pode facilitar bastante na confecção das queries. Se informado somente join, o inner join é assumido como default. Se informado left ou right, um outer join é utilizado mudando apenas a direção do outer.

Regras de Negócio e Batch o o o Ele reutiliza o gerenciamento de sessões de persistência, transações e pool de conexões de camadas de arquitetura, evitando problemas sérios que podem advir do uso programático, em quaisquer destas áreas. Ele utiliza bem o recurso de JPAQL, procurando trazer em um único comando o maior conjunto de dados úteis possível. Ao evitar o envio de comandos a cada iteração de cálculo, ou minimizá-lo *, otimizamos significantemente a performance. Ele utiliza prepared stament, passando os argumentos via setparameter, ao invés de concatená-los à cláusula JPAQL principal. Isto não somente é mais seguro, como mais performático. Porém, no estágio em que nosso método se encontra, utilizando select f.id, f.cpf, f.nome, pd.valor, count(d.id), max(hp.salario), ele ainda é um péssimo exemplo. O seu uso forçaria cada método cliente (e esperamos que sejam vários, ao longo do tempo) a receber tipos pobres no estilo COBOL ou Cliente/Servidor Relacional, como exemplificado no Código E17.2. // 1 List<Object[]> listacomdtinformadonoperiodo = funcdao.recuperacomdtinformado(anomesreferencia); for (Iterator i = listacomdtinformadonoperiodo.iterator(); i.hasnext();) { // 2. Recebimento do tipo "result set" Object[] resultado = (Object[]) i.next(); Long idfuncionario = (Long) resultado[0]; String cpffuncionario = (String) resultado[1]; String nomefuncionario = (String) resultado[2]; BigDecimal proventodescontodiastrabalhados = (BigDecimal) resultado[3]; Long dependentetotal = Long resultado[4]; BigDecimal salarioatual = (BigDecimal) resultado[5]; Código E17.2. Recebimento de retorno do tipo result set com List<Object[]>. #1. Cláusulas JPAQL que usam select <propriedades> retornam coleções de vetores de objetos, List<Object[]>. #2. O laço que irá percorrer a coleção deve, portanto, fazer o casting apropriado de cada atributo para cada tipo correto (veja que neste pequeno exemplo de JPAQL já temos uma mistura de três tipos distintos). Este padrão não somente é pouco produtivo para quem reutiliza o serviço de dados, mas também sujeito a diversos erros e extremamente inflexível. Uma eventual introdução de novas propriedades na cláusula select pode introduzir erros graves no método cliente, não detectáveis em tempo de compilação e, o que é pior, muitas vezes nem em tempo de execução! Faça uma reflexão: Qual o nível de caos aconteceria se, em uma manutenção futura do método de DAO, introduzíssemos o retorno de um valor adicional com o total de descontos, antes do salário, como select f.id, f.cpf, f.nome, pd.valor, count(d.id), sum(hp.valor), max(hp.salario)? O cenário do Código E17.3 chega a ser ainda pior - e acontecerá na prática se o serviço de dados incentivar, especialmente se forem dezenas de atributos de retorno a serem manipulados. É o que chamamos de manipulação do tipo result set. É lamentável ver um Desenvolvedor Java EE (em tese, OO também) programando regras de negócio com dados estruturados como tabelas relacionais, como no exemplo. List<Object[]> listacomdtinformadonoperiodo = funcdao.recuperacomdtinformado(anomesreferencia); for (Iterator i = listacomdtinformadonoperiodo.iterator(); i.hasnext();) { Object[] resultado = (Object[]) i.next(); * Aliás, como dica geral, deve-se evitar o uso de recuperações de JPAQL dentro de laços, sempre que possível. Em casos de programações batch, como é o nosso caso, vamos ainda utilizar mais uma cláusula durante o cálculo, mas isto é razoável considerando-se que pode não haver memória disponível para se manter tudo em caching, no início dos cálculos.

Capítulo E17 BigDecimal salarioliquido = ((BigDecimal)resultado[5]).subtract( ((BigDecimal)resultado[4]).multiply(new BigDecimal(200))); Código E17.3. O pesadelo do COBOL, aplicado ao mundo OO. Faça uma segunda reflexão: Qual o esforço você tem que fazer para entender que a fórmula acima em destaque está subtraindo R$ 200,00 de seu salário base, para cada dependente? Introduzimos este anti-padrão JPAQL retornando Result Set em nosso tutorial apenas para enfatizarmos a importância da utilização de coleções de Grafos de Entidades de Domínio como retorno de JPAQLs, sempre que possível. Vamos refatorar nosso método, então, para retornar valores deste tipo com maior semântica para o negócio. 1. Altere a cláusula select introduzindo um new FuncionarioEntity(<propriedades>) que passa todos os valores retornados em seu construtor. "select new FuncionarioEntity(f.id,f.cpf,f.nome,pd.valor,count(d.id),max(hp.salario))" Código E17.4. Uso de JPAQL retornando objetos do domínio. 2. Neste padrão assumimos o trabalho que cada método cliente teria para fazer casting de Object[] para variáveis com nomes significativos (se fizesse). Assim, como projetistas de serviços para reuso, cuidaremos de acomodar os dados em um modelo de Domínio com maior semântica, livrando os nossos clientes Desenvolvedores deste trabalho e, principalmente, da tentação de escreverem código relacional em Java. Note, portanto, que não haverá aumento de trabalho, apenas fatoração de trabalho que haveria de ser feito de qualquer modo em cada método cliente. public class Funcionario extends AppBaseEntity { // 1. Auxiliares transientes @Transient private transient BigDecimal diastrabalhados; @Transient private transient Long totaldependentes; @Transient private transient BigDecimal salarioatual; // 2. Somente getters gerados. public BigDecimal getdiastrabalhados() {return diastrabalhados; public Long gettotaldependentes() {return totaldependentes; public BigDecimal getsalarioatual() {return salarioatual; public class FuncionarioEntity extends Funcionario { // 3. Construtor apropriado public FuncionarioEntity(Long id, String cpf, String nome, BigDecimal proventodescontodiastrabalhados,long dependentetotaldependentes, BigDecimal historicoprofissionalsalarioatual) { setid(id); setcpf(cpf); setnome(nome); this.diastrabalhados=proventodescontodiastrabalhados; this.totaldependentes=dependentetotaldependentes; this.salarioatual=historicoprofissionalsalarioatual; // 4. Reconstituição do grafo persistente - apenas exemplo. //sethistoricoprofissional(historicoprofissional); Código E17.5. Uso JPAQL retornando propriedades de Entidades de Domínio. #1. Propriedades transientes são criadas na classe abstrata Funcionario para acomodarem resultados temporários obtidos de seu grafo e serem utilizadas em cálculos do negócio. O padrão é nomear as propriedade transientes de forma a manter a classe de origem de sua informação bem clara utilizando [propriedadeagregacao][propriedade]. #2. Além da visibilidade private provemos somente métodos getters para estas propriedades para garantir o uso seguro, apenas via construtor, das propriedades transientes. #3. Na classe concreta, criamos o construtor que acomoda o result set em propriedades de objetos. Mantendo o construtor na classe concreta, visamos despoluir ao máximo da classe abstrata para que contenha métodos de negócio somente (além dos inevitáveis getters e setters, é claro). #4. Note que, se um select retornasse um objeto ou uma coleção de objetos OneToMany (Ex.: select new FuncionarioEntity(f.id, f.cpf, f.nome, f.historicoprofissional) ) o correto seria

Regras de Negócio e Batch recompor a agregação e não utilizar propriedades novas. O mesmo vale para objetos ManyToOne. 3. Agora assim, podemos indicar que nosso método retorna List<Funcionario>, alterando sua assinatura para public List<Funcionario> recuperacomdtinformado(date anomesreferencia). 4. Nosso método cliente agora pode trabalhar de uma forma bem mais elegante, compreensível e estável ao longo do tempo. Compare o Código E17.2 e o Código E17.3 com o Código E17.6. List<FuncionarioEntity> listacomdtinformadonoperiodo = fdao.recuperacomdtinformado(anomesreferencia); for (Iterator i = listacomdtinformadonoperiodo.iterator(); i.hasnext();) { FuncionarioEntity funcionario = (FuncionarioEntity) i.next(); BigDecimal salarioliquido = funcionario.getsalarioatual().subtract( funcionario.gettotaldependentes().multiply(new BigDecimal(200)); Código E17.6. Exemplo de chamada OO. - Externando cláusulas JPAQL em NamedQueries Programação de Persistência III As vantagens das NamedQueries como a que criamos são muitas.vamos rever seu mecanismo: 1. Cláusula do método de DAO para a parte query fica declarada em alguma das entidades envolvidas (poderíamos também optar por FuncionarioEntity). @NamedQueries({ @NamedQuery(name="ProventoDescontoEntity.recuperaComDTInformado", query="select new FuncionarioEntity(f.id,f.cpf,f.nome,pd.valor,count(d.id),max(hp.salario))" + " from ProventoDescontoEntity pd" + " join pd.funcionario f" + " left join pd.funcionario.dependente d" + " join pd.funcionario.historicoprofissional hp" + " where pd.naturezaproventodesconto='diastrab' and pd.anomesreferencia=:anomesreferencia" + " group by f.id,f.cpf,f.nome,pd.valor") ) public class ProventoDescontoEntity extends ProventoDesconto { Código E17.7. NamedQuery ProventoDescontoEntity.recuperaComDTInformado. 2. O método de DAO recupera a cláusula nomeada usando createnamedquery e informando o nome da NamedQuery em lugar da cláusula JPAQL em si. return em.createnamedquery("proventodescontoentity.recuperacomdtinformado").setparameter("anomesreferencia", anomesreferencia).getresultlist(); Código E17.8. Comando de classe DAO, agora utilizando JPAQL externo em NamedQuery. As vantagens de externar cláusulas JPAQL são muitas: o o Sintaxes de NamedQueries são analisadas em tempo de entrada da aplicação - e erros impedem a configuração da fábrica de persistência. Deste modo, não se corre o risco de entrar em produção com cláusulas cancelando na mão de usuários finais! Metadados (NamedQueries) * que dizem respeito a uma Agregação de Entidades ficam encapsulados na sua classe Raiz. Note que todos os dados recuperados em nossa cláusula * NamedQueries anotadas em Entidades, ao contrário do que uma análise superficial pode sugerir, não acoplam Entidades do negócio a mecanismos de persistência! Na verdade, são como as próprias anotações de mapeamento nas propriedades: informações de metadados (configuração). Nesta categoria, elas nem sequer precisam ser utilizadas pelas classes que as contêm - como, de fato, não o serão.

Capítulo E17 de exemplo participam da agregação de Funcionario - deste modo, mantendo um ponto único e intuitivo para análise de impacto e reuso. o o o A facilidade para se editar e conferir construtores associados a cláusulas JPAQL é maior por estarem ambos no mesmo artefato. As cláusulas JPAQL que percorrem diversos grafos de objetos são as únicas que não devem ficar externadas em nenhuma agregação. Deste modo, segmenta-se claramente esta diferente categoria de cláusulas de varredura (ou de relatórios ) das encapsuláveis no escopo de uma Agregação de Entidades. Ao passarmos a nomear cláusulas e desacoplá-las de métodos tornamos possível categorizálas em padrões de nível de arquitetura, reutilizando-as em programações genéricas, como faz o jcompany, gerando e utilizando NamedQueries com as seguintes convenções padronizadas: [Entidade].querySel[Complemento] : Utilizada como padrão para Colaborações plcselecao. É possível definir mais de uma opção com sufixos diferenciados após o querysel (Ex.: querysel2 ou queryseltodos ) e indicar, para uma Colaboração específica, qual delas utilizar. [Entidade].queryEdita : Utilizada como padrão para Colaborações de manutenção de uma Agregação por vez, no momento da edição. Se não encontrada, o jcompany usa from [Entidade] obj como default. [Entidade].querySelLookup : Utilizada como padrão para recuperação da Entidade quando vinculada ao grafo de manutenção de outra Agregação (ou seja, referenciada, mas não mantidade). Neste caso, a cláusula JPAQL evita trazer muitas propriedades, mas somente as utilizadas no método tostring definidas como lookup durante o Assistente de Criação de mapeamento O-R. Além disso, esta cláusula espera um argumento de Object-Id na parte where, por recuperar um objeto por vez - ao contrário da querysel, utilizada em lógicas de QBE (Query By Example), onde a where é montada dinamicamente e pode permitir retorno de vários objetos. [Entidade]. naodeveexistir[complemento] : Cláusulas JPAQL utilizadas para verificação de restrições de integridade referenciais e de existência para melhorar o tratamento de mensagens de erro do SGBD ou em situações onde não se possa confiar somente nas restrições declarativas em seu nível. - Entendendo o mecanimos OO para classes DAO Programação de Persistência IV A herança de PlcBaseJpaDAO já trouxe simplicidade para o nosso DAO, nos permitindo herdar uma classe de log e obter o EntityManager em POJOs facilmente. Podemos ser ainda mais sucintos com a sintaxe que codificamos, trazendo o "getentitymanager" para a mesma linha da query e também retirando a cláusula que redispara "PlcException" no tratamento de exceção gerado, pois sabemos que não haverá esta possibilidade em nossa chamada. Vamos verificar o quão simples então ficou o método ao final: @PlcAggregationDAOIoC(FuncionarioEntity.class) @SPlcDataAccessObject @PlcQueryService public class FuncionarioDAO extends PlcBaseJpaDAO { () public List<Funcionario> recuperacomdtinformado(date anomesreferencia) { try { return getentitymanager(context).createnamedquery("proventodescontoentity.recuperacomdtinformado") Como exemplo de que estes metadados não trazem sequelas para estas classes, basta removê-los e perceber que nenhum comportamento interno destas Entidades é afetado. Por outro lado, assim utilizados, aprimoram a organização.

Regras de Negócio e Batch.setParameter("anoMesReferencia", anomesreferencia).getresultlist(); catch (Exception e) { throw new PlcException("FuncionarioDAO", "recuperacomdtinformado",e, log, ""); Código E17.9. Classe de DAO completa com método típico, utilizando herança direta do jcompany para exemplo. - Utilizando CDI em classes de cálculos de negócio Agora que finalizamos a nossa implementação da classe de persistência, deixando-a tão simples quanto possível, vamos ajustar nossa classe de cálculo para reconhecê-la. Altere a classe CalculoFolha para receber o serviço de persistência via Injeção de Dependência do CDI, como abaixo. public class CalculoFolha { @Inject private FuncionarioDAO funcionariodao; protected Long calculafolha(date anomesreferencia) throws CalculoFolhaException { Código E17.10. Chamada de AS para DAO via CDI com amarração automática via padrão de nomenclatura. - Criando serviços adicionais em FuncionarioDAO Programação de Persistência V Analisando nossa especificação, vemos que ainda restam informações a serem recuperadas via serviços de persistência, como os totais gerais de descontos e proventos para um Funcionário, em um período. Decidimos, hipoteticamente, por não recuperar todos estes totais inicialmente, como fizemos para a maior parte dos dados de funcionário para que nosso exemplo fique mais próximo das restrições que encontramos no mundo real. Em geral, sempre que possível, será muito mais performático recuperar todos os dados envolvidos em uma transação batch em um único comando JPAQL inicial - e depois varrermos o resultado em memória. Mas muitas vezes isso não é possível por restrições de memória disponível. No mundo prático dos projetos corporativos, uma das grandes complexidades de programação é ter de lidar com imperfeições advindas de limitações tecnológicas. Restrições típicas são memória disponível, capacidade de comunicação via rede (throughput) e de processamento. Precisamos, portanto, de recuperar o saldo de Proventos e Descontos com naturezas PA e DG para cada funcionário/período. Este é um serviço que, basicamente, irá acessar a entidade ProventoDesconto mas, em nosso caso, ficará acomodado na mesma classe de implementação FuncionarioDAO Mas será que o correto não seria definir um ProventoDescontoDAO? Poderia ser, se não tivéssemos modelado ProventoDesconto como parte da Agregação de Funcionario (uma composição, na verdade), como pode ser conferido na Figura E17.14, pelo símbolo UML de losango preto entre ProventoDesconto e Funcionario *. Existem duas outras hipóteses de modelagem que nos induziria a utilizar um ProventoDescontoDAO : o Se houvéssemos optado por utilizar uma agregação compartilhada (losango claro) estaríamos definindo que não somente Funcionario, mas outras Entidades Raízes poderão se relacionar com ProventoDesconto. * Note que, apesar de termos realizado esta especificação para o modelo de Domínio, é uma boa prática utilizar estes mesmos critérios para o modelo de classes de Persistência quando possuir correlação.

Capítulo E17 o Se houvéssemos modelado ProventoDesconto como plcraiz também exporíamos esta Entidade de uma forma direta para fora da agregação de Funcionario. Agora que já justificamos o encapsulamento que iremos seguir e que já conhecemos a arquitetura básica de classes DAO sugerida pelo jcompany vamos acelerar nosso tutorial: 1. Defina o método recuperasaldogeralporfuncionario em nossa classe FuncionarioDAO. /** * @param anomesreferencia Período de referência (MM/yyyy) * @return BigDecimal, contendo o saldo de proventos e descontos gerais, para o funcionário, no * período. */ public BigDecimal recuperasaldogeralporfuncionario(date anomesreferencia,funcionario funcionario) { Código E17.11. Novos métodos para recuperar saldo de ProventoDesconto. 2. Implemente o método em FuncionarioDAO utilizando a mesma organização que já discutimos anteriormente, com a NamedQuery em ProventoDescontoEntity. public BigDecimal recuperasaldogeralporfuncionario(date anomesreferencia,funcionario funcionario) throws PlcException { return (BigDecimal) getentitymanager(context).createnamedquery( "ProventoDescontoEntity.recuperaSaldoGeralPorFuncionario").setParameter("anoMesReferencia", anomesreferencia).setparameter("funcionario", funcionario).getsingleresult(); Código E17.12. Novos métodos para recuperar saldo de ProventoDesconto. 3. Declare a cláusula JPAQL em ProventoDescontoEntity. Note que agora utilizamos dois argumentos, inclusive com um deles sendo uma Entidade e a agregação sum para somar débitos e créditos que estão representados pelos tipos PA e DG em nossos proventos e descontos. Esta soma simples funciona porque, no momento dos lançamentos, transformamos os valores de débito em valores negativos. @NamedQueries({ @NamedQuery(name="ProventoDescontoEntity.recuperaSaldoGeralPorFuncionario", query="select sum(pd.valor) as saldo" + " from ProventoDescontoEntity pd" + " where pd.anomesreferencia = :anomesreferencia and pd.funcionario=:funcionario" + " and (pd.naturezaproventodesconto='provento' or pd.naturezaproventodesconto='desconto')") ) public class ProventoDescontoEntity extends ProventoDesconto { Código E17.13. NamedQuery em ProventoDescontoEntity. - Delegando cálculos do negócio para Entidades Programação de Domínio II 1. Retornando ao nosso algoritmo principal em CalculoFolha vamos implementar agora o método de cálculo individual calculafolhaumfuncionario. Implemente-o como orientado pelo Código E17.14. // 3. Constantes externadas public static final int TAXA_IR = 15; public static final BigDecimal DESCONTO_DEPENDENTE = BigDecimal.valueOf(200); /** * Calcula salário e impostos, para um Funcionário * @param anomesreferencia Período de referência * @param funcionario Funcionário a ser utilizado */ private void calculafolhafuncionario(date anomesreferencia,funcionario funcionario) throws PlcException,CalculoFolhaFuncionarioException { // 1. Saldo de proventos e descontos gerais para funcionario BigDecimal saldoproventosdescontosgerais = funcionariodao.recuperasaldogeralporfuncionario(anomesreferencia,funcionario); // 2. Calcula salário Bruto BigDecimal salariobruto = funcionario.calculasalariobruto(saldoproventosdescontosgerais);

Regras de Negócio e Batch // 3. Calcula IR, passando taxa para manter flexibilidade BigDecimal ir = funcionario.calculair(salariobruto,taxa_ir,desconto_dependente); // 4. Calcula Salário Líquido BigDecimal salarioliquidofinal = funcionario.calculasalarioliquido(salariobruto,ir); // Grava Código E17.14. Método calculafolhaumfuncionario em CalculoFolhaAS. #1. Perceba que, para cada funcionário, estamos recuperando o saldo de Proventos e Descontos gerais - para depois delegar para a Entidade Funcionario a realização dos cálculos em si. #2. 3, e 4. Como dissemos, classes de Cálculo não devem realizar cálculos ou operações de negócio atômicos, mas se concentrar no controle de todo o algoritmo. Note que fizemos três chamadas distintas a Funcionario a partir de nosso CalculoFolha. Se não precisássemos dos valores de IR e Salário Líquido ao final para gravarmos entradas em ProventoDesconto, uma única delegação ao Funcionario seria suficiente. Obs.: Note também o uso de constantes em escopo do cálculo, em lugar de valores literais, como boa prática. 2. Vamos implementar agora os três métodos na classe abstrata de Domínio Funcionario. Implemente o método calculasalariobruto testando as pré-condições e pós-condições, conforme estabelecidas na especificação. public abstract class Funcionario extends AppBaseEntity { // 1 public static final int TOTAL_DIAS_MES_REFERENCIA = 22; // 2 public BigDecimal calculasalariobruto(bigdecimal saldoproventosdescontosgerais) throws CalculoFolhaFuncionarioException { // 3. Pré-condição para o cálculo. Exemplo I18n if (getdiastrabalhados().compareto(bigdecimal.valueof(total_dias_mes_referencia))<0) throw new CalculoFolhaFuncionarioException("rhtutorial.erro.calculo.dias.trabalhados",getNome(),getCpf()); // 4. Cálculo BigDecimal salariobruto = getsalarioatual().divide(bigdecimal.valueof(total_dias_mes_referencia)).multiply(getdiastrabalhados()); // 5. Pós-condição para o cálculo. Exemplo português - iniciando com token '#' if (salariobruto.compareto(bigdecimal.valueof(0))<0) throw new CalculoFolhaFuncionarioException( "#Cálculo de salário negativo para funcionário ["+getnome()+" - "+getcpf()+"]", getnome(),getcpf()); return salariobruto; Código E17.15. Método calculasalariobruto em Funcionario. #1. Constante com valor 22 para dias totais do mês criada para evitar proliferação de uso literal. #2. Nunca é pouco lembrar: os métodos de negócio, especialmente, devem possuir comentários Javadoc bem elaborados. #3. Pré-condição especificada, com exemplo de tratamento utilizando uma exceção customizada e enviando mensagem internacionalizada com argumentos em separado. #4. O cálculo em si reside nesta linha. Utilizamos recursos da classe java.math.bigdecimal para fazer uma regra de três entre os dias totais e os efetivamente trabalhados. #5. Pós-condição especificada, com exemplo de exceção disparada agora sem internacionalizar, para variar o exemplo somente. O uso do token #, no início da mensagem, indica para o jcompany não tentar traduzir este texto. 3. Implemente agora os métodos calculair e "calculasalarioliquido", que dispensam maiores explicações:

Capítulo E17 public BigDecimal calculair(bigdecimal salariobruto,int taxair, BigDecimal valordescontopordependente) throws PlcException { // Calcula descontos, multiplicando valor de desconto por número de dependentes BigDecimal descontos = valordescontopordependente.multiply(bigdecimal.valueof(gettotaldependentes())); // Calcula taxa, retirando percentual de IR informado (em escala taxair/100) return salariobruto.subtract(descontos).multiply(bigdecimal.valueof(taxair,2)); public BigDecimal calculasalarioliquido(bigdecimal salariobruto, BigDecimal ir) { return salariobruto.subtract(ir); Código E17.16. Métodos calculair e "calculasalarioliquido" em Funcionario. 4. E finalmente os métodos auxiliares que obtêem informações da agregação de Funcionario: /** * @return pressupõe que registros estão ordenados ascendente pela data de início do cargo */ private BigDecimal getsalarioatual() { return gethistoricoprofissional().get(gethistoricoprofissional().size()-1).getsalario(); /** * @return total de dias de referencia. Simplificado para exemplo. */ private BigDecimal getdiastrabalhados() { return BigDecimal.valueOf(TOTAL_DIAS_MES_REFERENCIA); /** * @return total de dependentes */ private long gettotaldependentes() { if (getdependente()==null) return 0; else return getdependente().size(); Código E17.17. Métodos auxiliares que pegam informações da agregação de Funcionario. - Realizando manutenções programaticamente Programação de Persistência VI Até agora implementamos classes de DAO apenas para recuperar dados. A partir deste tópico, aprenderemos como persistir objetos para gravação dos cálculos. 1. Defina um terceiro método em FuncionarioDAO conforme o Código E17.18. /** * Recebe valores discretos e inclui um novo ProventoDesconto, com "batch" como usuário da última * alteração. */ public void incluiproventodesconto(funcionario funcionario, Date anomesreferencia, NaturezaProventoDesconto naturezaproventodesconto, BigDecimal valor) { Código E17.18. Nova cláusula de contrato com a persistência, para incluir objetos ProventoDesconto. 2. Implemente agora o método explicado em Código E17.19. public void incluiproventodesconto(funcionario funcionario, Date anomesreferencia, NaturezaProventoDesconto naturezaproventodesconto, BigDecimal valor) { // 1 ProventoDescontoEntity proventodescontoentity = new ProventoDescontoEntity(); // 2 proventodescontoentity.setfuncionario(funcionario); proventodescontoentity.setanomesreferencia(anomesreferencia); proventodescontoentity.setnaturezaproventodesconto(naturezaproventodesconto); proventodescontoentity.setvalor(valor); // 3 proventodescontoentity.setusuarioultalteracao("batch"); proventodescontoentity.setdataultalteracao(new Date()); proventodescontoentity.setdescricao("executando cálculo folha");

Regras de Negócio e Batch // 4 insert(context,proventodescontoentity); Código E17.19. Método que realiza inclusões programaticamente. #1. O método inicia criando uma nova instância da classe a ser persistida. #2. Em seguida, os valores recebidos como parâmetros são incluídos na classe (um outro modo mais sucinto poderia ser criar um construtor especial na classe, para este fim). #3. Valores obrigatórios e não informados são preenchidos. O usuário da auditoria pauta mínima deve ser incluído manualmente, em programações batch. #4. É possível se chamar o comando de persistência do JPA EntityManager diretamente (em.persist), mas o insert do jcompany já o encapsula e deve ser chamado preferencialmente. Importante: o método insert chama o "em.persist" internamente, que apenas registra o objeto em memória para ser persistido - mas não emite nenhum SQL para o SGBD! Os SQLs em si ficam em caching no escopo da sessão de persistência, até que explicitamente se envie um comando para descarregar todos eles (Ex.: flush ) ou um commit, que também descarrega todo o cache antes de confirmar a transação. Em nosso caso, é uma boa idéia deixar que todos os INSERTs sejam gerados em conjunto, de 100 em 100 registros, no mesmo momento do commit, pois deste modo nossa Unidade Lógica de Transação dura menos tempo. Por isso, em nosso exemplo, bastará implementarmos o método pendente encerratransacao(). Vamos no entanto, para efeitos didáticos, implementar os dois serviços no DAO: o Um que simplesmente descarrega o buffer de persistência enviando para o SGBD todos os SQLs em caching desde o último envio (somente para exemplo); o E outro que realiza um fechamento de transação com commit que implicitamente também envia os comandos pendentes (o que utilizaremos efetivamente). 3. Defina mais dois métodos em FuncionarioDAO, conforme o Código E17.20. public class FuncionarioDAO extends PlcBaseJpaDAO { /** * Sinaliza para o despacho imediato de comandos de persistencia. Somente necessário para * controle manual de transações (Ex.: batch) */ public void enviacomandos() { /** * Sinaliza para finalização definitiva da transação, com confirmação de dados. * Somente necessário para controle manual de transações (Ex.: batch). * Obs.: Se a operação "enviacomandos" não foi chamada, dispara todos os comandos em *caching, neste momento. */ public void encerratransacao() { Código E17.20. Cláusulas típicas para gerenciamento manual de transações. 4. Implemente os métodos acima conforme o Código E17.21. public class FuncionarioDAO extends PlcBaseJpaDAO { public void encerratransacao(){ super.commit(); public void enviacomandos(){ super.sendflush(context); Código E17.21. Implementação com reúso em FuncionarioDAO.

Capítulo E17 5. Troque agora o nosso esboço inicial de fechamento de transação, em CalculoFolha, para usar o serviço DAO. for (Funcionario funcionario : listacomdtinformadonoperiodo) { try { calculafolhafuncionario(anomesreferencia, (FuncionarioEntity) funcionario); calculadook++; catch (CalculoFolhaFuncionarioException fe) { felista.add(fe); if (calculadook==100) funcionariodao.encerratransacao(); funcionariodao.encerratransacao(); if (felista.size()>0) Código E17.22. Uso de fechamento de transação manual. 6. E, finalmente, inclua no cálculo individual as linhas finais para inclusão de salário e IR, em CalculoFolha. private void calculafolhafuncionario(date anomesreferencia,funcionario funcionario) throws PlcException,CalculoFolhaFuncionarioException { // Calcula Salário Líquido BigDecimal salarioliquidofinal = funcionario.calculasalarioliquido(salariobruto,ir); // Grava Salário Liquido e IR calculados, como Proventos e Descontos funcionariodao.incluiproventodesconto(funcionario,anomesreferencia, NaturezaProventoDesconto.IR,ir); funcionariodao.incluiproventodesconto(funcionario,anomesreferencia, NaturezaProventoDesconto.SALARIO,salarioLiquidoFinal); Código E17.23. Chamada para geração de novos objetos ProventoDesconto. - Utilizando subqueries Programação de Persistência VII Para finalizar as partes pendentes do nosso método calculafolha de CalculoFolhas, precisamos somente finalizar o método que chamamos verificafechamento. Este método deverá verificar se todos os funcionários ativos já tiveram seu salário calculado para um período e, se for o caso, encerrar o período, gravando a data de fechamento em FolhaPagamento. 1. Crie o último método que precisaremos em FuncionarioDAO, conforme o Código E17.24. public class FuncionarioDAO extends PlcBaseJpaDAO { /** * Conta o total de funcionários sem salário calculado, no período */ public Long contafuncionariosemsalario(date anomesreferencia) { Código E17.24. Última cláusula de persistência de nosso exemplo atual. 2. Implemente em FuncionarioDAO. public Long contafuncionariosemsalario(date anomesreferencia) { return (Long) getentitymanager(context).createnamedquery( "FuncionarioEntity.contaFuncionarioSemSalario").setParameter("anoMesReferencia", anomesreferencia).getsingleresult(); Código E17.25. Implementação da última cláusula de persistência.

Regras de Negócio e Batch 3. Declare a NamedQuery em FuncionarioEntity. Perceba que agora estamos utilizando um recurso de subquery bastante poderoso, também disponível em JPAQL. Outro detalhe importante é o teste f.sithistoricoplc= A Lembra-se que funcionários são excluídos apenas logicamente? @NamedQuery(name="FuncionarioEntity.contaFuncionarioSemSalario", query="select count(*) from FuncionarioEntity f where f.sithistoricoplc='a' and f.id not in" + " (select pd.funcionario.id from ProventoDescontoEntity pd" + " where pd.anomesreferencia = :anomesreferencia and pd.naturezaproventodesconto='salario')") ) public class FuncionarioEntity extends Funcionario { Código E17.26. Cláusula JPAQL que utiliza função de agregação count e subquery. 4. Agora podemos implementar o método que falta em CalculoFolha, conforme o Código E17.27. private void verificafechamento(date anomesreferencia) throws PlcException { Long totalcalculado = funcionariodao.contafuncionariosemsalario(anomesreferencia); if (totalcalculado==0) funcionariodao.incluifechamento(anomesreferencia); funcionariodao.encerratransacao(); Código E17.27. Método que testa se é para gerar fechamento de folha. Colocaremso o método "incluifechamento" no mesmo FuncionarioDAO. Como sugerido no modelo, estamos compreendendo FolhaPagamento como parte da agregação de FuncionarioDAO. Figura E17.15. FolhaPagamento participante da agregação de Funcionario. O uso de mais uma transação, como sugerido, é também necessário já que não estamos mais no laço principal e realizamos mais uma gravação! A implementação deste método pode ser vista no Código E17.28. public void incluifechamento(date anomesreferencia) { FolhaPagamento folhapagamento = new FolhaPagamentoEntity(); folhapagamento.setanomesultimofechamento(anomesreferencia); folhapagamento.setdataultalteracao(new Date()); folhapagamento.setusuarioultalteracao("batch"); insert(context,folhapagamento); Código E17.28. Método de inclusão de registro de Folha de Pagamento em FuncionarioDAO. - Realizando escalonamentos temporais básicos Programação Batch I Vamos agora tratar da temporalidade de nossa especificação, definindo uma rotina que irá disparar nosso cálculo, para o mês passado, todo dia 3. É uma especificação muito simplista, mas que servirá para nos introduzir nas possibilidades embutidas na linguagem Java para este fim.