Cast Dinamico

Acho que ao invés de:

ProdutoEmEstoque produtoVO = (ProdutoEmEstoque) criteria.uniqueResult();

O correto seria:

T produtoVO = (T) criteria.uniqueResult();

(EDIT) Ou melhor, com o cast dinâmico:

T produtoVO = objClass.cast(criteria.uniqueResult());

Funcionou o esquema que estava querendo implementar, segui as dicas dos colegas. :smiley:
Criei duas interfaces com os métodos em comun para os VOs.
Interfaces:

public interface ProdutoVO {

	/** Referente a tabela saldo de produtos **/
	public <T extends ProdutoSaldoVO> T getProdutoSaldoVO();

	/** Referente a tabela grupo de produtos **/
	public GrupoProdSigaVO getGrupoProdSigaVO();
	public void setGrupoProdSigaVO(GrupoProdSigaVO grupoProdSigaVO);

	/** Unidade de Medida **/
	public String getUnidadeMedida() ;
	public void setUnidadeMedida(String unidadeMedida);

	/** Código **/
	public String getCodgProduto();
	public void setCodgProduto(String codgProduto);

	/** Descrição **/
	public String getDescProduto();
	public void setDescProduto(String descProduto);

	/** Id **/
	public Integer getIdProduto();
	public void setIdProduto(Integer idProduto) ;

	/** Estoque **/
	public String getFlagEstoque();
	public void setFlagEstoque(String flagEstoque);

	/** Valor Standard  **/
	public BigDecimal getVlrStandard();
	public void setVlrStandard(BigDecimal vlrStandard);

	/** Valor do Ultimo Preço **/
	public BigDecimal getVlrUltPreco();
	public void setVlrUltPreco(BigDecimal vlrUltPreco);

	/** D_E_L_E_T da tabela do microsiga **/
	public String getDelet();
	public void setDelet(String delet);
}
public interface ProdutoSaldoVO {

	/** Código **/
	public String getCodgProdutoSaldo();
	public void setCodgProdutoSaldo(String codgProdutoSaldo);

	/** Quantidade **/
	public Integer getQuantidade();
	public void setQuantidade(Integer quantidade);

	/** Valor **/
	public BigDecimal getValorProd();
	public void setValorProd(BigDecimal valorProd);

	/** D_E_L_E_T da tabela do microsiga **/
	public String getDelet();
	public void setDelet(String delet);
}

As classes VOs, ficaram com esta estrutura, distinguindo apenas pelo nome, tabela que esta mapeada e o ProdutoSaldoxxxVO que esta relacionado a ProdutoxxxVO.

@Entity
@Table(name="sb1010", schema="sigavalidacao")
@Where(clause="d_e_l_e_t_ <> '*'")
public class ProdutoUnicVO implements ProdutoVO
{
	private static final long serialVersionUID = 1L;

	@Column(name="r_e_c_n_o_")
	private Integer idProduto;

	@Id
	@Column(name="b1_cod", insertable=false, updatable=false)
	private String codgProduto;

	@Column (name="b1_desc")
	private String descProduto;

	@Column (name="b1_estoque")
	private String flagEstoque;

	@Column (name="b1_um")
	private String unidadeMedida;

	@Column (name="b1_custd")
	private BigDecimal vlrStandard;

	@Column (name="b1_uprc")
	private BigDecimal vlrUltPreco;

	@Column (name="d_e_l_e_t_")
	private String delet;

	@OneToOne(cascade = CascadeType.ALL)
	@JoinColumn(name = "b1_grupo")
	private GrupoProdSigaVO grupoProdSigaVO;

	@OneToOne(cascade = CascadeType.ALL/*, fetch = FetchType.LAZY*/)
	@JoinColumn(name = "b1_cod")
	private ProdutoSaldoUnicVO produtoSaldoVO;

