JDBC RowMapper and Casts - java

I have a problem using RowMappers with JDBC (especially ParameterizedSingleColumnRowMapper) :
I am querying a list of existing ids from a database with the following :
List<Long> existingIds = DS.getJdbcTemplate().query(sql,
new ParameterizedSingleColumnRowMapper<Long>());
The only problem is that my list sometimes does not contains long as expected :
// for some value...
System.out.println(existingIds.get(0) instanceof Long); // return FALSE
System.out.println((Object)existingIds.get(0) instanceof Integer); // return TRUE
I could just go through existingIds and recast the values to long but I was expecting the row mapper to do that (I guess ParameterizedSingleColumnRowMapper is using getLong or something like that and usually it tries to cast to the desired value).
Do you have any explanation or ideas to solve that ?
Thanks in advance for your help.

You need to do ParameterizedSingleColumnRowMapper.newInstance(Long.class). Just creating a new instance directly means it doesn't know the type correctly (it can't infer it from the generics because they get erased at compile time) so it probably just does .getObject() which will be at the mercy of the JDBC driver.

Related

jOOQ Postgres PERCENTILE_CONT & MEDIAN Issue with Type Casting

Coercion of data types does not seem to work within median() or percentileCont(). Data type coercion works just fine with other aggregate functions like max() and min(). The Postgres queries that are produced as a result show that type casting is not applied in the final result. Below are the snippets from jOOQ and Postgres for reference. As of now, I have no work-around or knowledge of an open ticket for this issue.
Any direction would be much appreciated!
MEDIAN
jOOQ Snippet
selectFields.add(
median(
field(String.format("%s.%s", a.getDataSourceName(), a.getField()))
.coerce(Double.class)) // Seems to not successfully coerce data types
.as(
String.format(
"%s.%s.%s", a.getDataSourceName(), a.getField(), "median")));
SQL Output
select
tableA.columnA,
percentile_cont(0.5) within group (order by tableA.columnA) as "tableA.columnA.median"
from tableA
group by tableA.columnA
limit 100;
ERROR: function percentile_cont(numeric, text) does not exist
PERCENTILE_CONT
jOOQ Snippet
selectFields.add(
percentileCont(a.getPercentileValue())
.withinGroupOrderBy(
field(String.format("%s.%s", a.getDataSourceName(), a.getField()))
.coerce(Double.class)) // Seems to not successfully coerce data types
.as(
String.format(
"%s.%s.%s", a.getDataSourceName(), a.getField(), "percentile_" + Math.round(a.getPercentileValue() * 100))));
SQL Output
select
tableA.columnA,
percentile_cont(0.0) within group (order by tableA.columnA) as "tableA.columnA.percentile_0"
from tableA.columnA
group by tableA.columnA
limit 100;
ERROR: function percentile_cont(numeric, text) does not exist
POSTGRES -- This works due to type casting
select
percentile_cont(0.5)
within group (
order by tableA.columnA::INTEGER
)
as "tableA.columnA.median"
from tableA.columnA
group by (select 1)
https://www.jooq.org/javadoc/latest/org.jooq/module-summary.html
You're not looking for coercion, which in jOOQ-speak means changing a data type only in the client without letting the server know. This is mostly useful when fetching data of some type (e.g. Integer) despite jOOQ producing some other data type (e.g. BigInteger), otherwise. See the Javadoc on Field.coerce()
Unlike with casting, coercing doesn't affect the way the database sees a Field's type.
// This binds an int value to a JDBC PreparedStatement
DSL.val(1).coerce(String.class);
// This binds an int value to a JDBC PreparedStatement
// and casts it to VARCHAR in SQL
DSL.val(1).cast(String.class);
Cleary, you want to Field.cast(), instead, just like in your example where you actually used a cast tableA.columnA::INTEGER.

CrudRepository - Stored Procedure call is not working because of type/number of argument issue

I have JPA entity as
And then Repository as
Now, when I run it then I get following exception:
And stored procedure is:
It is running against Oracle database. Can someone please help me understand even though I have correct parameter numbers and type, still why I am getting this exception.
Please note: I do not have local environment so I cannot put a sample code, and please do not worry about class/method name, I tried to camouflage them so they may be inconsistent.
There is one more question that suppose I have 2 OUT parameters then how you I create my entity class, with one output parameter I know I can return String (or appropriate return type) but in case of 2 OUT parameters I do no know how to do it? I have read this article but it is only with 1 OUT parameter, and I couldn't find any article or post which explains for 2 OUT parameter. If someone has a code with 2 OUT parameter then it would be helpful.
Please try:
use the exact (db) names of procedure parameters:
#StoredProcedureParameter(name = "tbl_name" ...
#StoredProcedureParameter(name = "p_date" ...
#StoredProcedureParameter(name = "p_message" ...
or (alternatively) omit names completely (rely on position).
From StoredProcedureParameter javadoc:
The name of the parameter as defined by the stored procedure in the database. If a name is not specified, it is assumed that the stored procedure uses positional parameters.
Currently you can't have multiple OUT-parameters using spring-data, but (should be) no problem with standard JPA:
StoredProcedureQuery spq = em.createNamedStoredProcedureQuery("my_proc");
proc.setParameter("p_in", 1);
proc.execute();
Integer res1 = (Integer) proc.getOutputParameterValue("out1");
Integer res2 = (Integer) proc.getOutputParameterValue("out2");
see also: Spring Data JPA NamedStoredProcedureQuery Multiple Out Parameters

