How do I bind a list of tuples in jdbi? - java

I have a mysql query in this format
SELECT * from xyz where (key1, key2) in (('val1', 'val2'), ('val3', 'val4'));
I'm using jdbi to make this query. How do I bind the list of tuples in jdbi ?
I was trying to use something like this
List<String[]> query = new ArrayList<>();
for(String key: vars.keySet()){
String[] entry = {key, vars.get(key)};
query.add(entry);
}
List<String> result = getBasicQuery() + " WHERE (key, val) in (<query>)".bindList("query", query);
Getting this error on using bind this way
No argument factory registered for '[Ljava.lang.String;#11fa461a' of type class [Ljava.lang.String;

It's actually quite easy to do this in JDBI3, but for some reason the solution isn't documented on jdbi.org.
Here it is:
List<String> result = handle.createQuery(getBasicQuery() + " WHERE (key, val) in (<query>)")
.bindMethodsList("query", query, List.of("getKey", "getValue"))
.mapTo(String.class)
.list();
Note that to use bindMethodsList() you'll need to have your query objects defined as instances of a class with public methods (ie. getters). Something like this would work just fine:
class Query {
String key;
String value;
Query(String key, String value) {
this.key = key;
this.value = value;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
}
Alternatively, you can skip the getters by making the properties themselves public and using bindBeanList().

As #talon55 said, bindBeanList method is an alternative:
String QRY_STRING = "SELECT id FROM settlement_report WHERE (user_id, reference_id, has_type_id, record_type) IN (<conditions>)";
List<String> fieldsBean = Arrays.asList("userId", "referenceId", "hasTypeId", "recordType");
handle.createQuery(QRY_STRING)
.bindBeanList("conditions", records, fieldsBean);

Related

How to map Integer List from result

I am using java 7 and jdbc template to query a integer array from postgresql.My code is as below:
#Autowired
private JdbcTemplate jdbcTemp;
String SQL = "select item_list from public.items where item_id=1";
List<Integer> ListOfitems=jdbcTemp.queryForList(SQL , Integer.class);
My item_list column is integer[] in postgresql.But when I try like thid it throws an error as Bad value for type int psql exception.
Any help is appreciated
You can use java.sql.Array.
If you want to get only integer array you can try like this
String SQL = "select item_list from public.items where item_id=1";
Array l = template.queryForObject(SQL, Array.class);
List<Integer> list = Arrays.asList((Integer[]) l.getArray());
Or use RowMapper
Foo foo = template.queryForObject(SQL, new RowMapper<Foo>(){
#Override
public Foo mapRow(ResultSet rs, int rowNum) throws SQLException {
Foo foo = new Foo();
foo.setName(rs.getString("name"));
foo.setIntegers(Arrays.asList((Integer[]) rs.getArray("item_list").getArray()));
return foo;
}
});
Class Foo:
class Foo {
private String name;
private List<Integer> integers;
public String getName() {
return name;
}
// ...
}
queryForList saves each row that you got as a result from your query as an element of a List. It doesn't take the list that is saved into a column and returns it for you.
From the documentation : "The results will be mapped to a List (one entry for each row) of result objects, each of them matching the specified element type."
At best you will get a List<List<Integer>> returned, but I'm not sure fi you can use List<Interger>.class as a parameter for the call.

Java - Keeping 2 keys for one value (TreeMap)

I was trying to create a composite key of Type and Name both return String
public String getObjectName() {
return objectName;
}
public String getObjectType() {
return objectType;
}
and would like to store it in Treemap
both objectName and objectType needs to be a single key and the second String i.e value need to be a user specified Value of Type String
what are your suggestions on this?
This is where I'm gonna store the TreeMap
public static void setDomainDocumentationMap(Map<String, String> domainDocumentationMap) {
MMTUtil.domainDocumentationMap = domainDocumentationMap;
One way is to concatenate as shown in other answers. Then you can use them as key, and in value, put whatever user input is.
This leads to Map<String, String> type.
Another way is to create a class for key:
class Key{
private String objectName;
private String objectType;
//TODO write setters here
public String getObjectName(){
return objectName;
}
public String getObjectType(){
return objectType;
}
public String toString(){
return objectName + ":separator:" + objectType;
}
//TODO implement hashcode and equals method
}
Now your map should be: Map<Key, String> and I believe this should be more flexible.
You can concatenate the two prospective keys with a string like "##-#-#-##" or any other string which you are sure won't be there in your data, and make this concatenated string as the 'key' of your map.
In future if you need to retrieve the two key string from the 'key' of the map, just split the map's key using the "##-#-#-##" string.
As TreeMap is ultimately a map only, It will have same structure i.e key and value.
Only thing that is different is :A tree map guarantees that its elements will be sorted in an ascending key order. You want two Strings as composite key then You can Add any separator between these two strings and then add it as a key.
For example if we use # as separator it would be string1#string2 as key and against it there will be it's value stored.
Code Example :
public class Test{
private static final String SEPERATOR ="$$" ;
private String getCompositekey(String key1, String key2)
{
return key1+SEPERATOR+key2;
}
public static void main(String[] args){
Map<String,Object> t = new TreeMap<String,Object>();
Test test = new Test();
t.put(test.getCompositekey(test.getObjectName(),test.getObjectType()),VALUEGOESHERE);
}
}

Loading from csv and storing in HashMap with key containing multiple values

I have a hashmap that is storing values from a csv file. The file consists of three columns: TEMPLATE_NAME, PARAM_1, PARAM_2
I've been trying to use TEMPLATE_NAME as the key and PARM_1, PARAM_2 as the values for each row. The problem is that there may be many TEMPLATE_NAME with a different mix of params. My requirement specifies that a list of TEMPLATE_NAMES should be displayed when a PARAM_1 and PARAM_2 is selected.
I know that a hashmap cannot have duplicate keys so the hashmap is only creating one TEMPLATE_NAME key with values but ignoring the duplicates. How do I resolve this?
private void load() throws IOException{
CsvReader reader = new CsvReader();
List<List<String>> rows = reader.parse(csvFile);
for (int i = 1 ; i<rows.size() ; i++){
List<String> columns = rows.get(i);
String templateName = columns.get(TEMPLATE_NAME);
OnConfig config = entries.get(templateName);
if (config == null){
config = new OnConfig(templateName);
entries.put(templateName, config);
}
config.put(columns.get(PARAM_1), columns.get(PARAM_2));
}
}
public class OnConfig {
private final String templateName;
private final HashMap<String, String> attributes = new HashMap<>();
public OnConfig(String templateName){
this.templateName= templateName;
}
public void put(String param1, String param2){
attributes.put(param1, param2);
}
public String get(String param1){
return attributes.get(param1);
}
public String getTemplateName() {
return templateName;
}
#Override
public String toString() {
return String.format("Template Name: %s, Number of Attributes: %s", getTemplateName(), attributes.value());
}
}
The problem is that there may be many TEMPLATE_NAME with a different mix of params.
Create a new class representing TEMPLATE_NAMES and containing a List or Map of PARM_1, PARAM_2
Use a nested data structure. For instance, Nest a Map inside a Map: the outer keys are the TEMPLATE_NAMES, and inner key is PARAM_1

Java Collection with index, key and value

My question might sound silly, but would like to know whether there are any Collection object in Java that does store index,key and values in a single Collection object?
I have the following:
Enumeration hs = request.getParameterNames();
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<String, String>();
while (hs.hasMoreElements()) {
linkedHashMap.put(value, request.getParameter(value));
}
The above stores key and value in linkedHashMap, but it doesn't have an index. If it has then I could call by index(pos) and get corresponding key and value.
Edit 1
I would want to conditionally check if index(position) is x then get the corresponding key and value pair and construct a string with query.
As mentioned by others, Java collections does not support this. A workaround is Map<R, Map<C, V>>. But it is too ugly to use.
You can go with Guava. It provides a Table collection type. It has the following format Table<R, C, V>. I haven't tried this but I think this will work for you.
Enumeration hs = request.getParameterNames();
Table<Integer, String, String> table = HashBasedTable.create();
while (hs.hasMoreElements()) {
table.put(index, value, request.getParameter(value));
}
Now, if you want key, value pair at, let's say, index 1. Just do table.row(1). Similarly, to get index, value pairs just do table.column(value).
No Collection in java, will support this.
You need to create a new class IndexedMap inheriting HashMap and store the key object into the
arraylist by overriding put method.
here is the answer(answerd by another user: Adriaan Koster)
how to get the one entry from hashmap without iterating
Maybe you need implementing yourself for achieving this functionality.
public class Param{
private String key;
private String value;
public Param(String key, String value){
this.key = key;
this.value = value;
}
public void setKey(String key){
this.key = key;
}
public String getKey(){
return this.key;
}
public void setValue(String value){
this.value = value;
}
public String getValue(){
return this.value;
}
}
Enumeration hs = request.getParameterNames();
List<Param> list = new ArrayList<Param>();
while (hs.hasMoreElements()) {
String key = hs.nextElement();
list.add(new Param(key, request.getParameter(key)));
}
By doing this, you could get param with an index provided by List API.

spring-data query method with a list as arguments

We have to query data from database where we need to find entities matching a list of key value pairs. We thought it would be a nice idea to use Spring Data JPA as we need also pagination.
The tables we created are like below:
terminal(ID,NUMBER,NAME);
terminal_properties(ID,KEY,VALUE,TERMINAL_FK);
Is it possible to define a query method to fetch all terminals with properties containing given key/value pairs ?
Something like this: List<Terminal> findByPropertiesKeyAndValue(List<Property>);
I didn't execute the code, but given the correct import statements, this at least compiles. Depending on your entity definition, some properties may need to be adapted and in any case, you should get an idea of how to approach this.
My criteria query is based on the following SQL:
SELECT * FROM TERMINAL
WHERE ID IN (
SELECT TERMINAL_FK FROM TERMINAL_PROPERTIES
WHERE (KEY = 'key1' AND VALUE = 'value1')
OR (KEY = 'key2' AND VALUE = 'value2')
...
GROUP BY TERMINAL_FK
HAVING COUNT(*) = 42
)
Where you list each name/value pair and 42 simply represents the number of name/value pairs.
So I assume you defined a repository like this:
public interface TerminalRepository extends CrudRepository<Terminal, Long>, JpaSpecificationExecutor {
}
It's important to extend JpaSpecificationExecutor in order to make use of the criteria API.
Then you can build a criteria query like this:
public class TerminalService {
private static Specification<Terminal> hasProperties(final Map<String, String> properties) {
return new Specification<Terminal>() {
#Override
public Predicate toPredicate(Root<Terminal> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
// SELECT TERMINAL_FK FROM TERMINAL_PROPERTIES
Subquery<TerminalProperty> subQuery = query.subquery(TerminalProperty.class);
Root propertyRoot = subQuery.from(TerminalProperty.class);
subQuery.select(propertyRoot.get("terminal.id"));
Predicate whereClause = null;
for (Map.Entry<String, String> entry : properties.entrySet()) {
// (KEY = 'key1' AND VALUE = 'value1')
Predicate predicate = builder.and(builder.equal(propertyRoot.get("key"),
entry.getKey()), builder.equal(propertyRoot.get("value"), entry.getValue()));
if (whereClause == null) {
whereClause = predicate;
} else {
// (...) OR (...)
whereClause = builder.or(whereClause, predicate);
}
}
subQuery.where(whereClause);
// GROUP BY TERMINAL_FK
subQuery.groupBy(propertyRoot.get("terminal.id"));
// HAVING COUNT(*) = 42
subQuery.having(builder.equal(builder.count(propertyRoot), properties.size()));
// WHERE ID IN (...)
return query.where(builder.in(root.get("id")).value(subQuery)).getRestriction();
}
};
}
#Autowired
private TerminalRepository terminalRepository;
public Iterable<Terminal> findTerminalsWith(Map<String, String> properties) {
// this works only because our repository implements JpaSpecificationExecutor
return terminalRepository.findAll(hasProperties(properties));
}
}
You can obviously replace Map<String, String> with Iterable<TerminalProperty>, although that would feel odd because they seem to be bound to a specific Terminal.

Categories

Resources