Spring's Stored Procedure, parsing the result Map - java

I've got a setup with Spring & Hibernate over MySQL.
I have a MySQL Stored-Procedure I would like to call.
The procedure takes 2 float arguments, and returns a result-set with 3 fields. Integer,Integer,Float.
I created a class which extends spring's StoredProcedure.
This is the execute function:
public Map execute(float longitude, float latiude) {
Map inparams = new HashMap(2);
inparams.put("longitude", (float) longitude);
inparams.put("latitude", (float) latiude);
Map out = execute(inparams);
The problem is that I don't know how to parse the map result.
When I'm debugging, I see that all the result-set is in there, but It arranged in a strange way, and I don't know how to extract the fields.
The best I can do to show you how it looks, is to give you the toString() of out (Map)
Here it is:
{#result-set-1=[{id=4, out1=100, distance=40.9}, {id=5, out1=100,
distance=47.7}, {id=6, out1=100, distance=22.3}, {id=7, out1=100,
distance=27.4}, {id=8, out1=100, distance=22.1}, {id=9, out1=100,
distance=18.3}, {id=10, out1=100, distance=20.1}, {id=11, out1=100,
distance=28.6}, {id=12, out1=100, distance=23.1}], #update-count-1=0}

I'd look in a debugger to see what the types are; IntelliJ could tell me this easily.
It looks like a Map<String, Object> to me. The keys are "#result-set-1" and "#update-count-1".
The value for the first key is a List of Maps, one Map per row returned. The keys are the column names and the values are the returned values.
The value for the second key is an Integer; it's zero because you did a SELECT.
So, in the interest of spelling it out, here's how to extract your results (sorry for the initial coding error):
// Foo is some unknown object that encapsulates each row.
List<Foo> results = new ArrayList<Foo>();
Map<String, Object> rows = (Map<String, Object>) out.get("#result-set-1");
for (Map row : rows) {
int id = row.get("id");
int out1 = row.get("out1");
double distance = row.get("distance");
results.add(new Foo(id, out1, distance));
}
return results;

Since you are using Spring you can use the Jackson ObjectMapper.
It's more simple than the first answer.
class Foo {
private int id;
private int out1;
private double distance;
// getters and setters
}
ObjectMapper objectMapper = new ObjectMapper();
List<Foo> list = objectMapper.convertValue(execute.get("#result-set-1"), new TypeReference<List<Foo>>() {});
I hope that helps even if this question is open for a long time :)

Related

How to use Sum SQL with Spring Data MongoDB?

