Consulta entre tabelas com Hibernate

Possuo um projeto que possui uma classe Cliente, Produto e Compra onde em uma tabela mysql possui um relacionamento que n:n entre produto e compra (que se cria a tabela compraproduto).

na minha CompraDAO estou tentando fazer uma consulta para listar todos os produtos que estão inseridos em uma determinada compra

public List<Produto> carregarCarrinho(Compra compra){
    List<Produto> listaProduto;
    Session session = HibernateUtil.getSessionFactory().openSession();
    String sql = "from Produto p, compraproduto cp where idCompra = 1 and p.idProduto = cp.idProduto";
    //String sql = "from Produto p join p.compraproduto cp where cp.idCompra = :id and p.idProduto = cp.idProduto";
    listaProduto = session.createQuery(sql).setParameter("id", compra.getIdCompra()).list();
    session.close();
    
    return listaProduto;
}

Porém ele dá a mensagem que compraproduto não está mapeado

Advertência:   #{cadastroCompraBean.selecionaCompra()}: org.hibernate.hql.internal.ast.QuerySyntaxException: compraproduto is not mapped [from modelo.Produto p, compraproduto cp where idCompra = 1 and p.idProduto = cp.idProduto]

como compraproduto é o nome da tabela do relacionamento entre duas tabelas, não foi criado uma classe para ela. Como faço para mapear ou teria alguma outra maneira de fazer esse tipo de consulta.

Isso é um select?
Se for deve começar com “Select * from Produto …”

Sim, é um select porém se utilizo “Select * from” ele gera o erro

Advertência:   #{cadastroCompraBean.selecionaCompra()}: org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: * near line 1, column 8 [select * from modelo.Produto p, compraproduto cp where idCompra = 1 and p.idProduto = cp.idProduto]

Acho que não precisa do *

Não precisa do * só se você colocar quais os campos deseja selecionar.

Mostra a sua classe Compra e Produto, como estão esses mapeamentos?

É um HQL, ou seja, hibernate query language, onde omite-se o SELECT.

Por favor, verifique o mapeamento das classes Compra e Produto, verifique se o nome que deu à @JoinTable é compraproduto.

Voce está usando hibernate, a forma de chamar com hibernate é bem diferente do sql normal, por isso é chamado de HQL, no hibernate você pode fazer essas query usando Criteria ou então usando namedQuery, recomendo você a procurar na internet exemplos de DAO no hibernate que já vem até com o código de insert, update, remove, etc. Geralmente em uma classe do tipo controller. Abaixo vai exemplo do meu código que uso, lembrando que a classe VoidDAO é abstrata para não ter que ficar copiando código repetido toda hora, e a ClienteDAO implementa os códigos necessários para chamar insert, remove, update, pesquisa por id, e todos os metodos com anotação @Override são específicos da classe cliente.

public abstract class VoidDAO<T> {
    private EntityManager entityManager;

    public VoidDAO(EntityManager em) {
        this.entityManager = em;
        entityManager.getTransaction().begin();
    }

    public void salvar(T objeto) {
        this.entityManager.persist(objeto);
        commit();
    }

    public void alterar(T objeto) {
        this.entityManager.merge(objeto);
        commit();
    }

    public void remover(T objeto) {
        this.entityManager.remove(this.entityManager.merge(objeto));
        commit();
    }
    public void fechar(){
        entityManager.close();
    }
    public void commit() {
        if (this.entityManager == null) {
            return;
        }
        if (this.entityManager.getTransaction().isActive()) {
            this.entityManager.getTransaction().commit();
        }        
    }
    public void rollBack(){
        if (this.entityManager == null) {
            return;
        }
        if (this.entityManager.getTransaction().isActive()) {
            this.entityManager.getTransaction().rollback();
        }
    }

       
    

    
    /**
     *
     * @param namedQuery
     * @return
     */
    public List<T> getListaCompleta(String namedQuery) {
        Query query = this.entityManager.createNamedQuery(namedQuery);
         List<T> object=(List<T>) query.getResultList();
         rollBack();
         return object;
    }

