Dúvida sobre acesso ao banco de dados

Amigos, boa tarde.

Apesar de conhecer a sintaxe Java de maneira razoável e saber alguns conceitos e princípios de orientação a objeto, possuo dúvidas sobre a arquitetura que devo utilizar no novo projeto do qual vou participar.

Até agora, trabalhei com o desenvolvimento estruturado de sistemas. Infelizmente, muitos dos desenvolvedores não possuem critérios ou preocupações com a arquitetura e o resultado disto é um software com diversos problemas estruturais.

Queremos evitar que isto ocorra no novo projeto, que será desenvolvido em Java. Para isto, queremos nos apoiar em padrões e princípios da OO. Porém, sei que nada é absoluto e, por mais que sejam boas orientações a se seguir, toda regra tem sua exceção.

De qualquer forma, estou com alguns problemas para entender como acessar as informações de meu banco de dados. Pelo que já li, não é o ideal permitir que a aplicação acesse indiscriminadamente o banco de dados onde bem entender, ficando o acesso a cargo de objetos/classes específicas para isto, como por exemplo, os famosos DAOs. Isto faz sentido, uma vez que, em teoria, cada classe deve possuir uma responsabilidade, ou seja, uma única razão para mudar. Isto é facilmente visualizado em casos de classes simples de domínio, como uma possível classe Cliente, por exemplo. Logo, a classe Cliente teria a respectiva ClienteDAO, que ficaria responsável por inserir, atualizar, etc.

No entanto, as coisas começam a ficar mais difíceis de se visualizar quando pensamos em informações um pouco mais complexas. Imaginem que eu possuo um escopo simples, com classes representando cliente, pedido, item do pedido e produto. Digamos que eu queira saber todos os clientes que compraram em determinado período, logo eu precisaria selecionar os clientes de pedidos cuja data se enquadrasse nesse período. Como eu chegaria a esse conjunto de clientes? Implementaria um método estático findByDate na classe cliente (O médoto findByDate da classe Cliente chamaria um findByDate da ClienteDAO), cujos argumentos seriam as datas inicial e final do período selecionado que me retornaria um ArrayList ou alguma outra coleção de clientes? Implementaria só na classe DAO? Ou faria de um modo totalmente diferente? Caso eu devo implementada na classe Cliente/ClienteDAO, eu terei que criar um método de procura de clientes para cada forma que eu quiser pesquisá-los (Clientes de uma determinada cidade, por exemplo)?

Imaginemos outra situação. Suponhamos que eu possua uma Frame que irá me mostrar o total faturado para o cliente x no período de 01/01/2013 a 31/01/2013. Logo, preciso somar o valor total dos itens do pedido dos pedidos cujo o cliente é x e cuja data de faturamento se encaixa no período selecionado. Há a necessidade de selecionar os pedidos e fazer a soma na mão ou posso não instanciar pedido algum e solicitar a soma direto na SQL (o que me parece mais lógico)? Caso eu simplesmente pegue a soma direto do banco de dados, onde realizo a query? No próprio frame / classe de domínio por trás do frame? Num classe DAO representando o domínio do frame?

Amigos, me desculpem o texto e a ignorância. Infelizmente minha experiência com design orientado a objeto é nula. Espero que possam ajudar um humilde desenvolvedor que precisa de muita prática para entender corretamente o que faz sentido em teoria.

Abraços.

Olá Renatols

[quote]
Imaginem que eu possuo um escopo simples, com classes representando cliente, pedido, item do pedido e produto. Digamos que eu queira saber todos os clientes que compraram em determinado período, logo eu precisaria selecionar os clientes de pedidos cuja data se enquadrasse nesse período. Como eu chegaria a esse conjunto de clientes? Implementaria um método estático findByDate na classe cliente (O médoto findByDate da classe Cliente chamaria um findByDate da ClienteDAO), cujos argumentos seriam as datas inicial e final do período selecionado que me retornaria um ArrayList ou alguma outra coleção de clientes? Implementaria só na classe DAO? Ou faria de um modo totalmente diferente?[/quote]

