NamedParameterJdbcTemplate not updating when using batchUpdate - java

I have a list of objects provided by another service which I use to update my own data. When I try to use NamedParameterJdbcTemplate.batchUpdate, all returned values are zero.
public void updateWeather(List<Weather> weatherList) {
String query = "UPDATE weather \n" +
"SET rain_probability = ROUND(:rainProbability, 4), \n" +
"wind_speed = :windSpeed \n" +
"WHERE city_id = :cityId AND date = :date;";
List<MapSqlParameterSource> batchList = new ArrayList<>();
for(Weather weather : weatherList) {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("rainProbability", weather.getRainProbability());
params.addValue("windSpeed", weather.getWindSpeed());
params.addValue("cityId", weather.getCityId());
params.addValue("date", weather.getDate());
batchList.add(params);
}
this.namedParameterJdbcParameter
.batchUpdate(query, batchList.toArray(new MapSqlParameterSource[] {});
}
If I run this UPDATE directly in the database, it works fine. Futhermore, if I run it one by one, that is, replacing values (instead of adding the parameter source to batchList) it also works.
For example:
for (Weather weather : weatherList) {
String query = String.format("UPDATE weather \n" +
"SET rain_probability = ROUND('%d', 4), \n" +
" wind_speed = %d \n" +
" WHERE city_id = :cityId AND date = :date;",
weather.getRainProbability(),
weather.getWindSpeed(),
weather.getCityId(),
weather.getDate()
);
this.namedParameterJdbcTemplate.update(query, Collections.emptyMap());
}
Any suggestions of what I'm doing wrong?

Is it the use of "\n" or the ";" at the end of the statement within the String? (I'm surprised you don't get a SQL Syntax exception with the ; inside the actual query string)
Also dates are always a bit tricky and if that isn't converting properly then your WHERE clause isn't going to match and is possibly why 0 rows are returned. Could you temporarily try converting dates to Strings and see if the count is correct (e.g. for Oracle: AND date = TO_DATE(:dateStr, 'DD/MM/YYYY') )

Related

Get PostgreSql table size on disk (byte) in ORMlite

I use ORMLite in a Java application, in order to deal with a PostgreSql DataBase.
I want to get the space on the disc used by a table of DataBase.
It seems that OrmLite doesn't have a specific method to get it, so I tried without success:
final String TABLE_NAME = "a_table_name_of_db";
//1)
String SQL = "SELECT pg_relation_size('" + TABLE_NAME + "');" ;
int result = OGGETTO_DAO.executeRaw(SQL);
//2)
String SQL = "SELECT pg_table_size('" + TABLE_NAME + "');" ;
int result = OGGETTO_DAO.executeRaw(SQL);
//3 - it was just a try...)
SQL = "SELECT pg_table_size('" + TABLE_NAME + "');" ;
GenericRawResults<String> ARRAY = OGGETTO_DAO.queryRaw(SQL);
String result= ARRAY.getFirstResult();
With 1) and 2) I get always -1, with 3) I get a cast exception;
I I use the command 'pg_relation_size' or 'pg_table_size' by command line (linux - by psql prompt), it works properly.
What am I wrong?
Thank you
UPDATE - WORKING SOLUTION:
Now it works! Solution, as per accepted answer below, is:
final String TABLE_NAME = "a_table_name_of_db";
String SQL = "SELECT pg_table_size('" + TABLE_NAME + "');"
final long RESULT = OGGETTO_DAO.queryRawValue(SQL); //in bytes
int result = OGGETTO_DAO.executeRaw(SQL);
Yeah that's not right. Looking at the javadocs for executeRaw(...) they say that it returns the number of rows affected not the result.
SQL = "SELECT pg_table_size('" + TABLE_NAME + "');" ;
GenericRawResults ARRAY = OGGETTO_DAO.queryRaw(SQL);
String result= ARRAY.getFirstResult();
Looking at the javadocs for queryRaw(...), the problem here is it returns a GenericRawResults<String[]> and not <String>. It returns a collection of raw results, each row being represented by a string array. I'm really surprised that your code even compiles.
It should be:
GenericRawResults<String[]> ARRAY = OGGETTO_DAO.queryRaw(SQL);
String result= ARRAY.getFirstResult()[0];
Probably the best way to do this is to use queryRawValue(...) which performs a raw query and returns a single value.
// throws an exception if there are no results or if the first one isn't a number
long size = OGGETTO_DAO.queryRawValue(SQL);