	/** Referente a tabela saldo de produtos **/
	public ProdutoSaldoUnicVO getProdutoSaldoVO() { return produtoSaldoVO; }
	public final void setProdutoSaldoVO(ProdutoSaldoUnicVO produtoSaldoVO) {
		this.produtoSaldoVO = produtoSaldoVO;
	}

	/** Referente a tabela grupo de produtos **/
	public final GrupoProdSigaVO getGrupoProdSigaVO() { return grupoProdSigaVO; }
	public final void setGrupoProdSigaVO(GrupoProdSigaVO grupoProdSigaVO) {
		this.grupoProdSigaVO = grupoProdSigaVO;
	}

	/** Unidade de Medida **/
	public final String getUnidadeMedida() { return unidadeMedida; }
	public final void setUnidadeMedida(String unidadeMedida) {
		this.unidadeMedida = unidadeMedida;
	}

	/** Código **/
	public final String getCodgProduto() { return codgProduto; }
	public final void setCodgProduto(String codgProduto) {
		this.codgProduto = codgProduto;
	}

	/** Descrição **/
	public final String getDescProduto() { return descProduto; }
	public final void setDescProduto(String descProduto) {
		this.descProduto = descProduto;
	}

	/** Id **/
	public final Integer getIdProduto() { return idProduto; }
	public final void setIdProduto(Integer idProduto) {
		this.idProduto = idProduto;
	}

	/** Estoque **/
	public final String getFlagEstoque() { return flagEstoque; }
	public final void setFlagEstoque(String flagEstoque) {
		this.flagEstoque = flagEstoque;
	}

	/** Valor Standard  **/
	public final BigDecimal getVlrStandard() { return vlrStandard; }
	public final void setVlrStandard(BigDecimal vlrStandard) {
		this.vlrStandard = vlrStandard;
	}

	/** Valor do Ultimo Preço **/
	public final BigDecimal getVlrUltPreco() { return vlrUltPreco; }
	public final void setVlrUltPreco(BigDecimal vlrUltPreco) {
		this.vlrUltPreco = vlrUltPreco;
	}

	/** D_E_L_E_T da tabela do microsiga **/
	public final String getDelet() { return delet; }
	public final void setDelet(String delet) {
		this.delet = delet;
	}
}

ProdutoSaldoxxxVO:

@Entity
@Table(name="sb2010", schema="sigavalidacao")
@Where(clause="d_e_l_e_t_ <> '*'")
public class ProdutoSaldoUnicVO implements ProdutoSaldoVO
{
	@Id
	@Column(name="b2_cod")
	private String codgProdutoSaldo;

	@Column(name="b2_qatu")
	private Integer quantidade;

	@Column (name="b2_cm1")
	private BigDecimal valorProd;

	@Column (name="d_e_l_e_t_")
	private String delet;

	/** Código **/
	public final String getCodgProdutoSaldo() { return codgProdutoSaldo; }
	public final void setCodgProdutoSaldo(String codgProdutoSaldo) {
		this.codgProdutoSaldo = codgProdutoSaldo;
	}

	/** Quantidade **/
	public final Integer getQuantidade() { return quantidade; }
	public final void setQuantidade(Integer quantidade) {
		this.quantidade = quantidade;
	}

	/** Valor **/
	public final BigDecimal getValorProd() { return valorProd; }
	public final void setValorProd(BigDecimal valorProd) {
		this.valorProd = valorProd;
	}

	/** D_E_L_E_T da tabela do microsiga **/
	public final String getDelet() { return delet; }
	public final void setDelet(String delet) {
		this.delet = delet;
	}
}

DAO para consulta:


public class ProdutoDAOImpl  extends HibernateGenericDAO<ProdutoUnicVO, String> implements ProdutoDAO {

	/** Creates a new instance of ProdutoUnicDAOImpl **/
	public ProdutoDAOImpl ()
	{
		super(ProdutoUnicVO.class);
	}

