HQL - Calculando intervalos entre datas

Olá pessoal,

Estou tendo dificuldades para criar uma consulta com HQL, onde é necessário calcular a soma de uma data com um intervalo em meses. A questão é que a quantidade de meses é variável. Sei que no Hibernate cálculos de intervalos de datas não são padronizados (pelo menos foi o que eu li no JIRA do Hibernate), é algo dependente do banco. Pois bem, estou usando postgresql.

Tenho uma classe assim:

@Entity
public class Instrumento {
   //...
   @Temporal(TemporalType.DATE)
   private Date ultimaCalibracao;
   @ManyToOne
   private TipoInstrumento tipo;
   //...
}

E a classe TipoInstrumento é mais ou menos assim

public class TipoInstrumento {
   //...
   private int periodicidade;
   //...
}

Preciso verificar se a ultimaCalibracao, somada ao intervalo em meses da periodicidade é menor que a data atual.
Tentei algo como

from Instrumento i where i.ultimaCalibracao + interval i.tipo.periodicidade 'months' > current_date

Óbviamente não funcionou.

Alguma idéia?

Obrigado!

Não entendi bem esse hql…
Exemplifique melhor para eu tentar te ajudar…

[quote=nbluis]Não entendi bem esse hql…
Exemplifique melhor para eu tentar te ajudar…[/quote]

Esqueça esse HQL que eu coloquei, qualquer query que resolva meu problema serve :slight_smile:

Tenho um atributo ‘Date ultimaCalibracao’ na classe Instrumento. Essa classe Instrumento possui um atributo da classe TipoInstrumento, a qual por sua vez possui um inteiro ‘periodicidade’.
Preciso retornar todas as instâncias da classe Instrumento cuja soma da ultimaCalibracao com a periodicidade de seu TipoInstrumento seja menor que a data atual

Em pseudo-código seria algo como

Retorne instrumentos onde instrumento.ultimaCalibracao + (instrumento.tipo.periodicidade 'meses') > data atual

Em SQL (para o PostgreSQL) isso aqui funciona:

select i.* from instrumento i inner join tipoinstrumento ti on i.tipo_id = ti.id where i.ultimacalibracao + interval '1 month' * ti.periodicidade < now();

Agora, passar isso pra HQL que tá difícil de acertar… Como cálculo de intervalo entre datas é dependente do banco, tentei algo como

from Instrumento i where i.ultimaCalibracao + (interval '1 month' * i.tipo.periodicidade)  < current_date

Mas recebi o seguinte erro

unexpected token: '1 month' near line 1, column 84 
org.hibernate.hql.ast.QuerySyntaxException

jah tentou o usar o datediff ex: datediff(‘data1’,‘data2’)/365 ira retornar o intervalo entre as datas.

Mas isso não é hql… é dependente de banco…

Criteria é a chave!

int meses = 1; Calendar auxiliar = Calendar.getInstance(); // cria com a data corrente auxiliar.add(Calendar.MONTH, meses * (-1)); Date dataMenosMeses = auxiliar.getTime(); Criterion restricao = Restrictions.le("ultimacalibracao", dataMenosMeses); DetachedCriteria criterio = DetachedCriteria.forClass(Instrumento.class).add(restricao);

[quote=ricardosoares]Criteria é a chave!

int meses = 1; Calendar auxiliar = Calendar.getInstance(); // cria com a data corrente auxiliar.add(Calendar.MONTH, meses * (-1)); Date dataMenosMeses = auxiliar.getTime(); Criterion restricao = Restrictions.le("ultimacalibracao", dataMenosMeses); DetachedCriteria criterio = DetachedCriteria.forClass(Instrumento.class).add(restricao);[/quote]

Olá,

Não entendi essa parte

auxiliar.add(Calendar.MONTH, meses * (-1));

Porque esse (-1) ?

Outro problema é que eu acho que isso não iria funcionar, pois eu não tenho como saber de antemão esse intervalo de meses que está no atributo periodicidade. Cada instância da classe Instrumento persistida possui uma periodicidade diferente!

Obrigado!

Ok, entendi o porque do (-1)…
Mas ainda assim não vai funcionar, como eu disse, eu não sei de antemão a quantidade de meses.

entao cara vc vai ter q fazer algo do tipo criando sessao para acessar funçoes especificas do banco de dados entao ficaria algo assi:

