Converting query parameter from String to integer - mybatis

I'm passing a HashMap
HashMap<String, String> paramsMap = new HashMap<String, String>();
paramsMap.put("jobXXX", "" + jobState.getCode());
paramsMap.put("rmi_auftrag_xxx", "rmi_auftrag_status");
to a MyBatis query:
<select id="loadRmiOrdersByTypeOrState" parameterType="map" resultMap="rmiJobMap">
<![CDATA[
SELECT rmi_auftrag_id as rmiJobId,
rmi_auftrag_typ as rmiJobType,
rmi_auftrag_status as rmiJobState,
rm_dokument_id as rmDocId
FROM lis_mgr.rmi_auftrag
WHERE #{rmi_auftrag_xxx} = #{jobXXX,javaType=String,jdbcType=NUMERIC}
]]>
</select>
The type of the jobXXX parameter is yet an integer. Is there a way to tell MyBatis to convert the integer-in-string-form into a "true" integer?

You can put your Integer as Integer into your HashMap and then run the query.
HashMap<String, Object> paramsMap = new HashMap<String, String>();
paramsMap.put("jobXXX", jobState.getCode());
paramsMap.put("rmi_auftrag_xxx", "rmi_auftrag_status");

method 1). user custom type handler
public class StringToIntTypeHandler extends BaseTypeHandler<String> {
#Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, Integer.valueOf(parameter));
}
//...
}
xml
#{jobXXX,typeHandler=xxx.StringToIntTypeHandler}
method 2). use ognl
<bind name="param" value="#java.lang.Integer#valueOf(jobXXX)" />
//...
where #{rmi_auftrag_xxx} = #{param}
or
${#java.lang.Integer#valueOf(jobXXX)}

Related

How to use #BeforeStep Job Parameters in JdbcCursorItemReader for named Query

I have the code like below
#Bean
public JdbcCursorItemReader<Map<String, Object>> itemReader() {
return new JdbcCursorItemReader<Map<String, Object>>() {
private JobParameters jobParameter;
String sql = "select EMPLOYEE_ID as empId, EMPLOYEE_NAME as empName EMPLOYEE_AGE as age from EMPLOYEE EMPLOYEE_DEPT =:empDept and EMPLOYEE_SAL > :empSal";
Map<String, Object> namedParameters = null;
#PostConstruct
public void initialize() throws Exception
{
setDataSource(dataSource);
setSql("select 1 from dual");
setRowMapper(new ColumnMapRowMapper());
}
#BeforeStep
public void retrieveExecutionContext(StepExecution stepExecution)
{
jobParameter = stepExecution.getJobParameters();
namedParameters = new HashMap<String, Object>() {
{
put("bstd", jobParameter.getString("empDept"));
put("bwtn", jobParameter.getString("empSal"));
}
};
jobParameter.getParameters().forEach((k, v) -> System.out.println("key =" + k + ", Value:" + v));
}
#Override
public void afterPropertiesSet() throws Exception {
setSql(NamedParameterUtils.substituteNamedParameters(sql, new MapSqlParameterSource(namedParameters)));
setPreparedStatementSetter(new ListPreparedStatementSetter(
Arrays.asList(NamedParameterUtils.buildValueArray(sql, namedParameters))));
setRowMapper(new ColumnMapRowMapper());
setDataSource(dataSource);
super.afterPropertiesSet();
}
};
}
Tried using calling afterPropertiesSet, but still seeing below exception
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: No value supplied for the SQL parameter 'empDept': No value registered for key 'empDept'
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:361) ~[spring-jdbc-5.3.22.jar:5.3.22]
at org.springframework.jdbc.core.namedparam.NamedParameterUtils.buildValueArray(NamedParameterUtils.java:485) ~[spring-jdbc-5.3.22.jar:5.3.22]
Requirement is dynamic query, so don't have control of the Select query and the where conditions.
Thanks in advance,
You can use a SpEL expression to inject and use job parameters in your item reader bean definition as follows:
#Bean
#StepScope
public JdbcCursorItemReader<Map<String, Object>> itemReader(#Value("#{jobParameters['empDept']}") String empDept, #Value("#{jobParameters['empSal']}") String empSal) {
JdbcCursorItemReader<Map<String, Object>> itemReader = new JdbcCursorItemReader<>();
// use parameters 'empDept' and 'empSal' in your sql query as needed
return itemReader;
}
Note that the item reader should be step-scoped for that to work. For more details, please refer to the documentation: Late Binding of Job and Step Attributes.