    public T findById(Class<T> classe, Integer id) {
         T objeto=(T) this.entityManager.find(classe, id);
         rollBack();
         return objeto;
    } 

public T buscaUltimo() {
return null;
}

public T buscaPrimeiro() {
    return null;
}

public T findNome(String nome) {
    return null;
}

public T findCodigo(int codigo) {
    return null;
}
public T findCPF(String CPF){
    return null;
}

public T findUsuario(String usuario) {
    return null;
}
public T findAnterior(int codigo){
    return null;
}
public T findProximo(int codigo){
    return null;
}
}

public class ClienteDAO extends VoidDAO<CadastroCliente> {
    public ClienteDAO(){
        super(ThreadPrincipal.em);
    }
    @Override
    public CadastroCliente buscaUltimo(){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findUltimoRegistro");        
        try{
        CadastroCliente cadastroCliente;
            cadastroCliente = (CadastroCliente) query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
            rollBack();
            return null; 
        }
    }
    @Override
    public CadastroCliente buscaPrimeiro(){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findPrimeiroRegistro");        
        try{
            CadastroCliente cadastroCliente;
            cadastroCliente = (CadastroCliente) query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
            rollBack();
            return null; 
        }
    }    
    @Override
    public CadastroCliente findCodigo(int codigo){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findCodigo");        
        query.setParameter("codigo", codigo);
        try{
            CadastroCliente cadastroCliente;
            cadastroCliente = (CadastroCliente) query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
           rollBack();
           return null; 
        }
    }
    
    @Override
    public CadastroCliente findAnterior(int codigo){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findAnterior");        
        query.setParameter("codigo", codigo);
        query.setMaxResults(1);
        try{
            CadastroCliente cadastroCliente = (CadastroCliente)query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
           rollBack();
           return null; 
        }
    }  
    
    @Override
    public CadastroCliente findProximo(int codigo){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findProximo");        
        query.setParameter("codigo", codigo);
        query.setMaxResults(1);
        try{
            CadastroCliente cadastroCliente = (CadastroCliente)query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
            rollBack();
            return null;
        }
    }    
    
    @Override
    public CadastroCliente findNome(String nome){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findNome");        
        query.setParameter("nome",nome+"%");
        query.setMaxResults(1);
        try{
            CadastroCliente cadastroCliente;
            cadastroCliente = (CadastroCliente) query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
            rollBack();
            return null; 
        }
    }
    
    @Override
    public CadastroCliente findCPF(String CPF){
        Query query = ThreadPrincipal.em.createNamedQuery("CadastroCliente.findUltimoCPF");        
        query.setParameter("cpf",CPF);
        query.setMaxResults(1);
        try{
            CadastroCliente cadastroCliente;
            cadastroCliente = (CadastroCliente) query.getSingleResult();
            rollBack();
            return cadastroCliente;
        }catch(NoResultException ex){
            rollBack();
            return null;             
        }
    }
}

Compra

@Entity
@Table(name = “compra”)
@XmlRootElement
@NamedQueries({
@NamedQuery(name = “Compra.findAll”, query = “SELECT c FROM Compra c”)
, @NamedQuery(name = “Compra.findByIdCompra”, query = “SELECT c FROM Compra c WHERE c.idCompra = :idCompra”)
, @NamedQuery(name = “Compra.findByData”, query = “SELECT c FROM Compra c WHERE c.data = :data”)})
public class Compra implements Serializable {

private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "idCompra")
private Integer idCompra;
@Column(name = "data")
@Temporal(TemporalType.DATE)
private Date data;
@JoinTable(name = "compraproduto", joinColumns = {
    @JoinColumn(name = "idCompra", referencedColumnName = "idCompra")}, inverseJoinColumns = {
    @JoinColumn(name = "idProduto", referencedColumnName = "idProduto")})
@ManyToMany() //fetch = FetchType.EAGER
private List<Produto> produtoList;
@JoinColumn(name = "idCliente", referencedColumnName = "idCliente")
@ManyToOne(optional = false)
private Cliente idCliente;

Produto:

@Entity
@Table(name = "produto")
@XmlRootElement
@NamedQueries({
    @NamedQuery(name = "Produto.findAll", query = "SELECT p FROM Produto p")
    , @NamedQuery(name = "Produto.findByIdProduto", query = "SELECT p FROM Produto p WHERE p.idProduto = :idProduto")
    , @NamedQuery(name = "Produto.findByDescricao", query = "SELECT p FROM Produto p WHERE p.descricao = :descricao")
    , @NamedQuery(name = "Produto.findByPreco", query = "SELECT p FROM Produto p WHERE p.preco = :preco")})