jdbcTemplate.queryForObject returning EmptyResultDataAccessException while normal query runs

I am trying to change a normal query to Parameterized query using jdbcTemplate.queryForObject for avoiding SQL Injection. But the query returns EmptyResultDataAccessException - Incorrect result size: expected 1, actual 0 where the normal query works fine. Below is the normal query where i get the correct result.
StringBuilder builder = new StringBuilder();
String AcctNameBuilder = adhpDetailUtil.getAccName();
builder.append("select * " +
"from gfc.LSI_ELGBLTY " +
"where INSURANCE_ID = '" + request.getInsuranceId() + "' and " +
"SYS_CD = '" + request.getSystemId() + "' and " +
"ACCT_TYPE in (" + AcctNameBuilder.toString() + ")");
Here is the parameterized query that i have created from the above query.
StringBuilder builder = new StringBuilder();
String AcctNameBuilder = adhpDetailUtil.getAccName();
final String QUERY = "select * " + "from gfc.LSI_ELGBLTY " + "where INSURANCE_ID = ? and " + "SYS_CD = ? and " + "ACCT_TYPE in (?)";
Object[] params = new Object[] {
request.getInsuranceId(),request.getSystemId(),AcctNameBuilder};
String ids = jdbcTemplate.queryForObject(QUERY, params, String.class);
builder.append(ids)
In the first case, builder.append contains the exact query while in the second case jdbcTemplate.queryForObject is returning EmptyResultDataAccessException. What am I doing wrong here.
I don't believe you can just append ids for an "IN" clause like that.
The parameter for the "IN" clause is technically an Array. I ran into this a number of years ago and I don't think that this has ever been truly addressed.
If you think about it this is a fairly difficult problem as the query planner for preparing the statement cannot effectively bound the number of parameters.

how to make filter between two datebox zk

