I was wondering if there is a more elegant way to do IN() queries with Spring's JDBCTemplate. Currently I do something like that:
StringBuilder jobTypeInClauseBuilder = new StringBuilder();
for(int i = 0; i < jobTypes.length; i++) {
Type jobType = jobTypes[i];
if(i != 0) {
jobTypeInClauseBuilder.append(',');
}
jobTypeInClauseBuilder.append(jobType.convert());
}
Which is quite painful since if I have nine lines just for building the clause for the IN() query. I would like to have something like the parameter substitution of prepared statements
You want a parameter source:
Set<Integer> ids = ...;
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("ids", ids);
List<Foo> foo = getJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",
parameters, getRowMapper());
This only works if getJdbcTemplate() returns an instance of type NamedParameterJdbcTemplate
I do the "in clause" query with spring jdbc like this:
String sql = "SELECT bg.goodsid FROM beiker_goods bg WHERE bg.goodsid IN (:goodsid)";
List ids = Arrays.asList(new Integer[]{12496,12497,12498,12499});
Map<String, List> paramMap = Collections.singletonMap("goodsid", ids);
NamedParameterJdbcTemplate template =
new NamedParameterJdbcTemplate(getJdbcTemplate().getDataSource());
List<Long> list = template.queryForList(sql, paramMap, Long.class);
If you get an exception for : Invalid column type
Please use getNamedParameterJdbcTemplate() instead of getJdbcTemplate()
List<Foo> foo = getNamedParameterJdbcTemplate().query("SELECT * FROM foo WHERE a IN (:ids)",parameters,
getRowMapper());
Note that the second two arguments are swapped around.
Refer to here
write query with named parameter, use simple ListPreparedStatementSetter with all parameters in sequence. Just add below snippet to convert the query in traditional form based to available parameters,
ParsedSql parsedSql = NamedParameterUtils.parseSqlStatement(namedSql);
List<Integer> parameters = new ArrayList<Integer>();
for (A a : paramBeans)
parameters.add(a.getId());
MapSqlParameterSource parameterSource = new MapSqlParameterSource();
parameterSource.addValue("placeholder1", parameters);
// create SQL with ?'s
String sql = NamedParameterUtils.substituteNamedParameters(parsedSql, parameterSource);
return sql;
Many things changed since 2009, but I can only find answers saying you need to use NamedParametersJDBCTemplate.
For me it works if I just do a
db.query(sql, new MyRowMapper(), StringUtils.join(listeParamsForInClause, ","));
using SimpleJDBCTemplate or JDBCTemplate
Related
I'm trying to query with JdbcTemplate using a list to match:
List foos = jdbcTemplate.query("select * from foo where name IN (?)",
new Object[] { Arrays.asList("foo1", "foo2", "foo3")},
new FooMapper()
);
Result: the database columns are not matched, even though the name column equal the string foo1. Why?
If possible with JdbcTemplate without named parameters.
Set<String> names = ...;
MapSqlParameterSource fooParams = new MapSqlParameterSource();
fooParams.addValue("names", names);
List<Foo> foo = getJdbcTemplate().query("SELECT * FROM foo WHERE name IN (:names)",
fooParams, getRowMapper());
I am using Java and SQLBuilder from http://openhms.sourceforge.net/sqlbuilder/ and am trying to build SQL SELECT query dynamicly:
SelectQuery sql = new SelectQuery();
sql.addAllColumns().addCustomFromTable("table1");
sql.addCondition(BinaryCondition.like("column1", "A"));
However, it creates string like this:
SELECT * FROM table1 WHERE ('column1' LIKE 'A')
Because of wrong quotes ('column1') it doesn't work properly. I suppose it expects some Column object in .like() method.
Is there any way to create query with proper quotes?
I've found a solution. I had to create new class Column that extends CustomSql and pass my column name as parameter:
public class Column extends CustomSql {
public Column(String str) {
super(str);
}
}
And then:
SelectQuery sql = new SelectQuery();
sql.addAllColumns().addCustomFromTable("table1");
sql.addCondition(BinaryCondition.like(new Column("column1"), "A"));
Or without creating own class:
SelectQuery sql = new SelectQuery();
sql.addAllColumns().addCustomFromTable("table1");
sql.addCondition(BinaryCondition.like(new CustomSql("column1"), "A"));
It creates following SQL query, which works fine:
SELECT * FROM table1 WHERE (column1 LIKE 'A')
BinaryCondition.like() takes Object which is a Column Object and then it is converted to SqlObject using Converter.toColumnSqlObject(Object) internally . There is a method named findColumn(String columnName) and findSchema(String tableName) in Class DbTable and Class DbSchemarespectively where you can pass a simple String Object. Try this it would solve your problem:
DbTable table1= schema.findSchema("table1");
DbColumn column1 = table1.findColumn("column1");
SelectQuery sql = new SelectQuery();
sql.addAllColumns().addCustomFromTable(table1);
sql.addCondition(BinaryCondition.like(column1, "A"));
Please, check the working example and refactor your own query
String query3 =
new SelectQuery()
.addCustomColumns(
custNameCol,
FunctionCall.sum().addColumnParams(orderTotalCol))
.addJoins(SelectQuery.JoinType.INNER, custOrderJoin)
.addCondition(BinaryCondition.like(custNameCol, "%bob%"))
.addCondition(BinaryCondition.greaterThan(
orderDateCol,
JdbcEscape.date(new Date(108, 0, 1)), true))
.addGroupings(custNameCol)
.addHaving(BinaryCondition.greaterThan(
FunctionCall.sum().addColumnParams(orderTotalCol),
100, false))
.validate().toString();
Look at this library JDSQL (It requires Java 8):
JQuery jquery = new JQuery();
Collection<Map<String, Object>> result = jquery.select("tbl1::column1", "tbl2::column2") //Select column list
.from("Table1" , "TB1") // Specifiy main table entry, and you can add alias
.join("Table2::tb2") // Provide your join table, and another way to provide alias name
.on("tbl1.key1", "tbl2.key1") // your on statement will be based on the passed 2 values equaliy
.join("Table3", "tbl3", true) // Join another table with a flag to enable/disable the join (Lazy Joining)
.on("tbl2.key2", "tbl3.key1", (st-> {st.and("tbl3.condition = true"); return st;}))
.where("tbl1.condition", true, "!=") // Start your where statment and it also support enable/disable flags
.and("tbl2.condition = true", (st-> {st.or("tbl.cond2", 9000, "="); return st;})) // And statment that is grouping an or inside parentheses to group conditions
.and("tbl3.cond3=5", false) // And statment with a flag to enable/disable the condition
.get((String sql, Map<String, Object> parameters)-> getData(sql, parameters)); // Passing the hybrid getter.
//You can also assign the getter at the jqueryobject itself by calling setGetter.
}
private static Collection<Map<String, Object>> getData(String sql, Map<String, Object> parameters){
return null;
}
}
I have configured a Datagrid Cache in apache ignite. the query fields are configured using CacheTypeMetada.
But I cannot retrieve the values from the Cache using sql.
If I change the query fields configured to #QueryIndexType (index = true)
The query returns result.
My question is, is there any config I am missing to do sql query lookup into Cache configured using CacheTypeMetadata ?
Thankyou.
Providing my code snippet.
CacheConfiguration<TestKey, Test> testCacheCfg = new CacheConfiguration<>(TEST_CACHE);
The query firlds are configured using CacheTypeMetadata.
private static Collection<CacheTypeMetadata> testCacheMetadata(){
Collection<CacheTypeMetadata> types = new ArrayList<>();
CacheTypeMetadata type = new CacheTypeMetadata();
type.setKeyType(TestKey.class.getName());
type.setValueType(Test.class.getName());
Map<String, Class<?>> qryFlds = type.getQueryFields();
qryFlds.put("testId", int.class);
qryFlds.put("orgId", String.class);
qryFlds.put("md5", String.class);
Map<String, Class<?>> ascFlds = type.getAscendingFields();
ascFlds.put("testId", int.class);
ascFlds.put("orgId", String.class);
types.add(type);
return types;
}
Query called :
private static void sqlQuery(Ignite ignite, TestKey testKey) {
IgniteCache<TestKey, Test> cache = Ignition.ignite().cache(TEST_CACHE);
// SQL clause
String sql = "where testId = ? and orgId = ?";
// Execute query
System.out.println("query result" +
cache.query(new SqlQuery<TestKey, Test>(Test.class, sql).
setArgs(testKey.getTestId(), testKey.getOrgId())).getAll());
}
Your example works fine for me. Can you take a look at my code and see if there is any difference from yours (see link below)?
https://github.com/vkulichenko/ignite-tests/blob/master/src/main/java/org/vk/ignite/query/metadata1/Main.java
What version are you on?
From poking around in PreparedStatement, it appears that parameterizing SQL statements only allows the developer to specify positional arguments using ? and PreparedStatement.setX(index, value):
PreparedStatement statement = connection.prepareStatement("select * from something where id = ?;");
statement.setString(1, '5');
Is there a way to supply named parameters to prepared statements like this:
ParameterizedStatement statement = connection.parameterizeStatement(
"select * from something where id = $id;");
statement.setString("id", "5");
Does something exist for this in Java?
Not in the base JDK, but this sounds like the MyBatis SQL Builder Class.
For example,
// Anonymous inner class
public String deletePersonSql() {
return new SQL() {{
DELETE_FROM("USER");
WHERE("ID = ${id}");
}}.toString();
}
// Builder / Fluent style
public String insertPersonSql() {
String sql = new SQL()
.INSERT_INTO("USER");
.VALUES("ID, FIRST_NAME", "${id}, ${firstName}")
.VALUES("LAST_NAME", "${lastName}")
.toString();
return sql;
}
JPA queries can use named parameters, example:
EntityManager em = ...
Query q = em.createQuery("SELECT x FROM Magazine x WHERE x.title = :titleParam and x.price > :priceParam");
q.setParameter("titleParam", "JDJ");
q.setParameter("priceParam", 5.0);
List<Magazine> results = (List<Magazine>) q.getResultList();
To avoid SQL injection attacks in my project, I'm attempting access database with Parameterized Query way. Right now I know how to handle equal case like below (With Spring JdbcTemplate):
String sql = "SELECT * FROM T_USER WHERE USERNAME = ? AND PASSWORD = ?"
jdbcTemplate.query(sql,
new UserRowMapper(),
new Object[]{"%admin%", "%password%"});
Above code runs no problem, but I had no idea how to handle the 'IN' case, following is my case, and it works failed:
String sql =
"SELECT * FROM T_USER WHERE USERNAME = ? AND PASSWORD = ? AND CLASS_ID IN (?)"
jdbcTemplate.query(sql,
new UserRowMapper(),
new Object[]{"%admin%", "%password%", "1,2,3"});
Anybody give me guidance? Thanks a lot.
I think you can create a List and pass it as 3rd parameter. Also You need to use LIKE in place of = in first two column filters.
List<Integer> classIds = new ArrayList<Integer>();
classIds.add(1);
classIds.add(2);
classIds.add(3);
String sql = "SELECT * FROM T_USER WHERE "+
"USERNAME LIKE ? AND PASSWORD LIKE ? AND CLASS_ID IN (?)";
jdbcTemplate.query(sql, new Object[]{"%admin%", "%password%", classIds},
new UserRowMapper());
Please note: Here is the syntax:
public List query(String sql, Object[] args, RowMapper rowMapper)
throws DataAccessException
EDIT: Please try namedParameterJdbcTemplate as bwlow:
String sql = "SELECT * FROM T_USER WHERE "+
"USERNAME LIKE :uname AND PASSWORD LIKE :passwd AND CLASS_ID IN (:ids)";
Map<String, Object> namedParameters = new HashMap<String, Object>();
namedParameters.put("uname", "%admin%);
namedParameters.put("passwd", "%password%");
namedParameters.put("ids", classIds);
List result = namedParameterJdbcTemplate.query(sql, namedParameters,
new UserRowMapper());
Three options:
Generate different JDBC queries for each length of the IN LIST, and parameterize each INDIVIDUAL item, e.g. this answer
For small tables, you can cheat and use a LIKE statement, e.g. this answer
Use a SPLIT function (anti-LISTAGG) to turn the delimited list into individual rows of one column each, and JOIN against it. Example SPLIT function
You'll parameterize the argument to the function as a single string