Preciso testar melhor, mas parece que consegui resolver o meu problema.
Vou tentar resumir meu problema.
O sistema que estou implementando precisa fazer “Lancamentos de Crédito” (Class LancamentoCredito (na verdade é LancamentoCreditoTarifado, mas vamos ignorar esse sufixo)) em uma determinada conta (Class BancoConta).
Cada LancamentoCredito está associado (@ManyToOne) a uma “Vigência Tarifária” (Class VigenciaTarifaria), ou seja, um LancamentoCredito possui 1 única VigenciaTarifaria e 1 VigenciaTarifaria pode ter vários LancamentoCredito’s.
LancamentoCredito também possui 1 campo BigDecimal chamado “valor”, 1 campo BancoConta chamado “conta” e 1 campo CreditoTipo chamado “creditoTipo” (que representa o tipo de lançamento, DAC, TED, DOC, Home Banking, etc…).
Essa VigenciaTarifaria possui um campo int chamado “floatDias”. Esse floatDias representa o D+floatDias que o crédito lançado realmente entrará na conta.
Em um certo relatório, eu preciso saber o “Total de Créditos” que será transferido para uma determinada BancoConta, de um determinado CreditoTipo, entre o intervalo de datas dataInicial e dataFinal.
Se o floatDias fosse sempre o mesmo, isso seria facilmente resolvido. Por exemplo, se o floatDias fosse SEMPRE igual a “2” e eu quisesse saber o total de créditos que entrou em uma conta, entre o dia 16/04/2009 e 30/04/2009:
Calcularia a dataIni e a dataFim “por fora” (com a classe GregorianCalendar), resultando dataIni = 14/04/2009 e dataFim = 28/04/2009 e executaria a consulta hql:
"select sum(lanc.valor) from LancamentoCredito lanc where (lanc.dataLancamento between :dataIni and :dataFim) and (lanc.conta = :bancoConta) and (lanc.creditoTipo = :creditoTipo)"
Acontece que o floatDias pode variar durante o período selecionado. Por exemplo, do dia 16/04/2009 ao dia 20/04/2009 floatDias pode ser 2, e a partir do dia 21/04 até o dia 30/04 o floatDias pode passar a ser 3…
Então eu precisaria de uma função que pudesse somar uma quantidade de dias dentro da query para poder me dar o resultado desejado. Felizmente dá pra fazer isso com o PostgreSQL usando a seguinte sintaxe:
Somar dias e horas a uma data (Fonte: http://pt.wikibooks.org/wiki/PostgreSQL_Prático/Funções_Internas/Data_e_Hora):
Só que para usar isso eu não poderia usar HQL, teria que usar sql nativo. Para continuar usando HQL, tive que criar minhas funções HQL “add_day(data, dias)” e “sub_day(data, dias)” e extender o dialeto org.hibernate.dialect.PostgreSQLDialect:
public class CustomPostgreSQLDialect extends PostgreSQLDialect {
public CustomPostgreSQLDialect() {
super();
registerFunction("add_day", new SQLFunctionTemplate(Hibernate.DATE, "( cast((?1) as Date) + cast((?2) || ' days' as interval) )"));
registerFunction("sub_day", new SQLFunctionTemplate(Hibernate.DATE, "( cast((?1) as Date) - cast((?2) || ' days' as interval) )"));
}
}
Agora, finalmente, eu posso fazer a consulta HQL que eu gostaria (não testado a fundo ainda) :
Query query = em.createQuery("select sum(lanc1.valor) from LancamentoCreditoTarifado lanc1, LancamentoCreditoTarifado lanc2 where (lanc1.dataLancamento = add_day(lanc2.dataLancamento, lanc2.vigenciaTarifaria.floatDias)) and (lanc2.conta = :conta) and (lanc1.conta = :conta) and (lanc1.creditoTipoTarifado = :creditoTipoTarifado) and (lanc2.creditoTipoTarifado = :creditoTipoTarifado) and (add_day(lanc2.dataLancamento, lanc2.vigenciaTarifaria.floatDias) between :dataIni and :dataFim)");
Espero que isso possa ajudar alguém no futuro!