Solucionado (ver solução)
Solucionado
(ver solução)
8
respostas

Preencher objetos com consulta personalizada

Boa tarde galera.

Estou tentando fazer algo um pouco diferente do que vi no curso até o momento. Estou tentando preencher um objeto a partir de uma consulta jpql.

Porém estou tendo alguns problemas para popular o objeto. É retornada uma mensagem, alertando sobre o construtor do meu objeto, informando que ele não seria apropriado para popular o objeto.

Segue o código java desenvolvido.

package br.com.caelum.financas.teste;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import br.com.caelum.financas.modelo.Conta;
import br.com.caelum.financas.modelo.MovimentacaoMediaPorData;
import br.com.caelum.financas.modelo.TipoMovimentacao;
import br.com.caelum.financas.util.JPAUtil;

public class TesteFuncoesJPQL {

    public static void main(String[] args) {

        Conta conta = new Conta();
        conta.setId(3);

        String jpql = "Select new br.com.caelum.financas.modelo.MovimentacaoMediaPorData(AVG(m.valor), day(m.data), month(data), year(data)) from Movimentacao m where m.conta = :pConta "+
        "and m.tipo = :pTipo "+
        "group by day(data), month(data), year(data)";

        EntityManager em = new JPAUtil().getEntityManager();

        Query query = em.createQuery(jpql);
        query.setParameter("pConta", conta);
        query.setParameter("pTipo", TipoMovimentacao.SAIDA);

        List<MovimentacaoMediaPorData> movimentacoesMediaPorData = (List<MovimentacaoMediaPorData>) query.getResultList();

        for (MovimentacaoMediaPorData movimentacaoMediaPorData : movimentacoesMediaPorData) {

            System.out.println("A média do valor das movimentações para o dia "+movimentacaoMediaPorData.getDia()+"/"+movimentacaoMediaPorData.getMes()+"/"+movimentacaoMediaPorData.getAno()+" é:"+movimentacaoMediaPorData.getMedia());

        }

        em.getTransaction().begin();

        em.getTransaction().commit();

        em.close();

    }

}

Penso que o problema possa ser no retorno das funções day(), month() e year(). Como elas funcionam, retornam qual tipo de dado, eu imaginei que pudesse funcionar colocando os atributos do meu objeto com Long.

8 respostas

Esqueci de postar a classe MovimentacaoMediaPorData.

Segue...

package br.com.caelum.financas.modelo;

public class MovimentacaoMediaPorData {

    private Long dia;
    private Long mes;
    private Long ano;
    private Double media;

    @Deprecated
    MovimentacaoMediaPorData(){

    }

    MovimentacaoMediaPorData(Double media, Long dia, Long mes, Long ano){
        this.setDia(dia);
        this.setMes(mes);
        this.setAno(ano);
        this.setMedia(media);
    }

    public Long getDia() {
        return dia;
    }

    public void setDia(Long dia) {
        this.dia = dia;
    }

    public Long getMes() {
        return mes;
    }

    public void setMes(Long mes) {
        this.mes = mes;
    }

    public Long getAno() {
        return ano;
    }

    public void setAno(Long ano) {
        this.ano = ano;
    }

    public Double getMedia() {
        return media;
    }
    public void setMedia(Double media) {
        this.media = media;
    }
}

Boa Noite.

Problema solucionado.

O problema ocorria porque eu havia definido o tipo Long para os atributos da classe MovimentacaoMediaPorData, porém eles precisam ser Integer para serem compatíveis com o retorno da consulta.

Também faltava definir a visibilidade public para o costrutor. Como não havia especificado a visibilidade antes, o contrutor estava com visibilidade default, fazendo com que o mesmo não pudesse ser visível fora do seu pacote e conseqüentemente para a consulta da jpa.

Segue o código da classe MovimentacaoMediaPorData com as alterações realizadas.

package br.com.caelum.financas.modelo;

public class MovimentacaoMediaPorData {

    private Integer dia;
    private Integer mes;
    private Integer ano;
    private Double media;

    @Deprecated
    public MovimentacaoMediaPorData(){

    }

    public MovimentacaoMediaPorData(Double media, Integer dia, Integer mes, Integer ano){
        this.setDia(dia);
        this.setMes(mes);
        this.setAno(ano);
        this.setMedia(media);
    }

    public Integer getDia() {
        return dia;
    }

    public void setDia(Integer dia) {
        this.dia = dia;
    }

    public Integer getMes() {
        return mes;
    }

    public void setMes(Integer mes) {
        this.mes = mes;
    }

    public Integer getAno() {
        return ano;
    }

    public void setAno(Integer ano) {
        this.ano = ano;
    }

    public Double getMedia() {
        return media;
    }
    public void setMedia(Double media) {
        this.media = media;
    }

}

Só uma útima questão, a impressão da data está saindo desta forma: "26/1/2017" , qual seria a maneira mais elegante de fazer com que o mês tivesse o 0 antes, por exemplo, "26/01/2017".

Abraços

solução!

Boa noite, Anderson! Acho que a forma mais fácil e elegante de resolver essa questão da data é não desmembrando ela em dia, mês e ano na consulta! Vc pode trabalhar com tipos que representem realmente uma data como o Date ou o Calendar e, depois, na hora de imprimir, vc formata a data com o padrão que vc deseja.