public class Produto implements Serializable {

    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "idProduto")
    private Integer idProduto;
    @Basic(optional = false)
    @NotNull
    @Size(max = 100)
    @Column(name = "descricao")
    private String descricao;
    @Basic(optional = false)
    @NotNull
    @Column(name = "preco")
    private float preco;
    @ManyToMany(mappedBy = "produtoList")
    private List<Compra> compraList;

Então, o que acontece é que compraproduto não é uma classe, por isso o erro.

A forma correta de realizar este select é:

FROM Compra c JOIN Produto p WHERE c.idCompra = 1

Isso deve retornar todas as compras.
Como você está querendo os produtos da compra, basta devolver o resultado.getProdutoList()

Nesses dois exemplos acima mostra primeiro como conseguir uma lista e no outro como passar o parâmetro para a @NamedQuery que é feita na sua classe. No hibernate não é bom usar SQL, sempre da algum erro, por isso ele tem as alternativas que ele mesmo mapeia. Eu vi que você já tem uma @NamedQuery igual ao da sua pesquisa, é só substituir pelas do meu exemplo.

Discordo.
Embora a API Criteria seja muito boa, tem coisas que ficam muito mais fáceis quando rodamos com HQL.
Além disso, se o conhecimento sobre Criteria ou HQL é limitado, sempre é possível utilizar Native Queries.

Mas, o melhor sempre será utilizar JDBC puro.

Todas as query feitas em SQL da para fazer com HQL, no caso da query que ele fez de SQL, ele já tinha uma NamedQuery pronta igual aquela, que era fácil de usar e concerteza usando menos código. Utilizar o HQL ajuda a errar menos e fica mais fácil do hibernate achar. Se você fizer a mesma consulta em uma base de dados grande no hibernate uma usando SQL e a outra usando HQL você vai verificar que a que está em HQL vai retornar mais rápido. Por isso eu disse que não é bom usar SQL, não que era proibido.

Criteria/HQL gera SQL, como SQL vai ser mais rápido do que SQL? Só se a pessoa não souber SQL direito e fazer besteira.

Não vem ao caso, mas ambas as formas com Hibernate são ruins em consumo de recursos. Quanto mais engenhocas no meio do caminho mais recursos vai consumir.

é que o hibernate já mantém cache com os recursos dele, instancias a objetos, facilitando o retorno, além de ter vários recursos que integram bem com java, pois a pesquisa não é só ir no banco, mas também colocar seus valores em variáveis. Eu já fiz o teste aqui para saber se havia diferença ao usar o hibernate com SQL ou com HQl, e vi que tinha. Além do que o hibernate já vem com alguns tratamentos de erro que você tem que fazer manual se usar apenas o SQL, hibernate tem drivers específicos de integração com vários bancos e tem melhor portabilidade se por um acaso você precisar mudar por exemplo do MySQL pro Postegre. Com Hibernate diminui quantidade de código da aplicação(isso quando programas comerciais que são maiores). SQL nativo é bom apenas para trabalhos pequenos de faculdade, no Hibernate você acha mais fácil exemplos de utilização de diversos recursos principalmente de novos e no SQL normal você custa a achar coisas prontas. Se quiser saber mais diferenças entre os 2 ai é só procurar na internet.

Tudo isso custa processamento e memória, principalmente o session factory. Isso não é necessário para o resultado na maioria das aplicações atuais (back-end HTTP). Não é eficiente ir até o servidor para acessar cache, que deveria estar no próprio client (navegador ou app). Lazy é outro recurso inútil para HTTP. Para legados desktop concordo que o custo do hibernate traz retornos.

SQL sempre foi bem difundido e não se limita ao mundo do programador Java. Se o profissional depende de coisas prontas é por que não sabe SQL. Sei que JDBC puro é improdutivo, mas existem meios termos como JDBCTemplate da Spring, que é mais leve do que jpa/hibernate, trazendo somente o que é necessário para atender requisições sem estado, como é o exemplo de HTTP.