	/**
	 * Método que busca um ProdutoVO da tabela de produto da UNIC, UNIME ou FAMA a partir do código do produto.
	 *
	 * @param <T>
	 * @param objClass
	 * @param codgProduto
	 * @param tipoSolicConsProd
	 * @param codgUsuario
	 * @return
	 * @throws Exception
	 */
	public <T extends ProdutoVO> T consultarProdutoPorCodg(Class<T> objClass, String codgProduto,
			String tipoSolicConsProd, Integer codgUsuario) throws Exception
	{
        Criteria criteria = this.getSession().createCriteria(objClass);
		if(tipoSolicConsProd != null && !tipoSolicConsProd.equals("")) {
			criteria.add(Restrictions.eq("flagEstoque", tipoSolicConsProd.equals("1") ? "S" : "N"));
			criteria.add(Restrictions.in("grupoProdSigaVO.codgGrupoProd", this.consultaListaGrupoProdPorCodgUsuario(codgUsuario)));
			criteria.add(Restrictions.eq("codgProduto", StringUtil.preencheDir(codgProduto, 15, ' ')));
		} else {
			criteria.add(Restrictions.eq("codgProduto", StringUtil.preencheDir(codgProduto, 15, ' ')));
		}
		T produtoVO = objClass.cast(criteria.uniqueResult());

        this.carregaDadosProdutoVO(produtoVO);
		this.verificaMaiorValorPorSolicitacao(produtoVO);

        return (T) produtoVO;
	}

	/**
	 * Método que busca uma lista de Produtos da tabela de produto da UNIC, UNIME ou FAMA a partir de uma descrição.
	 *
	 * @param <T>
	 * @param objClass
	 * @param descProduto
	 * @param tipoSolicConsProd
	 * @param codgUsuario
	 * @return
	 * @throws Exception
	 */
	public <T extends ProdutoVO> List<T> consultarProdutoPorDesc(Class<T> objClass, String descProduto,
			String tipoSolicConsProd, Integer codgUsuario) throws Exception {
		List<T> produtos = null;

		Criteria criteria = this.getSession().createCriteria(objClass);
		if(tipoSolicConsProd != null && !tipoSolicConsProd.equals("")) {
			criteria.add(Restrictions.eq("flagEstoque", tipoSolicConsProd.equals("1") ? "S" : "N"));
			criteria.add(Restrictions.in("grupoProdSigaVO.codgGrupoProd", this.consultaListaGrupoProdPorCodgUsuario(codgUsuario)));
			criteria.add(Expression.like("descProduto", "%"+descProduto.toUpperCase()+"%"));
		} else {
			criteria.add(Expression.like("descProduto", "%"+descProduto.toUpperCase()+"%"));
		}
		produtos = (ArrayList<T>) criteria.list();

		// Altera Valores Produtos
		for(ProdutoVO produtoVO: produtos) {
			this.carregaDadosProdutoVO(produtoVO);
			this.verificaMaiorValorPorSolicitacao(produtoVO);
		}
		return produtos;
	}

	/**
	 * Método listar todos produtos data tabela da UNIC, UNIME ou FAMA.
	 *
	 * @param <T>
	 * @param objClass
	 * @param tipoSolicConsProd
	 * @param codgUsuario
	 * @return
	 * @throws Exception
	 */
	public <T extends ProdutoVO> List<T> listarProdutos(Class<T> objClass, String tipoSolicConsProd,
			Integer codgUsuario) throws Exception {
		List<T> produtos = null;

		Criteria criteria = this.getSession().createCriteria(objClass);
		if(tipoSolicConsProd != null && !tipoSolicConsProd.equals("")) {
			criteria.add(Restrictions.eq("flagEstoque", tipoSolicConsProd.equals("1") ? "S" : "N"));
			criteria.add(Restrictions.in("grupoProdSigaVO.codgGrupoProd", this.consultaListaGrupoProdPorCodgUsuario(codgUsuario)));
		}
		produtos = (ArrayList<T>) criteria.list();

		// Altera Valores Produtos
		for(ProdutoVO produtoVO: produtos) {
			this.carregaDadosProdutoVO(produtoVO);
			this.verificaMaiorValorPorSolicitacao(produtoVO);
		}
		return produtos;
	}