Play framework get array of query string parameters from request.queryString

routes.conf
GET /api/v1/jurisdictions controllers.v1.JurisdictionController.getJurisdictions()
JurisdictionController
def getJurisdictions() = Action { implicit request =>
// this is returning None
val filters = request.queryString.get("filters")
val result = jurisdictionService.getJurisdictions()
Ok(serializer.serialize(result)).as("application/json")
}
Relevant request URI:
http://localhost:9000/api/v1/jurisdictions?filter[name]=Ryan&filter[number]=333333
How can I grab this query string filter?
You have to create a custom binder this is a Java implementation but it follows the same principle:
public class AgeRange implements QueryStringBindable<AgeRange> {
public Integer from;
public Integer to;
//A simple example of the binder’s use binding the :from and :to query string parameters:
#Override
public Optional<AgeRange> bind(String key, Map<String, String[]> data) {
try{
from = new Integer(data.get("from")[0]);
to = new Integer(data.get("to")[0]);
return Optional.of(this);
} catch (Exception e){ // no parameter match return None
return Optional.empty();
}
}
#Override
public String unbind(String key) {
return new StringBuilder()
.append("from=")
.append(from)
.append("&to=")
.append(to)
.toString();
}
}
Java documentation
Scala documentation

How to return HashMap from JPA query?

I want to return a HashMap from JPA query like the below but I don't know how to fill the HashMap from this query. Actually I want to fill charts from HashMap in the frontend
public HashMap<String,String> getCount(Date start,Date end) {
HashMap<String, String> map=new HashMap<String, String>();
Query q =
em.createQuery(
"select count(i.uuid),i.username from Information i where i.entereddt between :start and :end group by i.username");
q.setParameter("start",new Timestamp(start.getTime()));
q.setParameter("end",new Timestamp(end.getTime()));
System.out.println(" query"+ q.getResultList().get(0).toString());
return map;
}
Any suggestions?
It appears that you were trying to execute a query which return types not mapped to any Java entities you have (or if they be present you never mentioned them). In this case, you want to use createNativeQuery(), which will return a List of type Object[].
Try using this version of the method:
public HashMap<String,String> getCount(Date start,Date end) {
HashMap<String, String> map=new HashMap<String, String>();
Query q = em.createNativeQuery(
"select count(i.uuid),i.username from Information i" +
"where i.entereddt between :start and :end group by i.username");
q.setParameter("start",new Timestamp(start.getTime()));
q.setParameter("end",new Timestamp(end.getTime()));
List<Object[]> list = query.getResultList();
for (Object[] result : list) {
map.put(result[0].toString(), result[1].toString());
}
return map;
}
Please refer, JPA 2.0 native query results as map
In your case in Postgres, it would be something like,
List<String> list = em.createNativeQuery("select cast(json_object_agg(count(i.uuid),i.username) as text) from schema.information i where i.entereddt between :start and :end group by i.username")
.setParameter("start",new Timestamp(start.getTime()))
.setParameter("end",new Timestamp(end.getTime()))
.getResultList();
//handle exception here, this is just sample
Map map = new ObjectMapper().readValue(list.get(0), Map.class);
Kindly note, I am just sharing my workaround with Postgres.
I know that it's an old question, but you can create an object to store info
public class UserCount {
private String username;
private Long count;
public UserCount(String user, Long count){
this.username = user;
this.count = count;
}
}
It's important to create the constructor and to pass the parameters in the correct way.
The JPQL became
select my.package.UserCount(i.username, count(i.uuid) ) from schema.information i where i.entereddt between :start and :end group by i.username
The query returns a List<UserCount> .

How can I avoid converting an empty HashMap to null in morphia?