Veja só: quando vc define um tipo objeto Cliente, este deverá (por vias normais) somente ter entendimento sobre os dados pertencentes a ele (exemplo, código, nome, endereço, telefone, etc). Qualquer informação de pesquisa em banco não deverá (ou deveria, se for o caso) ser armazenado no bean Cliente, mas na classe ClienteDAO (a partir do exemplo que vc deu, usando o pattern DAO).

Assim, nesta classe vc poderia ter métodos diversos, sobrescritos ou não. Em outras palavras, vc pode ter os métodos:

Client find(int codigo) {...};
Client find(int cpf) {...};
List findAll() {...};
List findByDate()  {...};
ListfindByCity() {...};

Ou seja, todos os acessos ao BD deverão ser feitos utilizando o DAO, e setando os resultados nos objetos, sejam eles simples, ou listas! Dentro de cada método, vc poderia, por exemplo, alterar o script SQL que será executado.

Aí existem diversas opções. Do meu ponto de vista, se vc consegue ir fazer em SQL, eu aconselho. Uma vez que vc pode ter centenas de milhares de registros que possam atender ao pré-requisito que vc citou, instanciar na app e calcular de novo é um gasto desnecessário de tempo e processamento. Principalmente se o servidor do BD estiver numa máquina diferente de onde a app roda.

O local tem que ser nos DAO’s. Se conhecer a fundo a arquitetura, penso que vc poderia ter as classes Cliente e Faturamento. Esta última teria os atributos Cliente, Data, Total (ou Soma). Assim, seguindo a arquitetura anterior, vc teria um FaturamentoDAO com um método ‘findByClientePeriodo(Faturamento f)’, que faria a pesquisa.

Agora, exatamente como seria o script, não tenho como sugerir, sem conhecer a arquitetura do BD.

Blz?
Espero ter ajudado.

Berg, boa noite.

Obrigado pela resposta, com certeza ajudou. A questão do cliente já entendi, porém, sobre a totalização, ainda não entendi completamente.

O que me deixa um pouco confuso é que o retorno não será um objeto de domínio ou bean ou seja o que for, será simplismente um valor double primitivo. Sabendo que eu quero totalizar o valor que o cliente comprou em determinada data, não sei qual o DAO deverá ter o método, se é o ClienteDAO ou o FaturamentoDAO. Provavelmente seria o Faturamento, como você falou. Logo, no Faturamento DAO, seria eu simplismente colocar o método double getClientTotalByDate(int codigo, java.util.Date datInicial, java.util.Date datFinal) no FaturamentoDAO?

Acho que estou começando a entender melhor como deve ser a arquitetura. Se eu quiser pegar os contas a receber de um cliente, eu devo inserir o método ArrayList findByClient(int codigo) no ContasAReceberDAO?

Mais uma vez, obrigado pela atença Berg, com certeza está me ajudando muito.

Se mais alguém quiser colocar seu ponto de vista, por favor, sintam-se a vontade. Adoraria ver a opinião de quem quiser compartilhar.

Abraços.

Fala Renato.

Exatamente isso. Vc colocaria estas pesquisas nas classes DAO’s relacionadas. Às classes beans (Cliente, Faturamento e ContasAReceber, como vc citou), não lhes cabe pesquisas no BD.

É sempre bom utilizar a lógica utilizada pelo padrão MVC (em conjunto com o padrão DAO ou não): divida as responsabilidades! Ou seja, à classes ‘beans’ cabe-lhes o armazenamento dos objetos e seus atributos e métodos. À classes de conexão, a responsabilidade de pesquisa em BD e validação de dados (se for o caso). Assim, uma bean Cliente não precisa saber o que / por que / onde / aonde / de que forma é feita a pesquisa num BD. E a classes ‘conectivas’ não importa quais os métodos validadores e/ou demais funções dos beans.

Imagine seu sistema como uma gelatina. Imgine o que que acontece quando se dá um ‘chute na gelatina’. Uma célula afeta a outra, que responde em uma próxima, e assim por diante.

Se seus sistema está cheio de dependências entre as classes, onde cada uma conheça ou ‘mexe’ ou interfere no funcionamento das outras, se houver uma mudança drástica à posteriore, as consequências dessas dependências serão drásticas

