Opposing class cast exceptions in Java - java

I have an issue that is making me a little nuts. Here is the Java method.
public List<FtpActiveMerchantDTO> getFtpActiveMerchants() {
String sql = "select m.merchantId, ma.merchantAcctId, m.domain, f.fetchUrl, ma.acctActive, " +
"f.fieldDelimiter, f.feedType " +
"from merchant_account ma " +
"join merchant_ftp_account f on f.merchantAcctId = ma.merchantAcctId " +
"join merchant m on m.merchantAcctId = ma.merchantAcctId " +
"where f.fetchUrl is not null and ma.acctActive = 1";
Query query = currentSession().createSQLQuery(sql);
List<FtpActiveMerchantDTO> ftpActiveMerchantDTOList = new ArrayList<FtpActiveMerchantDTO>();
int merchantId, merchantAcctId;
byte acctActive;
for (Object rowObject : query.list()) {
Object[] row = (Object []) rowObject;
merchantId = ((BigDecimal) row[0]).intValue();
merchantAcctId = ((BigDecimal) row[1]).intValue();
acctActive = ((BigDecimal) row[4]).byteValue();
ftpActiveMerchantDTOList.add(new FtpActiveMerchantDTOBuilder().withMerchantId(merchantId)
.withMerchantAcctId(merchantAcctId).withDomain((String) row[2])
.withFetchUrl((String) row[3]).withAcctActive(acctActive > 0)
.withFieldDelimiter(row[5].toString()).withFeedType((String) row[6]).build());
}
return ftpActiveMerchantDTOList;
}
When I run my service with the code as it is shown here, I get
$ curl -X GET http://localhost:8080/merchants/ftpActive
{"responseData":null,"errorData":[{"code":500,"detailMessage":"","message":"java.lang.Byte cannot be cast to java.math.BigDecimal"}],"debugData":null}
The error is occurring at the line where acctActive is assigned. When I fix that line to this:
acctActive = (Byte) row[4];
then the service works as expected. But then my integration test (run from within IntelliJ)
private void whenFetchingFtpActiveMerchants() {
openAndBindSession();
ftpActiveMerchantDTOList = merchantDAO.getFtpActiveMerchants();
flushAndCloseSession();
}
fails, with this error:
java.lang.ClassCastException: java.math.BigDecimal cannot be cast to java.lang.Byte
at com.pronto.mpds.dal.MerchantDAOImpl.getFtpActiveMerchants(MerchantDAOImpl.java:143)
at com.pronto.mpds.dal.MerchantDAOIT.whenFetchingFtpActiveMerchants(MerchantDAOIT.java:96)
at com.pronto.mpds.dal.MerchantDAOIT.testFtpActiveMerchants(MerchantDAOIT.java:44)
...
The field in the db table is a tinyint(4). Why would the results from the db query be "expecting" to be a BigDecimal? Is there some kind of default data type? I know I am not configuring one anywhere.

At a first glance it looks like the database schema for the integration test is different from the production one, thus the type mismatch.

Related

Generic DAO search with group by / unqiue

I am using hibernate-generic-dao for a searching function. Since I only need to show one of the records if they have same value on a column field. But I am not sure how to achieve this by the search / filter functions.
package com.googlecode.genericdao.search;
PersonContact domain object:
...
#Column(name = "group_key", length = 20)
public String getGroupKey() {
return groupKey;
}
#Formula(value = "(SELECT status from person_contact m " +
" WHERE m.case = case AND m.movement_id = movement_id )")
public String getActiveRecord() {
return activeRecord;
}
...
Search search = new Search();
search.addFilterNotNull("groupKey"); //groupKey is the field I want to use "group by / unqiue" with it
search.addFilterEqual("type","C");
search.addFilterCustom("{activeRecord} != 'I' ");
search.setMaxResults(limit);//for paging
search.setFirstResult(startIdx);
SearchResult<PersonContact> resultObj = PersonContactDAO.searchAndCount(search);
You should probably ask this question by opening an issue in the repository for that project here: https://github.com/vincentruan/hibernate-generic-dao
It seems though as if the project is abandoned, so unless you feel like digging into the details, you should probably try to get away from it.

Search by the combination of multiple parameters via Native Query