	/**
	 * Seta alguns dados no produtoVO passado como parãemtro.
	 *
	 * @param produtoVO
	 */
	private void carregaDadosProdutoVO(ProdutoVO produtoVO) {
		if(produtoVO.getFlagEstoque() != null) {
			if(produtoVO.getFlagEstoque().equals("S")) {
				produtoVO.setFlagEstoque("Sim");
			} else {
				produtoVO.setFlagEstoque("Não");
			}
		}
		produtoVO.setCodgProduto(produtoVO.getCodgProduto().trim());
	}

	/**
	 * Verifica se o produto existe em estoque.
	 * Sim - Pega o maior valor entre o custo médio, ultimo preço e valor standard
	 * Não - Pega o valor do custo médio
	 *
	 * @param produtoVO
	 */
	private void verificaMaiorValorPorSolicitacao(ProdutoVO produtoVO)
	{
		if(produtoVO.getProdutoSaldoVO() != null ) {
			float vlrProduto = 0;
			if(produtoVO.getProdutoSaldoVO().getValorProd() != null) {
				vlrProduto = produtoVO.getProdutoSaldoVO().getValorProd().floatValue();
			}

			if(produtoVO.getFlagEstoque().equals("Sim")) {
				float percenVlrProd = (vlrProduto + (vlrProduto * 15 / 100));
				produtoVO.getProdutoSaldoVO().setValorProd(new BigDecimal(percenVlrProd).setScale(2,BigDecimal.ROUND_HALF_DOWN));
			}
			else {
				float vlrStandard = 0;
				float vlrUltPreco = 0;
				if(produtoVO.getVlrStandard() != null) {
					vlrStandard = produtoVO.getVlrStandard().floatValue();
				}
				if(produtoVO.getVlrUltPreco() != null) {
					vlrStandard = produtoVO.getVlrUltPreco().floatValue();
				}

				if(vlrProduto > vlrStandard && vlrProduto > vlrUltPreco) {
					float percenVlrProd = (vlrProduto + (vlrProduto * 15 / 100));
					produtoVO.getProdutoSaldoVO().setValorProd(new BigDecimal(percenVlrProd).setScale(2,BigDecimal.ROUND_HALF_DOWN));
				}
				else if(vlrStandard > vlrProduto && vlrStandard > vlrUltPreco) {
					float percenVlrStand = (vlrStandard + (vlrStandard * 15 / 100));
					produtoVO.getProdutoSaldoVO().setValorProd(new BigDecimal(percenVlrStand).setScale(2,BigDecimal.ROUND_HALF_DOWN));
				}
				else if(vlrUltPreco > vlrProduto && vlrUltPreco > vlrStandard) {
					float percenVlrUltPr = (vlrUltPreco + (vlrUltPreco * 15 / 100));
					produtoVO.getProdutoSaldoVO().setValorProd(new BigDecimal(percenVlrUltPr).setScale(2,BigDecimal.ROUND_HALF_DOWN));
				} else {
					produtoVO.getProdutoSaldoVO().setValorProd(new BigDecimal(0.0f).setScale(2,BigDecimal.ROUND_HALF_DOWN));

					new BigDecimal((Double)123.456).setScale(2,BigDecimal.ROUND_HALF_DOWN);
				}
			}
		}
	}