We are using org.mongodb.morphia to convert objects BasicDBObjects before persistence. One issue encountered is that in some cases the object to convert contains an empty HashMap whose size is 0, after conversion, the HashMap is converted to null. So NullPointerException throw in later accessing. I want to ask experts for help, Is there any way to avoid this? I mean, after conversion, it's still an HashMap with size 0.
Part of the class to be converted:
public class ProjectServiceAdapterConfig {
#NotNull
private String serviceAdapterId;
#NotNull
private String projectId;
#Embedded
#Flatten
private Map<String, Map<String, String>> connections = new HashMap<>();
//...... setter and getter skipped here
}
code for conversion:
// create a mapper with default MapperOptions
private Mapper createMapper() {
return new Mapper();
}
ReplaceableItem objectToItem(final ProjectServiceAdapterConfig obj) {
final Mapper mapper = createMapper();
final MappedClass mc = mapper.getMappedClass(obj.getClass());
final Map<String, Object> map = mapper.toDBObject(obj).toMap();
}
the obj is created in other place. After some debug, I found that, the obj contains an empty Map(following data copied from IntelliJ IDEA debugger):
connections = {java.util.LinkedHashMap#8890} size = 1
[0] = {java.util.LinkedHashMap$Entry#8894}"accounts" -> size = 0
key: java.lang.String = {java.lang.String#8895}"accounts"
value: java.util.LinkedHashMap = {java.util.LinkedHashMap#8896} size = 0
and the one after converted:
[2] = {java.util.LinkedHashMap$Entry#8910}"connections" -> size = 1
key: java.lang.String = {java.lang.String#8911}"connections"
value: com.mongodb.BasicDBObject = {com.mongodb.BasicDBObject#8912} size = 1
[0] = {java.util.LinkedHashMap$Entry#8923}"accounts" -> null
key: java.lang.String = {java.lang.String#8895}"accounts"
value: = null
As you can see , it's converted to null which we try to avoid.
Thanks
Before you call morphia.mapPackage(), do this:
morphia.getMapper().getOptions().storeEmpties = true;
That should map back probably to an empty map for you.
I assume I cannot avoid it without customizing the MapOfValuesConverter. See from the source code that the empty map will be always converted to null:
#Override
public Object encode(Object value, MappedField mf) {
if (value == null)
return null
Map<Object, Object> map = (Map<Object, Object>) value;
if ((map != null) && (map.size() > 0)) {
Map mapForDb = new HashMap();
for (Map.Entry<Object, Object> entry : map.entrySet()) {
String strKey = converters.encode(entry.getKey()).toString();
mapForDb.put(strKey, converters.encode(entry.getValue()));
}
return mapForDb;
}
return null;
}
In case morphia.getMapper().getOptions().setStoreEmpties(true); doesn't work for you another solution would be to use the #PostLoad annotation to check whether you have a null collection and create an empty one if necessary.
import java.util.*;
import org.mongodb.morphia.annotations.*;
import org.bson.types.ObjectId;
#Entity
public class Model {
#Id
private ObjectId id;
private Map<String, String> map;
protected Model() {}
public Model(HashMap<String, String> map) {
super();
setMap(map);
}
public void setMap(HashMap<String, String> map) {
this.map = map;
checkForNullMap();
}
#PostLoad
private void checkForNullMap() {
if (map == null) {
map = new HashMap<String, String>();
}
}
}

MyBatis stored procedure call with optional parameters

I wonder how could I describe in mybatis xml stored procedure call with optional parameters.
For example:
DAO
public List getMethod1(Object arg1) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("arg1", arg1);
return selectList("myBatisSelect", map);
}
public List getMethod1(Object arg1, Object arg2) {
Map<String, Object> map = new HashMap<String, Object>();
map.put("arg1", arg1);
map.put("arg2", arg2);
return selectList("myBatisSelect", map);
}
XML
<select id="myBatisSelect"
parameterType="map"
resultType="MyResultObject">
CALL sql_stored_procedure
(
#arg1= #{arg1}
<!-- How to add optional parameter here??-->
)
</select>
You could use conditional logic to dynamically generate your sql.
i.e.
<select id="myBatisSelect"
parameterType="map"
resultType="MyResultObject">
CALL sql_stored_procedure
(
#arg1= #{arg1}
<if test="arg2 != null">
,#{arg2}
</if>
)
</select>
The MyBatis3 User Guide has more information.