I'm currently working on a fetaure that will allow the system to search public services receipts by the combination of 6 parameters which can be null meaning that receipts shouldn't be filtered by this parameter: accountNumber, amountRangeMin, amountRangeMax, dateRangeMin, dateRangeMax, publicServiceId. However making a method for each combination of the parameters is not an option, I'm thinking that there must be a better way, at first my approach was as following:
On my Service I have this method:
public Map<String,Object> findPublicServiceReceiptsByParams(Integer accountNumber, BigDecimal amountRangeMin,
BigDecimal amountRangeMax, LocalDate dateRangeMin, LocalDate dateRangeMax, Integer publicServiceId) {
Map<String,Object> publicServiceReceipts = new HashMap<String,Object>();
String accountNumberFilter = !(accountNumber==null) ? accountNumber.toString() : "AccountNumberTableName";
String amountRangeMinFilter = !(amountRangeMin==null) ? amountRangeMin.toString() : "table.AmountColumnName";
String amountRangeMaxFilter = !(amountRangeMax==null) ? amountRangeMax.toString() : "table.AmountColumnName";
String dateRangeMinFilter = !(dateRangeMin==null) ? dateRangeMin.toString() : "Table.ReceiptCreationDateColumn";
String dateRangeMaxFilter = !(dateRangeMax==null) ? dateRangeMax.toString() : "Table.ReceiptCreationDateColumn";
String publicServiceIdFilter = !(publicServiceId==null) ? publicServiceId.toString() : "table.publicServiceIdColumn";
publicServiceReceipts = publicServiceReceiptRepository.findPublicServiceReceiptsByParams(accountNumberFilter,
amountRangeMinFilter, amountRangeMaxFilter, dateRangeMinFilter, dateRangeMaxFilter,
publicServiceIdFilter);
return publicServiceReceipts;
}
And then in my repository I had:
final static String FIND_PUBLIC_SERVICES_BY_ARGS = "Select (Insert whatever logic should go in here to select columns from receipts the where clause is the one that matters)"
+ " WHERE ACT.ACT_AccountNumber=:accountNumberFilter\n"
+ " AND PSE.PSE_Id=:publicServiceIdFilter\n"
+ " AND PSR.PSR_CreateDate BETWEEN :dateRangeMinFilter AND :dateRangeMaxFilter\n"
+ " AND PSR.PSR_Amount BETWEEN :amountRangeMinFilter AND :amountRangeMaxFilter\n"
+ " order by PSR.PSR_CreateDate desc";
#Query(nativeQuery = true, value = FIND_PUBLIC_SERVICES_BY_ARGS)
Map<String, Object> findPublicServiceReceiptsByParams(#Param("accountNumberFilter") String accountNumberFilter,
#Param("amountRangeMinFilter") String amountRangeMinFilter,
#Param("amountRangeMaxFilter") String amountRangeMaxFilter,
#Param("dateRangeMinFilter") String dateRangeMinFilter,
#Param("dateRangeMaxFilter") String dateRangeMaxFilter,
#Param("publicServiceIdFilter") String publicServiceIdFilter);
}
My reasoning was that if a parameter was null meant that whoever consumed the Web Service is not interested in that paramater so if that happens I set that variable as the Column Name so that it wouldn't affect in the WHERE clause and in theory make it simpler, but what I found was that It would send the names as Strings so it wouldn't be recognized as an sql statement which was the flaw in my thinking and as I said there must be another way other than writing each method for each combination, I appreciate any help :).
You should use the Criteria API, which was designed for creating dynamic queries. Named queries aren't really meant to be used in this case.
With it you can do something like this:
#PersistenceContext
EntityManager em;
List<YourEntity> method(String argument) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<YourEntity> cq = cb.createQuery(YourEntity.class);
Root<YourEntity> root = cq.from(YourEntity.class);
List<Predicate> predicates = new ArrayList<>();
if (argument == null) {
predicates.add(cb.equal(root.get("yourAttribute"), argument);
}
// rest of your logic goes here
cq.where(predicates.toArray(new Predicate[]{}));
return em.createQuery(cq).getResultList();
}
I found a way to fix this, I did it like this (I'm going to show only the native Query since it's the inly thing that i changed):
DECLARE #actNum varchar(50),#crdNum varchar(50),#pseId varchar(50),#dateMin varchar(50),#dateMax varchar(50),#amountMin varchar(50),#amountMax varchar(50)
SET #actNum = :actNum
SET #crdNum = :crdNum
SET #pseId = :pseId
SET #dateMin = :dateMin
SET #dateMax = :dateMax
SET #amountMin = :amountMin
SET #amountMax = :amountMax
--Whatever Select with joins statement
WHERE ACT.ACT_AccountNumber = CASE WHEN #actNum = 'N/A'
THEN ACT.ACT_AccountNumber
ELSE #actNum END
AND CRD_CardNumber = CASE WHEN #crdNum = 'N/A'
THEN CRD_CardNumber
ELSE #crdNum END
AND PSE.PSE_Id= CASE WHEN #pseId = 'N/A'
THEN PSE.PSE_Id
ELSE #pseId END
AND PSR.PSR_CreateDate >= CASE WHEN #dateMin = 'N/A'
THEN PSR.PSR_CreateDate
ELSE #dateMin END
AND PSR.PSR_CreateDate <= CASE WHEN #dateMax = 'N/A'
THEN PSR.PSR_CreateDate
ELSE #dateMax END
AND PSR.PSR_Amount BETWEEN CASE WHEN #amountMin = 'N/A'
THEN PSR.PSR_Amount
ELSE #amountMin END
AND CASE WHEN #amountMax = 'N/A'
THEN PSR.PSR_Amount
ELSE #amountMax END
ORDER BY PSR.PSR_CreateDate DESC
The backend will send the parameters as either "N/A" (if it shouldn't be used to filter data) or the actual value, this worked fine for me!

Dynamic Variable Initialization Error in Java

I have this piece of code which is giving an error saying
variable priorityCheck is not initialized
The output that I require is that the query executes with the given value of priorityCheck in a loop. Here is the code:
String query;
String StrComp;
int PerFound;
Statement stmt = null;
ResultSet rs = null;
String priorityCheck;
// running the queries
for (int i = 0; i < names.size(); i++) {
StrComp = types.get(i).toString();
PerFound = StrComp.indexOf("%");
if (StrComp.indexOf("P1") != -1) {
priorityCheck =
"a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P1 & a.P2 = 1";
} else if (StrComp.indexOf("P2") != -1) {
priorityCheck =
"a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P3 & a.P4 = 1";
} else if (StrComp.indexOf("WO") != -1) {
priorityCheck = "a.SubscriptionType=2";
}
if (PerFound == -1) {
query =
"SELECT DATEDIFF(minute,a.LastStatusDate,GETUTCDATE()) FROM SASubscriptionPIIView a,SAWorkflowToolPIIView b WHERE (a.toolid=b.id and a.active=1 and a.SubscriptionCategory=0 and "
+ priorityCheck
+ " and b.toolname like "
+ types.get(i)
+ ")";
writeLog(query);
} else {
query =
"SELECT DATEDIFF(minute,a.LastStatusDate,GETUTCDATE()) FROM SASubscriptionPIIView a,SAWorkflowToolPIIView b WHERE (a.toolid=b.id and a.active=1 and a.SubscriptionCategory=0 and "
+ priorityCheck
+ " and b.toolname like "
+ types.get(i)
+ ")";
writeLog(query);
}
}
This error basically means: you are using (reading!) a variable in your code, but there are paths in your code that do not initialize your variable.
Simplified:
String foo;
if (whatever) {
foo = "from if";
}
bar = foo;
That is what your code is doing: it contains a way to reach a "read" for priorityCheck without a previous "write" to that variable. One simple fix:
String foo = "not initialized";
In other words: think what the variable should contain as "default". Or make sure that there are no paths without assigning a value.
Beyond that: your real problem is that you are writing overly complicated code. If you really intend to write "database code" at this point; consider reading a bit about "clean coding practices"; for example the Single Layer of Abstraction principle. Your problem is that you are writing code that is so complex that you can't see any more what the code is doing.
Please initialize variable priorityCheck with some default value - "", for example.
The problem is that in code
if (StrComp.indexOf("P1")!=-1)
{
priorityCheck="a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P1 & a.P2 = 1";
}
else if (StrComp.indexOf("P2")!=-1)
{
priorityCheck="a.SubscriptionType=0 and a.applyticketpriorityfilterflag = 1 and a.P3 & a.P4 = 1";
}
else if (StrComp.indexOf("WO")!=-1)
{
priorityCheck="a.SubscriptionType=2";
}
variable priorityCheck can be not initialized - else statement is missing.

java.lang.ClassCastException: java.util.Vector cannot be cast to My Class

im using a for with a query to obtain some rows and finally add all of these rows into one Arraylist of one class. But i receive the following error:
java.lang.ClassCastException: java.util.Vector cannot be cast to com.dominion.procop.agr.util.AGRSalvaguardasInforme
at (pathofmyclass).mostrarInformeActivosAGR(AGRInformes.java:1130)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
the lines are these (The marked with $ is the AGRInformes.java:1130) :
for (int i = 0; i < InformeAmenazasAGR.size()-1; i++) {
String Amenaza = InformeAmenazasAGR.get(i).toString();
Amenaza = Amenaza.substring(1,3);
List<AGRSalvaguardasInforme> resultadoQuery = (List<AGRSalvaguardasInforme>) manager.SalvaguardaPorAmenaza(Amenaza);
if(!resultadoQuery.isEmpty() ){
$ for (AGRSalvaguardasInforme salvaguardaExtraida : resultadoQuery) {
InformeSalvaguardasAGR.add(salvaguardaExtraida);
}
}
}
And this is the manager.SalvaguardaPorAmenaza() method (Query and return the objects):
public List<AGRSalvaguardasInforme> SalvaguardaPorAmenaza(String idAmenaza) {
// SALVAGUARDAS POR ID_AMENAZA
int dimension = 0;
String consulta = "";
if(dimension!=0){
consulta = "SELECT COALESCE(MFT.ID_AMENAZA, MFA.ID_AMENAZA) as ID_AMENAZA, SAL.ID_SALVAGUARDA, SAL.DENOMINACION, SAL.DESCRIPCION, SAL.EFICACIA FROM AGR_SALVAGUARDAS SAL LEFT JOIN AGR_MIT_FREC_TIPO MFT "
+ " ON SAL.ID_SALVAGUARDA = MFT.ID_SALVAGUARDA AND MFT.ID_AMENAZA = "+idAmenaza+" "
+ " LEFT JOIN AGR_MIT_FREC_ACT MFA "
+ " ON SAL.ID_SALVAGUARDA = MFA.ID_SALVAGUARDA AND MFA.ID_AMENAZA = "+idAmenaza+" "
+ " WHERE MFT.ID_SALVAGUARDA IS NOT NULL OR MFA.ID_SALVAGUARDA IS NOT NULL GROUP BY SAL.ID_SALVAGUARDA, SAL.DENOMINACION, SAL.DESCRIPCION, SAL.EFICACIA, MFT.ID_AMENAZA, MFA.ID_AMENAZA";
}else{
consulta = "SELECT COALESCE(MFT.ID_AMENAZA, MFA.ID_AMENAZA) as ID_AMENAZA, SAL.ID_SALVAGUARDA, SAL.DENOMINACION, SAL.DESCRIPCION, SAL.EFICACIA FROM AGR_SALVAGUARDAS SAL LEFT JOIN AGR_MIT_FREC_TIPO MFT "
+ " ON SAL.ID_SALVAGUARDA = MFT.ID_SALVAGUARDA AND MFT.ID_AMENAZA = "+idAmenaza+" "
+ " LEFT JOIN AGR_MIT_FREC_ACT MFA "
+ " ON SAL.ID_SALVAGUARDA = MFA.ID_SALVAGUARDA AND MFA.ID_AMENAZA = "+idAmenaza+" "
+ " WHERE MFT.ID_SALVAGUARDA IS NOT NULL OR MFA.ID_SALVAGUARDA IS NOT NULL GROUP BY SAL.ID_SALVAGUARDA, SAL.DENOMINACION, SAL.DESCRIPCION, SAL.EFICACIA, MFT.ID_AMENAZA, MFA.ID_AMENAZA";
}
Query q = dao.createNativeQuery(consulta);
List<AGRSalvaguardasInforme> resultado = q.getResultList();
return resultado;
}
Why appears this error and how i can manage correctly?.
Thank you in advance.
Problem 1
Naming is mess, please follow java naming convention.
Problem 2
for (int i = 0; i < InformeAmenazasAGR.size()-1; i++) {
in this way, the last element of List:InformeAmenazasAGR would never be read.
Problem 3
for (int i = 0; i < InformeAmenazasAGR.size()-1; i++) {
...
InformeSalvaguardasAGR.add(salvaguardaExtraida);
...
}
You are adding element to a list within the for looping. It will lead to unpredictable consequence. If you need do that, use Iterator.
Problem 4
If you used "NativeQuery" (I guess jpa?) .getResultList(), you got a List<Object[]>, You cannot expect the query to give you a List<YourClass>. You have to go into the result, and do the work by yourself.
To me it looks like
Query q = dao.createNativeQuery(consulta);
List<AGRSalvaguardasInforme> resultado = q.getResultList();
it is not really returning a List of AGRSalvaguardasInforme but a List of Vector. Is Query class yours? You can use debugger to see what is the real type of the elements inside the "resultado" list at runtime.
Note: Generic information is lost at runtime due erasure. So there is no runtime check of the types of the elements contained in the collection. The issue is when you start iterating it and expecting each element to be a concrete type they are not.
This:
for (AGRSalvaguardasInforme salvaguardaExtraida : resultadoQuery) {
InformeSalvaguardasAGR.add(salvaguardaExtraida);
}
Is just a code sugar for :
for (Iterator it : resultadoQuery.iterator(); it.hasNext();) {
AGRSalvaguardasInforme salvaguardaExtraida = (AGRSalvaguardasInforme) it.next();
InformeSalvaguardasAGR.add(salvaguardaExtraida);
}
So the cast is implicitly there (and that cast is the one throwing ClassCastException at runtime for the reasons I mentioned above)

native sql into an unmapped object

I'm working on modifying an existing application and I've decided to work with these 2 things.
My unmapped object is a simple object that consists of 2 integer properties:
public class EmployeeScore {
private int id;
private int score;
}
and I have a DAO which does the following:
public List<EmployeeScore> findEmployeeTotals(int regionId, int periodId) {
DataVerify.greaterThan(regionId, 0, "Invalid Region id: Region Id cannot be zero");
DataVerify.lessThan(regionId, 4, "Invalid Region id: Region id cannot be greater than 3");
List<EmployeeScore> results = (List<EmployeeScore>) currentSession().createSQLQuery(
"select n.EMP_ID, SUM(DISTINCT(nom.TOTAL_POINT)) from" +
" NOMINEE n join NOMINATION nom on nom.NOM_ID = n.NOM_ID" +
" join EMPLOYEE e on n.EMP_ID = e.EMP_ID" +
" join COMPANY c on c.COMPANY_CODE = e.COMPANY_CODE" +
" join REGION r on r.REGION_ID = c.REGION_ID" +
" where nom.PERIOD_ID = :periodId" +
" AND nom.STATUS_ID = 2" +
" AND e.ISACTIVE = 1" +
" AND nom.CATEGORY_CODE != 'H'" +
" AND r.REGION_ID = :regionId" +
" group by n.EMP_ID")
.setParameter("regionId", regionId)
.setParameter("periodId", periodId)
.list();
return results;
}
It's a huge query i know. I'm having problems on my tests and I assume because I'm not understanding how to apply these 2 correctly.
My test goes as follows:
#Test
#Transactional(isolation = Isolation.SERIALIZABLE)
public void testEmpScore() {
NomPeriod period = nomPeriodHibernateDAO.findById(6);
Region region = regionHibernateDAO.findById(1);
List<EmployeeScore> results = winnerHibernateDAO.findEmployeeTotals(region.getId(), period.getId());
results.toString();
Assert.assertEquals(13, results.size());
}
It should return 13 objects type EmployeeScore but instead it returns 0 so the test fails.
Can you point me in the right direction of what I'm doing wrong? I know it has to be something with my object seeing as it is not mapped but I have no way of mapping the score value or the id value since they reference different tables or aggregates.
Thanks.
The problem is that you are querying for two integers and trying to interpret them as EmployeeScores. Hibernate can do it but it will take a bit more work than that.
Assuming EmployeeScore has a constructor that takes two integers, you can try
select new my.package.EmployeeScore(n.EMP_ID, SUM(DISTINCT(nom.TOTAL_POINT))) ...
You need to give it the full package path to your object.
Alternatively, by default, I think the query will return a List<Object[]>. So you could iterate through these and form your employee scores manually.
List<Object[]> results = query.list();
List<EmployeeScore> scores = new LinkedList<EmployeeScore>();
for (Object[] arr : results)
{
int id = (int) arr[0];
int total = (int) arr[1];
scores.add(new EmployeeScore(id, total));
}

Categories

Resources