QueryObject/Specification. Só atenção: não é para você resgatar tudo do SGBD sem ordem e orndenar em java (isso é eficaz mas não eficiente), faça seu DAO conseguir entender a Specification e transformá-la num SQL/HQL/coisa que o valha.
Geralmente eu tenho um método na implementação da Specification (que é um QueryObject neste caso) que retorna cláusulas WHERE e ORDER BY (ou equivalentes) que o DAO utiliza.
Boa pergunta. Acho que usaria specs também.
Pra que? Não entendi.
Que tipo de informações de paginação?
Se o agrupamento não faz sentido no seu Domain Model não deveria existir. Se faz sentido você deve ter um objeto no domain model que possa ser utilizado neste caso. Lembre-se que o foco do sistema são objetos, não tabelas e consultas.
Legal phillip, você poderia colocar alguns exemplos ? (sem querer abusar)
Eu quero dizer se é melhor criar uma método listarVendas genérico que receba diversos tipos de filtro ou um método listarVendasPagas que satisfaça apenas um caso de uso.
Eu quero dizer que junto com a lista de registros trazer o número de páginas, a pagina atual, etc.
Em algum outro tópico eu dei a idéia de criar classes para resultado para informar se o resultado está ordenado ou até mesmo para vc poder saber que objeto especificação gerou aquele resultado.
Hj após este tópico aqui não sei se isso seria uma boa idéia, pois acho que sua busca não deveria retornar um objeto especializado em resultado de busca, pois seus resultados de busca são objetos de dominio em uma lista.
Acho que no seu problema, vc poderia colocar um metodo para consultar o total de objetos dentro de um repositorio, criar uma especificação onde vc define o inicio de uma consulta, quantos objetos ela pode retornar apartir do inicio informado e em que ordem.
Sendo assim o que vc consegue fazer por exemplo:
Eu tenho 30 objetos no meu repositorio.(Uso o metodo de consulta de total de objetos)
Peço 10 objetos apartir do 11 objeto.(Uso minha especificação para fazer isso)
Com isso eu sei que eu tenho 3 páginas e que eu estou na segunda página.
Acho que seria melhor que criar classes especializadas em resultados de consulta!
QueryObject tem algo aqui, mais detalhes no livro. Já sobre Specification não achei muita coisa, provavelmente procurei da forma errada, se alguem tiver algum link seria de grande ajuda.
Usando QueryObject, eu teria de montar um adapter para Hibernate (criar os Criteria por exemplo) e se quizesse poderia criar um Adapter para gerar o SQL direto se for usar JDBC, certo?
Outra funcionalidade interessante que percebi é que poderia persistir tb o próprio QueryObject para permitir o usuário salvar as consultas parametrizadas, achei isso interessante pq eu estou modelando (tentando) uma situação onde tenho meta-objetos e parece q esse tipo de abordagem irá facilitar nesse caso.
Pena que o livro não se aprofunda muito, mas mostra o uso de QueryObject com Repository, que fez com que eu entendesse melhor como eles trabalham.
Phillip, existe alguma literatura com mais detalhes ou algum framework com mais detalhes sobre QueryObject ? Pelo que percebi as funções são bem padronizadas. Melhor ainda se já tiver algo para fazer QueryObject -> HibernateCriteria.
Não conheço nenhum framework nem implementação pública. O pessoal da lista do DDD está pensando em algo na linha, mas em C# (aliás, foi lançado a pouco um livro com implementações em C#).
Quanto à Criteria, um Criteria tem conceitualmente o mesmo objetivo, mas é genérica. Uma Specification é algo específico, por exemplo eu posso ter:
class PodeBeberSpecification{
public boolean isStatisfiedBy(Pessoa p){
return (p.getItade()>18);
}
}
Isso poderia ser feito com um Criteria facilmente mas deixa de ser uma "abstração atômica" de domínio. Normalmente você vai usar um QueryObject com Criteria para implementar as Specifications, e usar a interface enquanto dentro do Domain Model, exatamente como a dobradinha Repository/DAO.
Quanto à paginação, dê uma olhada na documentação do Hibernate sobre o tema.
Acho que seu Domain Model pode não saber sobre paginação com alguma facilidade, a interface gráfica também, basta abstrair tudo numa lsita ‘inteligente’ que trabalhe em conjunto com o Repository.
Imagina: sua consulta é feita ao Repository que retorna uma Lista. Na verdade esta Lista está populada apenas com a primeira página de objetos mas ela sabe perguntar ao Repository por mais páginas e se popular com elas.
É uma estrutura de dados mais chatinhas mas interessante… talvez o Hibernate já tenha algo parecido, não tem? Provavelmente vou precisar de algo assim num futuro próximo, bem próximo…
Philip valeu, cada dia mais eu abro minha mente.
Cara sem quere abusar muito, não teria como você montar algum artigo no seu blog sobre isto.
Tenho certeza que você irá ajudar muita gente.
Tente montar um exemplo e um tutorial com os assuntos abordados.
[quote=pcalcado]
É uma estrutura de dados mais chatinhas mas interessante… talvez o Hibernate já tenha algo parecido, não tem? Provavelmente vou precisar de algo assim num futuro próximo, bem próximo…[/quote]
no hibernate dá para fazer (exemplo retirado do Hibernate in Action)
Se quiser persistir objetos de forma simples em arquivo é só usar a classe abaixo e acrescentar o implements Serializable na classe do objeto a ser serealizado.
public class Arquivo{
/**Serializa o Vector recebido como parametro no arquivo que também é
recebido como parametro
@param ListaObjetos conteudo que será serializado
@param CaminhoArquivo caminho do arquivo
*/
protected static synchronized void gravaArquivo(Vector ListaObjetos, String CaminhoArquivo) throws Exception {
FileOutputStream objFileOS = null;
ObjectOutputStream objOS = null;
try{
objFileOS = new FileOutputStream(“dados\”+CaminhoArquivo);
objOS = new ObjectOutputStream(objFileOS);
objOS.writeObject(ListaObjetos);
objOS.flush();
}
catch (FileNotFoundException exc) {
throw new Exception(“Arquivo”+CaminhoArquivo+“não encontrado :”+exc.getMessage());
}
catch (IOException ex) {
throw new Exception(“Erro ao salvar o arquivo”+CaminhoArquivo+":"+ex.getMessage());
}
finally {
if (objOS != null) objOS.close();
if (objFileOS != null) objFileOS.close();
return;
}
}
/**Deserializa o arquivo recebido como parametro e retorna um Vector
com os dados do arquivo
@param CaminhoArquivo caminho do arquivo
@return Vector com os dados do arquivo
*/
protected static synchronized Vector recuperaArquivo(String CaminhoArquivo) throws Exception{
Vector listaObjetos = new Vector();
File objFile = new File(“dados\”+CaminhoArquivo);
if (objFile.exists()) {
FileInputStream objFileIS = new FileInputStream(“dados\”+CaminhoArquivo);
ObjectInputStream objIS = new ObjectInputStream(objFileIS);
try{
listaObjetos = (Vector)objIS.readObject();
objIS.close();
}
catch (IOException ex) {
throw new Exception(“Erro ao abrir o arquivo “+CaminhoArquivo+”:”+ex.getMessage());
}
finally{
objIS.close();
return listaObjetos;
}
}
return listaObjetos;
}
[quote=pcalcado]Não conheço nenhum framework nem implementação pública. O pessoal da lista do DDD está pensando em algo na linha, mas em C# (aliás, foi lançado a pouco um livro com implementações em C#).
[/quote]
Juntando os exemplos do livro eu cheguei em algo assim, o caminho estaria certo? Se a ideia é começar a modelar e trabalhar com Domain Model acredito que algo assim seria interessante.
// Armazena todos os repository
// Em algum ponto defino qual a strategy que o meu repository
// vai utilizar para persistir os dados
class RepositoryRegistry {
private PersonRepository personRepository = new PersonRepository();
private RepositoryStrategy rs;
public static PersonRepository personRepository() { }
}
interface RepositoryStrategy {
public List matching(Criteria criteria);
}
abstract class Repository {
private RepositoryStrategy strategy;
protected List matching(Criteria criteria) {
return strategy.matching(criteria);
}
}
class QueryObject {
public QueryObject(Class klass) { }
public void addCriteria(Criteria criteria) { }
}
class Criteria {
private Criteria(int operation, String name, int value) { }
public static Criteria greaterThan(String name, int value) {
return new Criteria(Criteria.GREATERTHAN, name, value);
}
}
class PersonRepository extends Repository {
public List dependentsOf(Person person) {
return matching(Criteria.greaterThan("numberOfDependents", 0));
}
}
// Esse seria um matching genérico..
class InMemoryStrategy implements RepositoryStrategy {
private Set domainObjects;
public List matching(Criteria criteria) {
List results = new ArrayList();
Iterator it = domainObjects.iterator();
while (it.hasNext()) {
// separa os objectos que satisfazer a query
Person each = (Person) it.next();
if (criteria.isSatisfiedBy(each))
results.add(each);
}
return results;
}
}
Exemplo de como poderia ser o HibernateStrategy
class HibernateStrategy implements RepositoryStrategy {
private Set domainObjects;
public List matching(Criteria criteria) {
// crio query
Criteria crit = session.createCriteria(criteria.getKlass());
// converto criteria para hibernate criteria
crit = HibernateCriteriaAdapter(criteria);
return crit.list();
}
return results;
}
}
No caso de eu ter um HibernateStrategy eu teria de adaptar os criteria para as classes correspondentes do hibernate, isso não seria um trabalho muito grande, considerando que a api de criteria do hibernate é bem abrangente. Vale a pena?
Use dependency Injection/IoC e acabe com esses registries.
Sobre Criteria, você pode usá-la dentro do Spec/QueryObject, mas fazer que um QueryObject seja um Criteria o faz perder sentido, creio. Ou não. Preciso pensar mais sobre isso.
[quote=pcalcado]Use dependency Injection/IoC e acabe com esses registries.
[/quote]
Opa boa idéia, deixei assim pq primeiro quero entender como funciona, depois ver como melhorar, e na verdade estou quebrando mais a cabeça na parte do Specification/QueryObject/Criteria e como adaptar o criteria do Repository para o Criteria do hibernate.
[quote=pcalcado]
Sobre Criteria, você pode usá-la dentro do Spec/QueryObject, mas fazer que um QueryObject seja um Criteria o faz perder sentido, creio. Ou não. Preciso pensar mais sobre isso.[/quote]
Valeu pela ajuda, tanto o artigo como os posts tem ajudado bastante.