Contudo, se por algum motivo for obrigatório desmembrar a data, então na hora da impressão, vc pode invocar um método getDataFormatada(), que deverá devolver, por exemplo, um objeto Calendar com os valores dos atributos dia, mes e ano e formatado com o padrão desejado por vc!

Pegou a ideia? Qualquer coisa é só falar!

Grande abraço e bons estudos, meu aluno!

Boa noite Gabriel.

Se eu não desmebrar a data, o group by não retornará o agrupamento esperado, pois o campo "data" é do tipo DateTime.

A consulta me retornará quatro registros ao invés de dois, pois a consulta interpretará como sendo quatro datas diferentes, quando na verdade a única coisa de diferente são os horários.

Estou utilizando como base os registros gerados na Aula 8 do curso de JPA.

insert into Movimentacao(data, valor, conta_id, descricao, tipoMovimentacao) values ('2017-01-26 18:01:07', 80.0, 2, "Restaurante", "SAIDA");
insert into Movimentacao(data, valor, conta_id, descricao, tipoMovimentacao) values ('2017-01-26 19:31:12', 100.0, 2, "Cinema", "SAIDA");
insert into Movimentacao(data, valor, conta_id, descricao, tipoMovimentacao) values ('2017-01-27 10:01:54', 40.0, 2, "Café da manhã", "SAIDA");
insert into Movimentacao(data, valor, conta_id, descricao, tipoMovimentacao) values ('2017-01-27 15:20:13', 20.0, 2, "Lanche", "SAIDA");

Ah sim! Mas a boa notícia é que ainda dá pra tentar utilizar a segunda estratégia que eu sugeri!

Contudo, se por algum motivo for obrigatório desmembrar a data, então na hora da impressão, vc pode invocar um método getDataFormatada(), que deverá devolver, por exemplo, um objeto Calendar com os valores dos atributos dia, mes e ano e formatado com o padrão desejado por vc!

Vê se funciona e resolve o seu caso! Qualquer coisa é só falar, meu aluno!

Fiz aqui com pequenos ajustes e está funcionando. Segue os códigos das classes.

TesteFuncoesJPQL

package br.com.caelum.financas.teste;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import br.com.caelum.financas.modelo.Conta;
import br.com.caelum.financas.modelo.MovimentacaoMediaPorData;
import br.com.caelum.financas.modelo.TipoMovimentacao;
import br.com.caelum.financas.util.JPAUtil;

public class TesteFuncoesJPQL {

    public static void main(String[] args) {

        Conta conta = new Conta();
        conta.setId(8);

        String jpql = "Select new br.com.caelum.financas.modelo.MovimentacaoMediaPorData(AVG(m.valor), day(m.data), month(data), year(data)) from Movimentacao m where m.conta = :pConta "+
        "and m.tipo = :pTipo "+
        "group by day(data), month(data), year(data)";

        EntityManager em = new JPAUtil().getEntityManager();

        TypedQuery<MovimentacaoMediaPorData> query = em.createQuery(jpql, MovimentacaoMediaPorData.class);
        query.setParameter("pConta", conta);
        query.setParameter("pTipo", TipoMovimentacao.SAIDA);

        List<MovimentacaoMediaPorData> movimentacoesMediaPorData = (List<MovimentacaoMediaPorData>) query.getResultList();


        for (MovimentacaoMediaPorData movimentacaoMediaPorData : movimentacoesMediaPorData) {

            System.out.println("A média do valor das movimentações para o dia "+movimentacaoMediaPorData.getDataFormatada()+" é:"+movimentacaoMediaPorData.getMedia());

        }

        em.getTransaction().begin();

        em.getTransaction().commit();

        em.close();

    }

}

MovimentacaoMediaPorData

package br.com.caelum.financas.modelo;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

public class MovimentacaoMediaPorData {

    private Integer dia;
    private Integer mes;
    private Integer ano;
    private Double media;

    @Deprecated
    public MovimentacaoMediaPorData(){

    }

    public MovimentacaoMediaPorData(Double media, Integer dia, Integer mes, Integer ano){
        this.setDia(dia);
        this.setMes(mes);
        this.setAno(ano);
        this.setMedia(media);
    }

    public Integer getDia() {
        return dia;
    }

    public void setDia(Integer dia) {
        this.dia = dia;
    }

    public Integer getMes() {
        return mes;
    }

    public void setMes(Integer mes) {
        this.mes = mes;
    }

    public Integer getAno() {
        return ano;
    }

    public void setAno(Integer ano) {
        this.ano = ano;
    }

    public Double getMedia() {
        return media;
    }
    public void setMedia(Double media) {
        this.media = media;
    }

    public String getDataFormatada() {

        DateFormat df = new SimpleDateFormat("dd-MM-yyyy");

        Calendar data = Calendar.getInstance();
        data.set(this.getAno(), this.getMes(), this.getDia());

        return df.format(data.getTime());
    }

}

Muito Obrigado.

É isso aí, Anderson! Agora é seguir em frente com os estudos!

Sempre que tiver qualquer dúvida é só mandar aqui no fórum da Alura!

Grande abraço, meu aluno!