	/**
	 * Consulta Grupo de produtos vinculados ao usuário, por código
	 *
	 * @param codgUsuario
	 * @throws Exception
	 */
	private Object[] consultaListaGrupoProdPorCodgUsuario(Integer codgUsuario) throws Exception
	{
		Criteria criteria = this.getSession().createCriteria(ConfigGrupoProdVO.class);
		criteria.add(Restrictions.eq("usuarioVO.codgUsuario", codgUsuario));
		criteria.addOrder( Order.asc("grupoProdSigaVO.codgGrupoProd") );
		List<ConfigGrupoProdVO> confGrupoProds = (ArrayList<ConfigGrupoProdVO>) criteria.list();

		Object[] grupoProdsSiga = new Object[confGrupoProds.size()];
		int i = 0;
		for (ConfigGrupoProdVO c : confGrupoProds) {
			grupoProdsSiga[i++] = StringUtil.preencheDir(c.getGrupoProdSigaVO().getCodgGrupoProd(), 4, ' ');
		}
		return grupoProdsSiga;
	}

}

Apenas faço uma verificação, pela empresa que o usuário esta relacionado, pra realizar a chamada dos métodos de consulta:

switch (codgFilial) {
	case 1: {
		produtoVO = produtoService.consultarProdutoPorCodg(ProdutoUnicVO.class, codgProd, tipoSolicConsProd, codgUsuario);
		break;
	} case 2: {
		produtoVO = produtoService.consultarProdutoPorCodg(ProdutoUnimeVO.class, codgProd, tipoSolicConsProd, codgUsuario);
		break;
	} case 3: {
		produtoVO = produtoService.consultarProdutoPorCodg(ProdutoFamaVO.class, codgProd, tipoSolicConsProd, codgUsuario);
		break;
	}
}

Acho que é isso ai, caso alguém tenha uma sugestão ou alguma dica para melhorar esta implementação, posta ai para discutirmos.
OBRIGADO A TODOS PELA ATENÇÂO.

Na linha 36 de ProdutoDAOImpl você não precisa mais do cast.

Não use generics no método getProdutoSaldoVO, e sim retorno covariante.

public interface ProdutoVO {

	/** Referente a tabela saldo de produtos **/
	public ProdutoSaldoVO getProdutoSaldoVO();

	...
...
public class ProdutoUnicVO implements ProdutoVO
{
	...

	/** Referente a tabela saldo de produtos **/
	public ProdutoSaldoUnicVO getProdutoSaldoVO() { return produtoSaldoVO; }
	...

No DAO, o seu generics da herança tá errado.

O certo seria mais ou menos isso:

public class ProdutoDAOImpl<T extends ProdutoVO> extends HibernateGenericDAO<T, String> implements ProdutoDAO {

	private final Class<T> objClass;
	/** Creates a new instance of ProdutoUnicDAOImpl **/
	public ProdutoDAOImpl (Class<T> objClass)
	{
		super(objClass);
		this.objClass = objClass;
	}

        ...

Mas, antes de fazer isso, tenho que saber primeiro o que o construtor de HibernateGenericDAO faz com o parâmetro Class recebido.

[quote=victorwss]
(EDIT) Ou melhor, com o cast dinâmico:

T produtoVO = objClass.cast(criteria.uniqueResult()); [/quote]

Sim, isso mesmo

Verdade, eu não tinha pensado na solução utilizando retorno covariante, mas funciona tb… rsrsr…

Olha ai victors, como é miha classe HibernateGenericDAO.
Interface GenericDAO:

public interface GenericDAO<T, PK extends Serializable> {

	/**
	 * Save or Update
	 * @param obj
	 * @return
	 */
	public T saveOrUpdate(final T obj);

	/**
	 * Delete Obj
	 * @param object
	 */
	public void delete(final T object);

	/**
	 * List all Objs
	 * @return
	 */
	public List<T> listAll ();

	/**
	 * Consult Obj por Codg
	 * @param primaryKey
	 * @return
	 */
	public T get (final PK primaryKey);

	/**
	 * Get Obj
	 * @return
	 */
	public Class<T> getObjClass();