I have 2 datebox to make a filter. Their value will determine the 'from' and 'to' in my query (I'm using Oracle now), here is the code.
#Listen("onClick=#btnSaveFilter")
public void saveFilter() throws Exception {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
c_main_filter.detach();
cc.refreshFilter(""
+"[Date of Birth] between '" + formatter.format(dbDateBirthFrom.getValue()).toString() + "' "
+ "and '" + formatter.format(dbDateBirthto.getValue()).toString() + "'");
}
when both datebox has value, the query work. but when they have no value the query is giving no data.
when datebox has value, it's giving data
"[Registration Date] between '2010-09-23' and '2010-09-23' "
when datebox has no value, it's giving no data
"[Registration Date] between '' and '' "
like another filter I expect if the value is '' then all data will appear, but not :D hahaha. the condition is more than this actually, the filter has a lot of parameter one of them is this condition, and some of them use date format so there will be more condition like this.
do you know how to elegantly fix this problem, I've been thinking to use 'if' to determine the datebox has value or no then I will append the text to query text if both of them has value, but then I found another problem how I can add 'and' in query to give another condition,
let say I have 5 conditions so then
"select * from xxxx where 1stcondition and 2ndcondition and
3rdcondition and 4thcondition and 5th condition"
so when the dateboxes of the 5thcondition has no value the query will be wrong like this
"select * from xxxx where 1stcondition and 2ndcondition and
3rdcondition and 4thcondition and"
if I want to use 'if' how can I play with the 'and'? but if you have alternative it will be great cause I don't have to deal with 'if' :D
You can use String.isEmpty() to determine whether you need to put an and:
String where = "";
if (from != null && to != null) {
where += <yourDateCondition>;
}
if (<needToAddSecondCondtion>) {
if (!where.isEmpty()) {
where += " and ";
}
where += <secondCondition>;
}
// continue with other conditions
String query = "select * from xxxx where " + where;
I don't know if you use plain JDBC or ORM framework like hibernate to querying to the database, but you can try something like this :
public void saveFileter(){
StringBuilder sb = new StringBuilder();
sb.append("select * from table ");
if(dbDateBirthFrom.getValue() != null{
sb.append(determineFilterWord(sb.toString()));
sb.append("your condition ");
}
if(dbDateBirthTo.getValue() != null{
sb.append(determineFilterWord(sb.toString()));
sb.append("your condition ")
}
session.createQuery(sb.toString()); //if you using hibernate
}
private string determineFilterWord(String query){
if(query.toLowerCase().indexOf("where") != -1){
return "and ";
}else{
return "where ";
}
}

Sql2o keep returning a same set of data although query is different

I am new to using SQL2O with MySQL, but I am having a weird problem, where different queries return same results. Is SQL2O returning me cached results?
My code looks like this:
String sql = "SELECT * " +
"FROM report_A" +
"ORDER BY :order :sequence "+
"LIMIT :from, :limit";
int limit = 5;
int startIndex = (page-1)*limit;
String sequence = "DESC";
try(Connection con = sql2o.open()) {
if(order.contains("-")){
order = order.replace("-", "");
sequence= " ASC";
}
Query query= con.createQuery(sql)
.addParameter("from", startIndex)
.addParameter("limit", limit)
.addParameter("order", order)
.addParameter("sequence", sequence);
List<ReportA> result = query.executeAndFetch(ReportA.class);
con.close();
The 4 parameters always change, but the output remains the same. I have verified the queries in mysql workbench, the data is different, but SQL2O returns me the same set of data. Am I missing something?
Your query is invalid. It wont compile and throw an Sql2oException on execution.
The problem is, basically, that you can use parameters only for values, not for table names, column names or other keywords like "ASC". Changing those would change the structure of the query.
It's possible to construct queries with variable structure by good old string concatenation, i.e.
String sql = "SELECT * " +
"FROM report_A" +
"ORDER BY " + order " " + SEQUENCE +
"LIMIT :from, :limit";
and then
query(sql)
.addParameter("from", from)
.addParameter("limit", limit)
.executeAndFetch(...)

Multiple updates in one sql query

I have a table in H2 DB
Order
--------
id (key)
MarketId1
MarketId2
MarketId3
ListName1
ListName2
ListName3
From XML I'm getting list of ListOrder
public final class ListOrder
{
public long listId;
public String Name;
}
So I have 3 prepared statements
"UPDATE Order set " + ListName1 + " = ? WHERE " + MarketId1 + " = ?"
"UPDATE Order set " + ListName2 + " = ? WHERE " + MarketId2 + " = ?"
"UPDATE Order set " + ListName3 + " = ? WHERE " + MarketId3 + " = ?"
The in a method I prepare a list of PreparedStament to execute
final PreparedStatement statement1 = connection.prepareStatement(QUERY1);
final PreparedStatement statement2 = connection.prepareStatement(QUERY2);
final PreparedStatement statement3 = connection.prepareStatement(QUERY3);
for (ListOrder listOrder: listOrders)
{
statement1.setString(1, listOrder.Name);
statement1.setLong(2, listOrder.listId);
statement1.addBatch();
statement2.setString(1, listOrder.Name);
statement2.setLong(2, listName.listId);
statement2.addBatch();
statement3.setString(1, listName.Name);
statement3.setLong(2, listOrder.listId);
statement3.addBatch();
}
return new ArrayList<PreparedStatement>(){{add(statement1); add(statement2); add(statement3);}};
I'm a SQL noob. Is there any better way of doing it? I assume that MarketId 1 2 3 could be the same. ListNames could be null (there will be at least one)
UPDATE:
In code I would write something like this (prob change to HashMap)
for (ListOrder listOrder: listOrders)
{
for(Order order : orders)
{
if(order.marketID1 == listOrder.listID)
order.listName1 = listOrder.Name; //break if no dups
if(order.marketID2 == listOrder.listID)
order.listName2 = listOrder.Name;
if(order.marketID3 == listOrder.listID)
order.listName3 = listOrder.Name;
}
}
You can use update comma separated
UPDATE <TABLE>
SET COL1 = <VAL1>,
COL2= <VAL2>
WHERE <CONDITION>
Is it this what you expect as one update query?
Unless you are trying to update the same record, then there is no way to do this easily or efficiently in a single query. Otherwise, assuming this is the desired result, you could use an OR (or an AND if that is desired) statement such as:
UPDATE Order
SET ListName1=?, ListName2=?, ListName3=?
WHERE MarketId1=? OR MarketId2=? OR MarketId3=?
You might also consider updating your table to use a one:many relationship which might make your queries easier. For example:
Order
--------
id (key)
name
etc
Market_List
--------
id (key)
order_id (fk)
market
listname

Categories