JPA/Hibernate returning BigDecimal not Long - java

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

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.

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.

You should iterate over the results and convert the objects from String:
Query query = createSQLQuery(sql);
List<Long> ids = new java.util.ArrayList<Long>();
java.util.Iterator res = query.list().iterator();
try {
while(res.hasNext()){
ids.add(new Long(res.next().toString()));
}
} catch(Exception ex) {
ex.printStackTrace();
}

Related

Convert to DTO from join selection

I have these two tables
And I am using this query to get the results
#Query(value = "SELECT bet.bet, match.name, match.week FROM bet INNER JOIN match ON match.id=bet.match_id WHERE match.week = ?1", nativeQuery = true)
List<Object[]> customQuery(Long week);
So far this is the only way I could retrieve the results and actually use them later.
To use them I am using this code currently
List<Object[]> bets = gr.customQuery(2l);
for (Object[] object : bets) {
int bet = (BigInteger) object[0];
String name = (String) object[1];
int week = (BigInteger) object[2];
System.out.println(bet + " " + name + " " + week);
}
But using it that way seems odd to me. Is there a better way to directly map the result to a DTO or something...
There are some options. The most straighforward way would be to define a projection Interface, like:
public interface BetDTO {
Long getBet();
String getName();
Integer getWeek();
}
With that you could change your query return type to:
List<BetDTO> customQuery(Integer week);
The rest would then be using getters.

Wrong BigInteger result using hibernate native query