ISession _session = NHibernateSessionManager.Current;
IQuery query = m_session.CreateQuery(“SELECT a FROM tb1 as a WHERE datediff(Day, :date, a.BDate) > 0”);
query.SetDateTime(“date”, _someDate);

ow no lugar do datediff vc coloca a funçao q vc fez para realizar o calculo q vc conseguiu direto no postgresql.

[quote=ebarros]entao cara vc vai ter q fazer algo do tipo criando sessao para acessar funçoes especificas do banco de dados entao ficaria algo assi:

ISession _session = NHibernateSessionManager.Current;
IQuery query = m_session.CreateQuery(“SELECT a FROM tb1 as a WHERE datediff(Day, :date, a.BDate) > 0”);
query.SetDateTime(“date”, _someDate);

ow no lugar do datediff vc coloca a funçao q vc fez para realizar o calculo q vc conseguiu direto no postgresql.[/quote]
Dai não vai ser hql… e vai ser dependente de banco…
Com essas condições fica fácil…

[quote=nbluis][quote=ebarros]entao cara vc vai ter q fazer algo do tipo criando sessao para acessar funçoes especificas do banco de dados entao ficaria algo assi:

ISession _session = NHibernateSessionManager.Current;
IQuery query = m_session.CreateQuery(“SELECT a FROM tb1 as a WHERE datediff(Day, :date, a.BDate) > 0”);
query.SetDateTime(“date”, _someDate);

ow no lugar do datediff vc coloca a funçao q vc fez para realizar o calculo q vc conseguiu direto no postgresql.[/quote]
Dai não vai ser hql… e vai ser dependente de banco…
Com essas condições fica fácil…[/quote]

É exatamente o que estou tentando evitar, criar query nativa…

[quote=cassio]
Não entendi essa parte

auxiliar.add(Calendar.MONTH, meses * (-1));

Porque esse (-1) ?

Outro problema é que eu acho que isso não iria funcionar, pois eu não tenho como saber de antemão esse intervalo de meses que está no atributo periodicidade. Cada instância da classe Instrumento persistida possui uma periodicidade diferente!

Obrigado![/quote]

Esse (-1) é que, ao invés de somar um numero de meses da “ultimacalibracao”, subtrai-se esses meses da data corrente.

Só agora observei: a periodicidade varia com o instrumento, né? Neste caso… ainda não sei como implementar no Criteria.
Mas vale a pena dar uma pesquisada. O Criteria é bastante interessante.

Pessoal, resolvi usando Criteria mas tive que enfiar uma restrição nativa ali, não teve jeito…

Criteria criteria = getSession().createCriteria(Instrumento.class);
Criterion vencido = Restrictions.sqlRestriction("ultimaCalibracao + interval '1 month' * (select tipoinstrumento.periodicidade from tipoinstrumento where tipoinstrumento.id = tipo_id) < current_date");
criteria.add(vencido);   		
return criteria.list();		

Não é bem o que eu queria mas resolveu meu problema. Se eu descobrir uma maneira de fazer sem utilizar essa restrição nativa eu posto aqui.

Muito obrigado a todos!

acho que isso pode funcionar:

"from Instrumento i where (current_date - i.ultimaCalibracao) > (i.tipo.periodicidade * 2592000000)"

considerando que “ultimaCalibracao” seja do tipo TIMESTAMP, 2592000000 (2.592E9) representa um mês em milisegundos, resultado de 1000 * 60 * 60 * 24 * 30

por via das dúvidas, force ser do tipo date:

"from Instrumento i where (current_date - cast(i.ultimaCalibracao, date)) > (i.tipo.periodicidade * 30)"
daí, o resultado para comparação vem em número de dias

[quote=ricardosoares]acho que isso pode funcionar:

"from Instrumento i where (current_date - i.ultimaCalibracao) > (i.tipo.periodicidade * 2592000000)"

2592000000 (2.592E9) representa um mês em milisegundos, resultado de 1000 * 60 * 60 * 24 * 30.[/quote]

Mas e os meses com 31 dias e fevereiro, que pode ter 28 ou 29 dias? Esse critério de vencimento da calibração tem q estar certinho, se errar por um dia, já era…

Obrigado pela força Ricardo!