I want to sum the values of a column price.value where the column validatedAt is between startDate and endDate.
So I want a BigInteger as a result (the sum value).
This is what I tried:
final List<AggregationOperation> aggregationOperations = new ArrayList<>();
aggregationOperations.add(Aggregation.match(where("validatedAt").gte(startDate).lt(endDate)));
aggregationOperations.add(Aggregation.group().sum("price.value").as("total"));
final Aggregation turnoverAggregation = Aggregation.newAggregation(OrderEntity.class, aggregationOperations);
return this.mongoOperations.aggregate(turnoverAggregation, OrderEntity.class, BigInteger.class).getUniqueMappedResult();
This doesn't work. I have this error:
{"exception":"com.application.CommonClient$Builder$6","path":"/api/stats/dashboard","message":"Failed to instantiate java.math.BigInteger using constructor NO_CONSTRUCTOR with arguments ","error":"Internal Server Error","timestamp":1493209463953,"status":500}
Any help?
You don`t need to add a new pojo for just for this. It is helpful when you more fields to map and you want spring to map them automatically.
The correct way to fix the problem is to use BasicDBObject because MongoDB stores all values as key value pairs.
return this.mongoOperations.aggregate(turnoverAggregation, OrderEntity.class, BasicDBObject.class).getUniqueMappedResult().getInt("total");
Sidenote: You should use Double/BigDecimal for monetary values.
I resolved this problem by creating a class that has a BigInteger attribute:
private class Total {
private int total;
}
return this.mongoOperations.aggregate(turnoverAggregation, OrderEntity.class, Total.class).getUniqueMappedResult();

Which Map class should I use for data consisting of different types?

It seems that HashMap is limited to only one value, and I need a table of values like:
Joe(string) 25(Integer) 2.0(Double)
Steve(string) 41(Integer) 1.6(Double)
etc.
I want to store infomation similarly as in two-dimensional array, but I want it to have different variable types. I've look at various Map-implementing classes, but it seems that they only store value (assigned to a key), or two variables (I need at least three). What class should I use for this?
It sounds like you should be creating a separate class with a String field, an int field and a double field.
Then you can create a map with that as the value type, and whatever type you like as a key. For example:
Map<String, Person> map = new HashMap<>();
// What keys do you really want here?
map.put("foo", new Person("Joe", 25, 2.0));
map.put("bar", new Person("Steve", 41, 1.6));
Or it's possible that you don't even need a map at all at that point:
List<Person> list = new ArrayList<>();
list.add(new Person("Joe", 25, 2.0));
list.add(new Person("Steve", 41, 1.6));
Make class representing data you want to store, eg.
class Person {
String name;
//rest
}
and then make map like Map. Type of map is irrelevant
I would suggest that you create a simple class that stores the integer and double pair, which is then mapped to a String (I assume this is the desired outcome).
HashMap<String, Pair<Integer, Double>> map = new HashMap<String, Pair<Integer, Double>>;
map.put("Steve", new Pair<Integer, Double>(41, 1.6));
Where Pair is defined as
class Pair<T, K> {
public T val1;
public K val2;
public Pair(T val1, K val2){
this.val1 = val1;
this.val2 = val2;
}
}
There are a number of ways to do this.
The best way is the way that is suggested by Jon Skeet and #novy1234. Create a custom class that represents a person (or whatever the rows of the table are). Then use either a Map or a List of that class to represent the "table". (The Map allows you to select one of the fields / columns as a key ... if that is appropriate.)
So you might end up with a HashMap<String, Person> or an ArrayList<Person> ... where Person is your custom class.
A second way would be to represent each row as a Map<String,Object> so that (for example) "name" maps to "Joe", "age" maps to 25 and "height" maps to 2.0. (He is tall.) Then the table could be either a Map or a List of those maps.
A variation of the second way would be a Map<String, Map<String, Object>> where the keys of the outer map are each person's name, the keys of the inner map are the field names; e.g. "age" and "height".
However using a Map<String, Object> to represent a row is not a good Java solution when the set of columns is known. A custom class will use significantly less space than a Map (of any flavour), and a regular getter method is orders of magnitude faster that a Map.get(key) method. In addition, the Map.get(...) method is going to return you an Object that has to be cast to the expected type before it can be used. There is a risk that the typecast will fail at runtime, because you have (somehow) populated the row / map incorrectly.
You should only contemplate using a Map to represent a row in the table if the columns are not known at compile time, or if there are an unmanageably number of columns that are populated sparsely. (Neither is the case here ...)
So, which Map class should you use?
Your alternatives include HashMap, TreeMap, LinkedHashMap and ConcurrentHashMap. Each one has different properties and different target use-cases. However, if your table is small, and in the absence of specific requirements, it probably makes no real difference.
Make a node to store both the integer and double values?

dynamic arrays with more that one column

I need to store different types of DATA inside one same "array" associated to a key (or an ID) in my android application, and I'm wondering if there's a way to create HashMaps (or equivalent dynamic arrays) that have more than one column of content. Like this for example.
HashMap<Integer, String, LatLng, Marker> myHashMap = new HashMap<Integer, String, LatLng, Marker>();
Thanks in advance for any idea.
private class Row {
public Integer i;
public String s;
public LatLng ll;
public Row(Integer i, String s, LatLng ll) {
this.i = i;
this.s = s;
this.ll = ll;
}
}
List<Row> rows = new ArrayList<Row>();
This is semi pseudo code but I think you get the idea
Adding new Rows to the list can be done with
rows.add(new Row(new Integer(1), "a string", new LatLng(51.448495, 5.470877));
Editing a row can be done with
rowIndex = 3;
Row row = rows.get(rowIndex);
row.i = 2; //give a new value
//etc
Edit: replaced pseude code with real code
map is just a key and value so HashMap makes very little sense
Java does not let you define data structured this way. A collection of type Class A should have hold only instance of Class A or its sub type.
EDIT: From the data you have provided seems like you want to process each row from a database table. Most appropriate data structure for it is to have a class with fields that correspond to a database table.
When you fetch a row from the a result set just create an instance of this class.
Class mysqlTuple
{
public Integer id;
public String message;
public LatLng latitude;
public Marker longitude;
}
mysqlTupe [] mysqlTable = new mysqlTupe [];
It depends a lot on what you are trying to achieve
One option is to keep 3 different maps:
Map<String, Integer>
Map<String,Marker>
Map<String,LatLng>
Another option is to make a:
Map<String,Object>
However you will have to 'instanceof' to check the real type, which is not a best practice.

Map with multi values

I basically want 2 values on 1 map if possible or something equivalent. I want to store this info
Map<K,V1,V2> sample = new HasMap<K,V1,V2>
(Key - caller) = 26
(value 1 - callee) = 55
(value 2 - seconds) = 550
sample(26,55,550)
the only other way i see how i can do this is
Map(k, arraylist(v2))
having the position in the arraylist as V1 but this will take forever to search if i would want to find what callers have called a specific callee.
i have also read this HashMap with multiple values under the same key but i do not understand how to do this.
Create a bean for your value like below
class Value {
VariableType val1;
VariableType val2;
...
}
Then we can create a map like below
Map<K,Value> valueSample = new HashMap<K,Value>();
valueSample .put(new K(), new Value());
We need to set the value in Value calss by setter or constructor
One solution is to create a wrapper object (it was given in the discussion link that you have in your question) to hold values v1 and v2:
class ValueWrapper {
V1Type v1;
V2Type v2;
...
}
Now you can map your keys to the instances of this wrapper:
Map<K,ValueWrapper> sample = new HashMap<K,ValueWrapper>();
sample.put(new K(), new ValueWrapper());
If all values are <V1, V2> you can use Entry as value:
Map<K,Map.Entry<V1,V2>> sample = new HasMap<K,Map.Entry<V1,V2>>();
Create Entry<V1,V2> and put it with the relevant key.
Another solution (and even better one) is to create your own value class.
You can do like this.
Map<Integer,List<Integer>> map = new HashMap<Integer,List<Integer>>();
List<Integer> list =new ArrayList<Integer>();
list.add(55);
list.add(550);
//Adding value to the map
map.put(26, list);
//getting value from map
List<Integer> values = map.get(26);

How to get Map data using JDBCTemplate.queryForMap

How to load data from JDBCTemplate.queryForMap() and it returns the Map Interface.How the maintained the query data internally in map.I trying to load but i got below exception i.e., org.springframework.dao.IncorrectResultSizeDataAccessException: Incorrect result
Code:-
public List getUserInfoByAlll() {
List profilelist=new ArrayList();
Map m=new HashMap();
m=this.jdbctemplate.queryForMap("SELECT userid,username FROM USER");
Set s=m.keySet();
Iterator it=s.iterator();
while(it.hasNext()){
String its=(String)it.next();
Object ob=(Object)m.get(its);
log.info("UserDAOImpl::getUserListSize()"+ob);
}
return profilelist;
}
Plz help me
queryForMap is appropriate if you want to get a single row. You are selecting without a where clause, so you probably want to queryForList. The error is probably indicative of the fact that queryForMap wants one row, but you query is retrieving many rows.
Check out the docs. There is a queryForList that takes just sql; the return type is a
List<Map<String,Object>>.
So once you have the results, you can do what you are doing. I would do something like
List results = template.queryForList(sql);
for (Map m : results){
m.get('userid');
m.get('username');
}
I'll let you fill in the details, but I would not iterate over keys in this case. I like to explicit about what I am expecting.
If you have a User object, and you actually want to load User instances, you can use the queryForList that takes sql and a class type
queryForList(String sql, Class<T> elementType)
(wow Spring has changed a lot since I left Javaland.)
I know this is really old, but this is the simplest way to query for Map.
Simply implement the ResultSetExtractor interface to define what type you want to return. Below is an example of how to use this. You'll be mapping it manually, but for a simple map, it should be straightforward.
jdbcTemplate.query("select string1,string2 from table where x=1", new ResultSetExtractor<Map>(){
#Override
public Map extractData(ResultSet rs) throws SQLException,DataAccessException {
HashMap<String,String> mapRet= new HashMap<String,String>();
while(rs.next()){
mapRet.put(rs.getString("string1"),rs.getString("string2"));
}
return mapRet;
}
});
This will give you a return type of Map that has multiple rows (however many your query returned) and not a list of Maps. You can view the ResultSetExtractor docs here: http://docs.spring.io/spring-framework/docs/2.5.6/api/org/springframework/jdbc/core/ResultSetExtractor.html
To add to #BrianBeech's answer, this is even more trimmed down in java 8:
jdbcTemplate.query("select string1,string2 from table where x=1", (ResultSet rs) -> {
HashMap<String,String> results = new HashMap<>();
while (rs.next()) {
results.put(rs.getString("string1"), rs.getString("string2"));
}
return results;
});
You can do something like this.
List<Map<String, Object>> mapList = jdbctemplate.queryForList(query));
return mapList.stream().collect(Collectors.toMap(k -> (Long) k.get("userid"), k -> (String) k.get("username")));
Output:
{
1: "abc",
2: "def",
3: "ghi"
}

Categories