Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JPA/Hibernate returning BigDecimal not Long

I'm computing a SUM grouped by months

Query q = entityManager.createNativeQuery(qlString);
q.setParameter("program", program);
@SuppressWarnings("unchecked")
List<Long> resultList = (List<Long>) q.getResultList();
long tend = System.currentTimeMillis();

When I pass in two resultsLists (closed:ResultsList of Closed items, closedLate: ResultsList of items Closed late) into a method that computes percentages, I get

 javax.servlet.ServletException: java.lang.ClassCastException: java.math.BigDecimal cannot be cast to java.lang.Long

.

private List<Long> computeOTR(List<Long> closed, List<Long> closedLate) {
    List<Long> monthlyOTR = new ArrayList<Long>();
    long numerator;
    Long denominator;
    for (int i = 0; i <11; i++) {
        numerator = closed.get(i) - closedLate.get(i); <----java.lang.ClassCastException
        denominator = closed.get(i);
        long percentage = (int)(numerator * 100.0 / denominator + 0.5);
        monthlyOTR.add(i, percentage);
    }
    return monthlyOTR;

}

In Eclipse debug mode closed is showing as BigDecimal. Why is this when I decalre

List<Long> resultList = (List<Long>) q.getResultList();

EDIT-Hibernate Query:

public List<Long> findClosedLateByProgram(String program) {

    long tstart = System.currentTimeMillis();
    //@formatter:off
    String qlString = "with PRJ as ( " +
            "select  trunc(END_DATE) as END_DATE,  " +
            "trunc(NEED_DATE) as NEED_DATE   " +
            "from (SELECT UNIQUE * FROM TEST where PROGRAM_NAME = :program " +
            "AND ACTION_BY_ORG = 'AAA') " +
            "),  " +
            "DATES as ( select add_months(trunc(last_day(SYSDATE)), level-7) as thedate  " +
            "from dual connect by level <= 12  )   " +
            "SELECT nvl(sum(case when NEED_DATE <  trunc(thedate,'mm') AND END_DATE between trunc(thedate,'mm') and thedate then 1 end), 0 ) as CLOSED_LATE  " +
            "FROM  DATES, PRJ  " +
            "GROUP BY thedate ORDER BY thedate";
    //@formatter:on

    Query q = entityManager.createNativeQuery(qlString);
    q.setParameter("program", program);
    // q.setParameter("today",date, TemporalType.DATE);

    @SuppressWarnings("unchecked")
    List<Long> resultList =  q.getResultList();
    long tend = System.currentTimeMillis();
    long elapsed = tend-tstart;
    System.out.println("Elapsed Time For Closed But Late: " + elapsed);
    return resultList;
}

EDIT 2

I think I am stuck with a BigDecimal? http://weblogs.java.net/blog/mb124283/archive/2007/04/java_persistenc.html

like image 397
jeff Avatar asked Mar 11 '13 18:03

jeff


2 Answers

You should already be getting a warning showing that your cast isn't really checking things fully. Type erasure means that at execution time, there's no difference between a List<Long> and a List<BigDecimal>. So the cast succeeds, and it's only the later implicit cast to Long which fails.

Basically you need to change your query to make sure it creates Long values instead.

like image 78
Jon Skeet Avatar answered Oct 05 '22 00:10

Jon Skeet


I just faced the same problem.

One solution is to add scalar (https://stackoverflow.com/a/29479658).

But in my case (Spring data jpa with @Query annotation), I couldn't add it. One workaround is to get result list as a List <? extends Number> (superclass of Long and BigInteger)

Then you can call the Number's method longValue().

In java 8, your sample could become:

List<? extends Number> resultListAsNumber =  q.getResultList();
List<Long> resultList = resultListAsNumber.stream().map(i -> i.longValue()).collect(Collectors.toList());

This solution avoids String convertion and will work if one day hibernate returns Long.

like image 22
LaurentM Avatar answered Oct 05 '22 00:10

LaurentM