	/**
	 * Para setar a session nos DAOs
	 * @param session
	 */
	public void setSession(Session session);

}

Classe HibernateGenericDAO:

public class HibernateGenericDAO<T, PK extends Serializable> implements GenericDAO<T, PK>
{
	/** Objeto que referência o log da aplicação */
	protected final Logger logger = Logger.getLogger(getClass());

	/** Objeto que referencia a classe a ser persistida no banco de dados. */
	private Class<T> objClass;

	/** Objeto que referência a Session do hibernate */
	private Session session = null;

	/** Referente aos métodos acessores do objeto session */
	public Session getSession()	{ return this.session; }
	public void setSession(Session session) {
        this.session = session;
    }

	/** Construtor de objetos da classe HibernateGenericDAO. */
	public HibernateGenericDAO (final Class<T> classParam) {
		this.objClass = classParam;
	}

	public Class<T> getObjClass() {
        return this.getObjClass();
    }

	/**
	 * Método:
	 *   - salva um registro no banco de dados.
	 *                     ou
	 *   - atualiza um registro no banco de dados.
	 *
	 * @param object Objeto a ser salvo no banco de dados.
	 * @throws Exception
	 */
	public T saveOrUpdate(final T obj)
	{
       	getSession().saveOrUpdate(obj);
        return obj;
    }

	/**
	 * Método que remove um objeto no banco de dados.
	 *
	 * @param object Objeto a ser removido no banco de dados.
	 */
	public void delete(final T obj)
	{
		getSession().delete(obj);
    }

	/**
	 * Método que busca um objeto de um tipo no banco de dados.
	 *
	 * @param primaryKey Identificação do tipo no banco de dados.
	 * @return Objeto buscado no banco de dados.
	 */
	public T get(final PK primaryKey)
	{
        final T obj = (T)getSession().get(objClass, primaryKey);
        return obj;
    }

	/**
	 * Método que lista todos os objetos de um tipo no banco de dados.
	 *
	 * @return Lista com todos os objetos de um tipo buscado no banco de dados.
	 */
	public List<T> listAll()
	{
        Criteria criteria = getSession().createCriteria(this.objClass);
        final List<T> list = criteria.list();
        return list;
    }
}

Realmente, a minha classe ProdutoDAOImpl esta incorreta, pois esta utilizando um VO(ProdutoUnicVO) especifico na declaração da classe.
Vou analisar para ver oque pode ser modificado.
Você tem uma sugestão ?

Na classe HibernateGenericDAO:

Faça ele ser final. Coloque ele como protected ou public também. Como o atributo objClass não é o tipo de coisa que você vai ficar mudando após o objeto já ter sido criado e também não é algo onde polimorfismo seja desejável, então não tem problema em o atributo ser “public final” ou “protected final”.

Isso está errado, vai dar StackOverflowError. O certo é isso (observe o “final”):

Ou, se você preferir, pode apagar este método, desde que o campo objClass seja no mínimo protected.

Assim fica melhor (eu acho):

public T get(final PK primaryKey)
{
    return objClass.cast(getSession().get(objClass, primaryKey));
}

Agora o seu ProdutoDAOImpl:

public class ProdutoDAOImpl&lt;T extends ProdutoVO&gt; extends HibernateGenericDAO&lt;T, String&gt; implements ProdutoDAO {

	/** Creates a new instance of ProdutoUnicDAOImpl **/
	public ProdutoDAOImpl (Class&lt;T&gt; objClass)
	{
		super(objClass);
	}

	... Todos os outros métodos.
}

Daí, no seu ProdutoDAOImpl, você pode retirar todos os “<T extends ProdutoVO>” que estão antes do tipo de retorno dos métodos, uma vez que este generic já está na classe, e portanto não é necessário que cada método tenha o seu. Você também pode retirar os parâmetros “Class<T> objClass” dos métodos, porque isto já é um atributo herdado do HibernateGenericDAO.