I'm using Hibernate4 + PostgresSql.
I need to get a list of ids from my DB without getting a whole object.
I use a native query (because it's hierarchical) and it looks like:
String s = hierachical_part_skipped +"select id " +
"from search_hierarchy " +
"order by id";
Query q = getEntityManager().createNativeQuery(s);
List<Long> resList = new ArrayList<>();
List<BigInteger> biglist = q.getResultList();
for (Object id : biglist) {
System.out.print(id+",");
resList.add(id.longValue());
}
Well, 1 time out of three my bigList is filled with wrong values!
I have
10,20,30,40,50,60,70,80,90,100 ... 27350
instead of
1, 2... 2735
(a zero is added to each value).
Native query executed manually not through Hibernate returns correct values.
I've tried retrieving result as List of Object, but the result was the same
I have found a sufficient workaround
s.append("select id \\:\\:numeric " + //that's a hack
"from search_department " +
"order by id");
Query q = getEntityManager().createNativeQuery(s);
List<Long> resList = new ArrayList<>();
List<BigDecimal> biglist = q.getResultList();
for (BigDecimal id : biglist) {
System.out.print(id + ",");
resList.add(id.longValue());
}
Still, I'm wondering why BigInteger works incorrectly

How to use AND SqlRestriction which contains OR condition inside?

I'm facing problems in using Restrictions. I've a table employee which has following structure :
id : int (primary key)
create_date : datetime
modified_date: datetime
I'm using following code to list down an employee if it's created/modified within a particular time interval :
Criteria criteria = getSession().createCriteria(Employee.class);
criteria.add(Restrictions.eq("id", employeeId));
if (interval > 0) {
String sql = "{alias}.create_date > DATE_SUB(NOW(), INTERVAL " + interval + " SECOND) OR {alias}.modified_date > DATE_SUB(NOW(), INTERVAL " + interval + " SECOND)";
criteria.add(Restrictions.sqlRestriction(sqlWhere));
}
List<Employee> employeeList = criteria.list();
Please note that there is a OR condition inside the SqlRestriction.
Now suppose employeeId = 10 and interval = 3600, the employeeList contains other employees along with id=10 which should not happen.
Should I use Restrictions.and or Restrictions.conjunction to solve it ? Or I'm missing something else ?
There's no magic here. Use Restrictions.and method to group two criterion. Hibernate should automatically group subqueries to achieve the desired results.
Criteria criteria = getSession().createCriteria(Employee.class);
Criterion whereClause = Restrictions.eq("id", employeeId);
if (interval > 0) {
String sql = "{alias}.create_date > DATE_SUB(NOW(), INTERVAL " + interval + " SECOND) OR {alias}.modified_date > DATE_SUB(NOW(), INTERVAL " + interval + " SECOND)";
Criterion andConjunction = Restrictions.and(
whereClause,
Restrictions.sqlRestriction(sqlWhere)
);
whereClause = andConjunction;
}
criteria.add(whereClause);
List<Employee> employeeList = criteria.list();

From List to int. Unable to convert a query result

I have this query:
select count(*) from {Order as or join CustomerOrderStatus as os on
{or:CustomerOrderStatus}={os:pk} join OrderEntry as oe on
{or.pk}={oe.order} join PurchaseAmount as pa on
{or.pointOfSale}={pa.purchaseAmountOwner} join PurchaseAmountTimeSlice
as ts on {pa.pk}={ts.purchaseamount}} where {or:company} in
(8796093710341) and {or:pointOfSale} in (8796097413125)
I have this code in Java to retrieve result:
FlexibleSearchQuery query = new FlexibleSearchQuery(queryBuilder.toString());
List<Integer> result = new ArrayList<Integer>();
result = getFlexibleSearchService().<Integer> search(query).getResult();
I want to take the int value of the count from the result list.
If I write result.get(0) I obtain an error IllegalArgumentException:
java.lang.IllegalArgumentException: invalid pks [4] - unknown typecode 0
at de.hybris.platform.core.WrapperFactory.getCachedItems(WrapperFactory.java:304)
at de.hybris.platform.core.LazyLoadItemList.loadPage(LazyLoadItemList.java:230)
at de.hybris.platform.servicelayer.search.impl.LazyLoadModelList.loadPage(LazyLoadModelList.java:60)
at de.hybris.platform.core.LazyLoadItemList.switchPage(LazyLoadItemList.java:219)
at de.hybris.platform.core.LazyLoadItemList.switchBufferedPageNoLock(LazyLoadItemList.java:475)
at de.hybris.platform.core.LazyLoadItemList.switchBufferedPageSynchronized(LazyLoadItemList.java:467)
at de.hybris.platform.core.LazyLoadItemList.switchBufferedPage(LazyLoadItemList.java:462)
at de.hybris.platform.core.LazyLoadItemList.getOrSwitchBufferedPage(LazyLoadItemList.java:453)
at de.hybris.platform.core.LazyLoadItemList.getOrSwitchBufferedPage(LazyLoadItemList.java:433)
at de.hybris.platform.core.LazyLoadItemList.getBuffered(LazyLoadItemList.java:111)
at de.hybris.platform.core.LazyLoadItemList.get(LazyLoadItemList.java:97)
at java.util.AbstractList$Itr.next(AbstractList.java:358)
at de.hybris.platform.core.internal.BaseLazyLoadItemList$1.next(BaseLazyLoadItemList.java:180)
at java.util.AbstractCollection.toArray(AbstractCollection.java:195)
at java.util.Collections$UnmodifiableCollection.toArray(Collections.java:1059)
How I can get the int value?
Replace count(*) with count({or:Pk}) and set result class of flexible search query to Integer like this query.setResultClassList(Collections.singletonList(Integer.class));
I resolved in this way:
private FlexibleSearchService flexibleSearchService;
#Override
public void onValidate(PurchaseAmountTimeSliceModel paramMODEL, InterceptorContext paramInterceptorContext)
throws InterceptorException {
//.....
final StringBuilder queryBuilder = new StringBuilder();
queryBuilder.append("select {ts.pk}");
queryBuilder.append(
" from {Order as or join CustomerOrderStatus as os on {or:CustomerOrderStatus}={os:pk} join OrderEntry as oe on {or.pk}={oe.order} ");
queryBuilder.append(
"join PurchaseAmount as pa on {or.pointOfSale}={pa.purchaseAmountOwner} join PurchaseAmountTimeSlice as ts on {pa.pk}={ts.purchaseamount}} where ");
if (pointOfSale != null && company != null) {
queryBuilder.append("{or:company} in (" + company.getPk() + ") and {or:pointOfSale} in ("
+ pointOfSale.getPk() + ")");
}
FlexibleSearchQuery query = new FlexibleSearchQuery(queryBuilder.toString());
SearchResult<PurchaseAmountTimeSliceModel> result = flexibleSearchService.search(query);
if (result != null) {
if (result.getCount() >= 10) {
LOG.error("There's already 10 purchase amount time slices configured. You reached the limit.");
throw new InterceptorException(
"There's already 10 purchase amount time slices configured. You reached the limit.");
}
}
//.....
The solution is similar ;)

JPQL collection parameter - mismatch the expected type

A bit of context: I have a Spring app with Hibernate.
I want to get all Location entities filtered by ID so I pass a set of IDs as parameter to the query. The problem is that on the query.setParameter("ids", locationIds); row I get the following error:
:Parameter value element [728331] did not match expected type [java.lang.Long (n/a)]
I am confused since the set I am giving is set of Long values. So I assume no explicit casting should be done when passing it as parameter, right? Does anyone has suggestion what is causing the error?
I checked other similar questions but I didn't find one that solve my issue.
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public class LocationDao {
#PersistenceContext
private EntityManager em;
public List<Location> getLocationsByIds(Set<Long> locationIds) {
if (locationIds == null || locationIds.isEmpty()) {
return null;
}
final TypedQuery<Location> query =
em.createQuery("FROM Location l WHERE l.id IN :ids", Location.class);
query.setParameter("ids", locationIds);
return query.getResultList();
}
}
#Entity
#Table(name = "location")
public class Location {
#Id
private Long id;
// other fields
}
EDIT: Hibernate entity manager version: 4.3.8.Final
Found the problem. The locationIds are not exactly Set<Long> locationIds but Set<BigInteger>.
I retrieve the IDs through a native query since I need to perform recursive search in locations. Although I cast it to List<Long> it is actually returns a List<BigInteger>. Here is the code:
private static final String SQL_FIND_LOCATION_AND_CHILDREN_IDS =
" WITH RECURSIVE result_table(id) AS ( "
+ " SELECT pl.id "
+ " FROM location AS pl "
+ " WHERE pl.id = :parentId "
+ "UNION ALL "
+ " SELECT c.id "
+ " FROM result_table AS p, location AS c "
+ " WHERE c.parent = p.id "
+ ") "
+ "SELECT n.id FROM result_table AS n";
#SuppressWarnings("unchecked")
public List<Long> getLocationAndAllChildren(Long parentId) {
final Query query = em.createNativeQuery(SQL_FIND_LOCATION_AND_CHILDREN_IDS);
query.setParameter("parentId", parentId);
return query.getResultList();
}
Then I can just take the long value of the BigInteger since I am sure the values fit in Long's size.
#SuppressWarnings("unchecked")
public List<Long> getLocationAndAllChildren(Long parentId) {
final Query query = em.createNativeQuery(SQL_FIND_LOCATION_AND_CHILDREN_IDS);
query.setParameter("parentId", parentId);
final List<BigInteger> resultList = query.getResultList();
final List<Long> result = new ArrayList<Long>();
for (BigInteger bigIntId : resultList) {
result.add(bigIntId.longValue());
}
return result;
}
Thanks to all for replying and sorry for wasting your time.

Categories