DynamoDb Query/Scan Expression generic "FilterExpression"

I am trying to query DynamoDB using mapper.query() function with FilterExpression.
I need to generate a generic function where I will just tell the attributes operator and value, and it will return the generic string. But I see, it is not completely possible.
new DynamoDBQueryExpression<T>()
.withFilterExpression("att = string_val")
This is possible but if I have an Integer filter, then I have to do like this :
new DynamoDBQueryExpression<T>()
.withFilterExpression("att = :filter")
.withValueMap(":filter", new AttributeValue().withN(String.valueOf(12)));
Can this be avoided ? I wish to put everything in the filter string.. Can dynamoDB itself recognise int/string attributes ?
Thanks in advance!

hibernate, check if object exists and null values

I am using hibernate to save records (i.e. objects) to a database. Before saving my objects, I want to verify if the database already contains this object. (The primary key is just an incremental key and cannot be used for this.)
I am creating a HQL statement at runtime to check the existance of a record with these attributes (i.e. column1-3).
The resulting query should look like:
from myTable where column1 is null and column2 = :column2 and column3 = :column3'
Because sometimes the columns can contain null values, I check the value of the attributes, if it is a NULL value, then I use a is instead of a = in this query (e.g. the column1 is :column1 in the above statement).
Because I start to realize that I am doing a lot of work to achieve something reletively crucial, I am starting to wonder if I'm on the right track. Is there an easier way to check the existance of objects ?
EDIT: I slightly rephrased my question after I realized that also column1 is :column1 does not work when :column1 parameter is set to null. Apparently the only syntax that seems to work as expected is column1 is null. So, it seems like you just cannot use wildcards when searching for null values. But that does not change the main aspect of my question: should I really be checking all this stuff at runtime ?
This is the best way that I found so far.
I prefer to put my filters in a map. The key refers to the property (i.e. map.put("column1", Integer.valueOf(1))).
There is a Restritions.eqOrIsNull method that simplifies the conversion to a Criterion object. The following method converts an entire Map to a List<Criterion>.
public List<Criterion> mapToCriterion(Map<String, Object> params)
{
if (params == null) return null;
// convert the parameter map to a list of criterion
List<Criterion> criterionList = new ArrayList<>(params.size());
for (Map.Entry<String, Object> entry : params.entrySet())
criterionList.add(Restrictions.eqOrIsNull(entry.getKey(), entry.getValue()));
return criterionList;
}
Later on, I use the List<Criterion> to build a Criteria object.
Criteria criteria = session.createCriteria(clazz);
if (criterionList != null)
{
for(Criterion criterion : criterionList)
criteria.add(criterion);
}
// get the full list
#SuppressWarnings("unchecked")
List<T> objectList = criteria.list();
My general impression is still that there are missing several convenience methods here (e.g. Criteria#addAll(List<Criterion>) would have been nice).

ClassCastException on converting List to Object<class>?

I am trying to retrieve the values from database and storing it in List,
The data's are retrieved and working properly, But when i convert the List into Object<pojo class> i am getting an Exception ,
My Code is
Query qry=session.createQuery("select personaldetails.fname,personaldetails.lname from Personaldetails as personaldetails where refId=1001");
List<Personaldetails> l=(List<Personaldetails>)qry.list();
session.getTransaction().commit();
session.close();
System.out.println("--->"+l.size()); //List 'l' holds the values from DB
Personaldetails p; //This is an pojo class
p=(Personaldetails)l.get(i); //Here i am getting the exception here
System.out.println("Person name "+p.getFname());
In above mentioned line i got Exception as ClassCastException , i don't know why, i tried it shows no error while compiling.
Any suggestion will be appreciated....
I see that you are doing selective query - that is select fname, lname
select personaldetails.fname,personaldetails.lname from Personaldetails as personaldetails where refId=1001
which returns List<Object[]> where each elements in array represents the 2 column values
List will be something like [{"Fname1", "LName1"}, {"Fname2", "Lname2"}]
So ClassCastException is due to the fact that you are converting Object[] into PersonDetails.
To expect List<PersonDetails> as the result, you can use query like
select from Personaldetails as personaldetails where refId=1001
Or you can iterate through the List<Object[]> and construct PersonDetails yourself
for(Object[] arr : l) {
PersonDetails p = new PersonDetails();
p.setFName(arr[0]);
p.setLName(arr[1]);
}
I prefer the first approach
In above mentioned line i got Exception as ClassCastException , i
don't know why, i tried it shows no error while compiling.
When you explicitly cast, the compiler cannot help you. Type casting is your way to tell the compiler that you know this type is some other type. The compiler trusts that you know what you're doing.
The real question here is how does the list get created?
Query qry=session.createQuery("select personaldetails.fname,personaldetails.lname from Personaldetails as personaldetails where refId=1001");
List<Personaldetails> l=(List<Personaldetails>)qry.list();
How do you know that qry.list() returns a List? You just ran a SQL statement and jumped to a List. Since the cast to List works fine, clearly a List is in fact returned (though due to type erasure, we don't know if it in fact only holds Personaldetails objects).
It must return a List. #sanbhat seems to have pointed you to the right structure for that method. I don't have any experience with Hibernate.

Categories

Resources