Se você diminui a dependência entre as classes, futuros ‘chutes’ terão consequências ‘menos drásticas’. Espera-se! Afinal, TI não é uma ciência exata! kkkk

Abcs

Berg, muito obrigado pela ajuda e atenção.

Atualmente trabalho em um sistema de gestão estruturado, sem muita preocupação em divisão de responsabilidades, portanto o acesso ao banco é indiscriminado e feito sem cerimônias. Portanto, para mim, é um pouco difícil visualizar o acesso ao banco de dados e a transcrever dados relacionais em objetos.

Se me cabe, gostaria de fazer uma última pergunta (ao menos por enquanto hehe). Nas situações hipotéticas que propus, os informações são demasiadamente simples e pequenas. Porém, há relatórios e/ou consultas estatísticas que realizam uma quantidade razoável de buscas no banco de dados. Vamos supor que eu precise totalizar e calcular estatísticas de diversas áreas de domínio do meu sistema, portanto tenho que acessar valores de pedidos, ordens de compra, notas fiscais, contas a receber, contas a pagar, dentre outros. O mais correto seria eu criar um DAO específico para este processo ou eu deveria adicionar métodos de busca no DAO de cada domínio/bean que uma determinada SQL trata? O problema é que, em uma única SQL posso totalizar valores referentes a diversos beans (um exemplo simples seria o que eu ainda tenho que pagar: valor total das ordens de compra - contas pagas). Logo, posso eu criar um DAO específico para processos que necessitem? Esses DAOs não instanciariam objetos, somente me retornariam valores primitivos do banco de dados.

Espero não ter sido confuso em meu texto.

Abraço

Bom dia Renato.

Claro que vc pode usar qualquer uma das opções. Veja: essa situação de divisão de responsabilidades não é (e não deve ser) algo engessado. Como eu disse antes, td vai depender da arquitetura que vc está utilizando, tanto na app qto no bco, além da real necessidade.

Partindo do caso que vc citou: onde colocar as funções para estatísticas. Num exemplo anterior, vc perguntou sobre a soma do faturamento de um cliente num determinado período. Onde se colocar o DAO? Se esta soma é algo específico da classe Faturamento, e o bean Cliente não irá utilizá-la, não há porque essa pesquisa estar no ClienteDAO, pq não lhe convém (não é sua responsabilidade saber).

Da mesma forma, se vc consegue, por SQL, fazer as pesquisas estatísticas diretamente, usando funçoes próprias do SQL, como Average, Sum, Max, Count, entre outras, e essas informações sertão utilizadas somente pela classe Report, tais rotinas de pesquisa de BD devem estar num ReportDAO.

Blz, mas e se vc não for instanciar um bean para o Report, mas será algo que faz as pesquisas direto, retornando valores primitivos? Necessariamente, um DAO não precisa estar ligado à um Bean. Assim, vc pode ter um GenericDAO ou um ReportDAO, ou um SeveralSearchBDDAO, ou um NomeQueVcAcharMelhorDAO que lhe faça essas pesquisas, retorne os resultados esperado para que vc possa usá-los. O convencionamento é que, se vai usar Objetos específicos (exemplo Cliente), aí sim, vc liga um DAO a um Bean.

Uma última obs.: muito cuidado em implementar apps estruturadas em Java. A linguagem foi desenvolvida pensando na utilização da Orientação à Objetos, o que impacta diretamente na sua escalabilidade e desempenho. Ao contrário, por exemplo, de Object Pascal (mto utilizado em IDEs como Delphi e Lazarus), que vc pode utilizar tanto estruturado como orientado, em Java pode afetar no sucesso da app. Em outras palavras, se a app-Java não tiver usando OO, pode ser que ela fique lenta demais. Pode ser…

Desculpe se não fui claro em alguma coisa. Desculpe pelos erros de digitação, mas esse teclado tá duro d+.
kkkkk

Não se incomode em perguntar. O objetivo do fórum é esse!!
Abcs.

Uma outra dica que lhe dou é vc fazer uso de Stored Procedures e Views em pesquisas estatísticas no BD. Em alguns casos, é melhor jogar isso pro servidor de BD, deixando o peso dessas pesquisas para ele.

=)