Hibernate分组求和遇ClassCastException异常的解决方法
Ah, I've run into this exact issue before! The root cause here is straightforward: when you use Hibernate Criteria to run an aggregate query (like sum()) with grouping, Hibernate doesn't return instances of your CropProjections entity. Instead, it returns Object arrays where each array represents a row of your grouped results—with elements ordered exactly as you defined your projections (first the sum value, then the grouped c_code field). Trying to cast these arrays directly to your entity class is what triggers that java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to error.
Let's walk through the fixes, starting with the simplest approach and moving to a cleaner, more maintainable one.
1. Directly Handle Object Arrays
The quickest fix is to adjust your service layer to accept a list of Object[] instead of CropProjections, then extract the values from each array.
Example DAO Code (Criteria Setup)
public List<Object[]> getGroupedSum(String ccCode, Date enteredDate) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(CropProjections.class, "this_"); criteria.add(Restrictions.eq("this_.cc_code", ccCode)); criteria.add(Restrictions.eq("this_.entered_date", enteredDate)); // Define projections: sum first, then grouped field ProjectionList projectionList = Projections.projectionList(); projectionList.add(Projections.sum("this_.actual_qty")); projectionList.add(Projections.groupProperty("this_.c_code")); criteria.setProjection(projectionList); return criteria.list(); }
Example Service Layer Handling
public void processGroupedSumResults() { List<Object[]> results = cropProjectionsDAO.getGroupedSum("1089", Date.valueOf("2018-03-22")); for (Object[] row : results) { // First element is the sum result (type depends on your actual_qty column: BigDecimal/Long/Integer) BigDecimal sumActualQty = (BigDecimal) row[0]; // Second element is the grouped c_code String cCode = (String) row[1]; // Do your processing here System.out.println("C Code: " + cCode + ", Total Actual Qty: " + sumActualQty); } }
2. Use a DTO with ResultTransformer (Cleaner Approach)
For a more type-safe solution, create a Data Transfer Object (DTO) to encapsulate your grouped results, then use Hibernate's ResultTransformer to map the Object arrays to your DTO.
Step 1: Create the DTO Class
public class CropSumDTO { private BigDecimal sumActualQty; private String cCode; // Constructor matching the order of your projections public CropSumDTO(BigDecimal sumActualQty, String cCode) { this.sumActualQty = sumActualQty; this.cCode = cCode; } // Getters (and setters if needed) public BigDecimal getSumActualQty() { return sumActualQty; } public String getcCode() { return cCode; } }
Step 2: Update Criteria to Use Aliases and ResultTransformer
public List<CropSumDTO> getGroupedSumAsDTO(String ccCode, Date enteredDate) { Criteria criteria = sessionFactory.getCurrentSession().createCriteria(CropProjections.class, "this_"); criteria.add(Restrictions.eq("this_.cc_code", ccCode)); criteria.add(Restrictions.eq("this_.entered_date", enteredDate)); ProjectionList projectionList = Projections.projectionList(); // Add aliases for each projection to match DTO constructor/field names projectionList.add(Projections.sum("this_.actual_qty"), "sumActualQty"); projectionList.add(Projections.groupProperty("this_.c_code"), "cCode"); criteria.setProjection(projectionList); // Map the results to your DTO criteria.setResultTransformer(Transformers.aliasToBean(CropSumDTO.class)); return criteria.list(); }
Step 3: Service Layer Usage (Type-Safe!)
public void processGroupedSumDTOs() { List<CropSumDTO> results = cropProjectionsDAO.getGroupedSumAsDTO("1089", Date.valueOf("2018-03-22")); for (CropSumDTO dto : results) { System.out.println("C Code: " + dto.getcCode() + ", Total Actual Qty: " + dto.getSumActualQty()); } }
Note: For Hibernate 5.2+,
AliasToBeanResultTransformeris deprecated. UseTransformers.aliasToBean()instead, which is the recommended replacement.
3. Alternative: Use HQL Instead of Criteria
If you find Criteria API cumbersome for aggregate queries, you can achieve the same result with HQL, which often reads more like your target SQL:
public List<CropSumDTO> getGroupedSumViaHQL(String ccCode, Date enteredDate) { String hql = "SELECT SUM(c.actual_qty) as sumActualQty, c.c_code as cCode " + "FROM CropProjections c " + "WHERE c.cc_code = :ccCode AND c.entered_date = :enteredDate " + "GROUP BY c.c_code"; Query query = sessionFactory.getCurrentSession().createQuery(hql); query.setParameter("ccCode", ccCode); query.setParameter("enteredDate", enteredDate); query.setResultTransformer(Transformers.aliasToBean(CropSumDTO.class)); return query.list(); }
This works exactly like the Criteria DTO approach, but the query syntax is closer to your original SQL.
内容的提问来源于stack exchange,提问作者Chathura Prabhash

