How to implement Room database query at runtime or dynamically - java

I need to create a room database query dynamically because I have lots of fields for UPDATE in the database.
If I used like below method then it works, but I have lots of fields that's why I can't create a separate UPDATE method like this.
#Query("UPDATE PROCEDUREMODEL SET patient_in_time = :val WHERE procedure_sr_number LIKE :sr")
void updateP(String val, String sr);
Now I try below but it does not work.
In DAO class I created this
#RawQuery
ProcedureModel updateProcedure(SupportSQLiteQuery supportSQLiteQuery);
And use like this
public static void updateProcedure(Context context, String colName, String val, String id) {
String s = "UPDATE PROCEDUREMODEL SET " + colName + " = :" + val + " WHERE procedure_sr_number LIKE :" + id;
SupportSQLiteQuery supportSQLiteQuery = new SimpleSQLiteQuery(s);
Thread thread = new Thread(() -> {
DatabaseHelper.getInstance(context).getDao().updateProcedure(supportSQLiteQuery);
});
thread.start();
}
I want to run the UPDATE query at runtime in Room database.

I believe that your issue is with the SQL being passed:-
String s = "UPDATE PROCEDUREMODEL SET " + colName + " = :" + val + " WHERE procedure_sr_number LIKE :" + id;
As the value val is non-numeric, because of the :'s (which you probably don't want), likewise for the id (if not numeric) the values should be enclosed in single quotes.
So using:-
String s = "UPDATE PROCEDUREMODEL SET " + colName + " = ':" + val + "' WHERE procedure_sr_number LIKE '" + id + "'";
would work. Although you very likely don't want the : as part of the value so you very likely want:-
String s = "UPDATE PROCEDUREMODEL SET " + colName + " = '" + val + "' WHERE procedure_sr_number LIKE '" + id + "'";
Saying that it is not recommended to apply literal values via string concatenation but to utilise parameter binding. This protects against SQL injection. As such you may wish to consider the following version:-
public static void updateProcedureBetter(Context context, String colName, String val, String id) {
String s = "UPDATE PROCEDUREMODEL SET " + colName + "=? WHERE procedure_sr_number LIKE ?";
Thread thread = new Thread(() -> {
DatabaseHelper.getInstance(context).getDao().updateProcedure(new SimpleSQLiteQuery(s,new Object[]{val,id}));
});
thread.start();
}
notice the ?'s in the SQL these being replaced by the objects in the Object[], the values being properly enclosed by SQLite parameter binding.
the replacement is on a 1 by/for 1 basis (first ? replace by first object in array, 2nd ? by second object ....)
note also that the intermediate supportSQLiteQuery object has been done away with.
component names (tables, columns etc) typically cannot be bound/changed so the column name has to be concatenated.

Related

Couchbase parameterized query with numeric values

I'm trying to add numeric values to parameterized AnalyticsQuery but keep getting errors when the query runs. The java creating the query looks like this:
private ParameterizedAnalyticsQuery aggregateQuery(String userId, Long from, Long to) {
return AnalyticsQuery.parameterized(
"select d.field1,"
+ " d.field2"
+ " from data d"
+ " where d.userId = $userId"
+ " and d.timestamp between $from and $to",
JsonObject.create()
.put("userId", userId)
.put("from", from)
.put("to", to)
);
}
When the query is run the following error is returned:
<< Encountered \"from\" at column 213. ","code":24000}]
If I change the query to the following then it works and returns rows:
return AnalyticsQuery.parameterized(
"select d.field1,"
+ " d.field2"
+ " from data d"
+ " where d.userId = $userId"
+ " and d.timestamp between " + from
+ " and " + to,
JsonObject.create()
.put("userId", userId)
);
Why is there a problem when the parameters are not Strings? Is there a way to use parameterized queries with numeric values?
FROM and TO are reserved keywords in N1QL for Analytics and therefore must be put in backquotes when used as parameter names:
... and d.timestamp between $`from` and $`to`
For a list of all reserved keywords please see:
https://docs.couchbase.com/server/current/analytics/appendix_1_keywords.html

sql syntax error, but concatenated text runs fine in Datagrip

I'm trying to run a query, but I get an error when running it.
However, I'm using Intellij and when I use the copy string concatenation to clipboard feature, and run the query in the datagrip, the query runs fine (after putting in the parameters)
The error I am getting is "ERROR: syntax error at or near "tstoredarticle"\n Position: 199"
I find it a bit weird that it's showing there's a line change, after tstoredartiacle but besides that I don't see what's wrong.
What could be some issues that could cause a problem like that?
my query in intellij looks like this:
private ResultSet getHuaweiSqlQuery(Integer intBrandID, Integer intStorageID, Vector<Integer> vecArticleTypes, int iCurrencyID, int iContactSupplierID, int secondaryStorage, BigDecimal exchangeRate) throws SQLException {
return Hibernate3To4Utils.getConnection(session).createStatement().executeQuery("SELECT storedarticleid, "
+ " CASE WHEN storedarticle_storageid = " + intStorageID
+ " THEN id_storedarticleid_replacement ELSE"
+ " (SELECT COALESCE(id_storedarticleid_replacement, storedarticleid)"
+ " FROM etel.tstoredarticle x"
+ " WHERE tstoredarticle.storedarticle_articleid = x.storedarticle_articleid"
+ " AND x.storedarticle_storageid = " + intStorageID + ")"
+ " END as id_storedarticleid_replacement,"
+ " articlename,"
+ " articledescription, CASE WHEN price IS NULL THEN last_innprice*" + exchangeRate
+ " ELSE price END as last_innprice, id_modelids[1] as order_modelid, storedarticleamount-amount_in_order-amount_waiting as disponibelt,"
+ " amount_in_order, amount_in_bestilling, sum(tusedarticle.amount) as ant_artikler, articleid,amount_waiting, location"
+ " FROM etel.tstoredarticle"
+ " INNER JOIN etel.tarticle as b ON storedarticle_articleid = b.articleid"
+ " LEFT JOIN etel.tusedarticle ON usedarticle_storedarticleid = storedarticleid"
+ " AND ((tusedarticle.datesnap >= CAST(now() as date)-" + LOOK_BACK_DAYS
+ " AND usedarticlefromstorage = true) OR waiting = true)"
+ " LEFT JOIN etel.torder ON usedarticle_orderid = orderid AND torder.id_serviceplaceid = " + Constants.SERVICEPLACE
+ " LEFT JOIN etel.tsupplier_price ON id_articleid = b.articleid"
+ " AND tsupplier_price.id_serviceplaceid = " + Constants.SERVICEPLACE
+ " AND tsupplier_price.id_currencyid = " + iCurrencyID
+ " AND tsupplier_price.id_contactid_supplier = " + iContactSupplierID
+ " LEFT JOIN etel.tusedarticle as last_used on storedarticleid = last_used.usedarticle_storedarticleid"
+ " AND last_used.usedarticleid = (select MAX(latest_usedarticle.usedarticleid) from etel.tusedarticle as latest_usedarticle where"
+ " latest_usedarticle.usedarticle_storedarticleid = storedarticleid)"
+ " WHERE storedarticle_storageid IN(" + intStorageID + ", " + secondaryStorage + ") AND b.id_brandid = " + intBrandID
+ " AND id_articletypeid IN (" + getStringFromArray(vecArticleTypes.toArray()) + ")"
+ " AND tstoredarticle.passive <> true"
+ " GROUP BY storedarticleid,id_storedarticleid_replacement, articlename, articledescription, last_innprice, id_modelids[1],"
+ " storedarticleamount, amount_in_order, amount_in_bestilling, articleid,price,amount_waiting, location, last_used.datesnap"
+ " ORDER BY id_storedarticleid_replacement, storedarticleid");
}
and here's the same query after using the copy string concatenation to clipboard feature:
(this runs fine)
SELECT storedarticleid,
CASE WHEN storedarticle_storageid = ?
THEN id_storedarticleid_replacement ELSE
(SELECT COALESCE(id_storedarticleid_replacement, storedarticleid)
FROM etel.tstoredarticle x
WHERE tstoredarticle.storedarticle_articleid = x.storedarticle_articleid
AND x.storedarticle_storageid = ?)
END as id_storedarticleid_replacement,
articlename,
articledescription, CASE WHEN price IS NULL THEN last_innprice*?
ELSE price END as last_innprice, id_modelids[1] as order_modelid, storedarticleamount-amount_in_order-amount_waiting as disponibelt,
amount_in_order, amount_in_bestilling, sum(tusedarticle.amount) as ant_artikler, articleid,amount_waiting, location
FROM etel.tstoredarticle
INNER JOIN etel.tarticle as b ON storedarticle_articleid = b.articleid
LEFT JOIN etel.tusedarticle ON usedarticle_storedarticleid = storedarticleid
AND ((tusedarticle.datesnap >= CAST(now() as date)-42
AND usedarticlefromstorage = true) OR waiting = true)
LEFT JOIN etel.torder ON usedarticle_orderid = orderid AND torder.id_serviceplaceid = ?
LEFT JOIN etel.tsupplier_price ON id_articleid = b.articleid
AND tsupplier_price.id_serviceplaceid = ?
AND tsupplier_price.id_currencyid = ?
AND tsupplier_price.id_contactid_supplier = ?
LEFT JOIN etel.tusedarticle as last_used on storedarticleid = last_used.usedarticle_storedarticleid
AND last_used.usedarticleid = (select MAX(latest_usedarticle.usedarticleid) from etel.tusedarticle as latest_usedarticle where
latest_usedarticle.usedarticle_storedarticleid = storedarticleid)
WHERE storedarticle_storageid IN(?, ?) AND b.id_brandid = ?
AND id_articletypeid IN (?)
AND tstoredarticle.passive <> true
GROUP BY storedarticleid,id_storedarticleid_replacement, articlename, articledescription, last_innprice, id_modelids[1],
storedarticleamount, amount_in_order, amount_in_bestilling, articleid,price,amount_waiting, location, last_used.datesnap
ORDER BY id_storedarticleid_replacement, storedarticleid
Don't use string concatenation with dynamic text values to build a SQL statement.
For one, if the text values are supplied by a user, you're leaving yourself vulnerable to SQL injection attacks, allowing hackers to steal your data and delete your tables.
But you also have problems getting the text values quoted and escaped correctly.
Instead, use a PreparedStatement, where you place ? markers anywhere a dynamic value needs to go. That is the string in the clipboard.
Example: You're doing:
String name = "John";
String sql = "SELECT * FROM Person WHERE name = " + name;
That gives you this text at runtime:
SELECT * FROM Person WHERE name = John
which is wrong, because the text value needs to be quoted:
SELECT * FROM Person WHERE name = 'John'
You could try
String name = "John's Cross";
String sql = "SELECT * FROM Person WHERE name = '" + name + "'";
but that's also wrong, because this new text value has embedded quotes and would produce:
SELECT * FROM Person WHERE name = 'John's Cross'
To get it right, use a PreparedStatement:
String name = "John's Cross";
String sql = "SELECT * FROM Person WHERE name = ?";
PreparedStatement stmt = Hibernate3To4Utils.getConnection(session).prepareStatement(sql);
stmt.setParameter(1, name);
return stmt.executeQuery();
The JDBC driver will take care of any escaping needed, and thereby protects you from both SQL injection attacks and SQL syntax errors.

Error no such column in SQLite when updating rows

I'm trying to update data in rows in my DB, but i catch error that there's no such column (no such column 'Moscow' or another)
This is DBHelper code:
public static final String tableName = "currentWeather";
public static final String KEY_ID = "_id";
public static final String cityName = "city";
public static final String cityTemp = "temperature";
And creating DB:
sqLiteDatabase.execSQL("create table " + tableName + "(" + KEY_ID + "
integer primary key autoincrement,"
+ cityName + " text," + cityTemp + " text, " + " UNIQUE(" + cityName +
"))");
and error shows when i try to execSQl in response:
sqLiteDatabase.execSQL(
"UPDATE " + DBHelper.tableName + " SET " +
DBHelper.cityTemp + "=" +
response.body().getForecastMain().getTemp() + "
WHERE "
+ DBHelper.cityName + "=" + cityName);
I expect to update temperature data in rows by cityName
cityName and response.body().getForecastMain().getTemp() are strings and they should be passed surrounded with single quotes to the sql statement:
sqLiteDatabase.execSQL(
"UPDATE " + DBHelper.tableName + " SET " + DBHelper.cityTemp + "='" + response.body().getForecastMain().getTemp() + "'" +
"WHERE " + DBHelper.cityName + " = '" + cityName + "'"
);
But the recommended and safe way of doing the update is with the use of ContentValues and ? as placeholders for the parameters:
ContentValues cv = new ContentValues();
cv.put(DBHelper.cityTemp, String.valueOf(response.body().getForecastMain().getTemp()));
int rows = sqLiteDatabase.update(
DBHelper.tableName,
cv,
DBHelper.cityName + " = ?",
new String[] {cityName}
);
You can examine the value of the integer variable rows.
If it is 1 this means that 1 row was updated (because cityName is unique) so the update was successful.
I think you have changed column name or add new one (city). So you can fix it by two ways
By uninstall the application from phone
Add column name in upgrade method.
Example:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// If you need to add a column
if (newVersion > oldVersion) {
db.execSQL("ALTER TABLE foo ADD COLUMN new_column INTEGER DEFAULT 0");
}
}
The thing is you need to wrap the values after the = sign in single quotations in the UPDATE statement. As for digits they work in both cases.
For example here is the correct syntax
UPDATE currentWeather
SET temperature = 45
WHERE
city = 'Moscow'
But in your code I'm assuming cityName has the value Moscow without the single quotation marks so the converted SQL code will be like this
UPDATE currentWeather
SET temperature = 45
WHERE
city = Moscow
Now the sql interpreter will think Moscow is some database object or column or something and not a literal value. So you need to surround your values in single quotation marks.
Also consider What the data type of response.body().getForecastMain().getTemp() is.
If it's int you have to parse it or something, as the data type of the related column is Text.

PreparedStatement set Where clause [duplicate]

This question already has answers here:
How to use a tablename variable for a java prepared statement insert [duplicate]
(4 answers)
Closed 4 years ago.
I'm using PreparedStatement for a DELETE query.
My ps is configured as this
config.sql.statement.delete=DELETE FROM ? WHERE ?
Then in my Java code, I set values like this
ps.setString(1, schemaName == "F" ? "FUNDS" : "MANDATE" + "." + tableName);
ps.setString(2, whereClause);
The whereClause is set up as below
String whereClause = " ";
for (int m = 0; m < columns.size(); m++) {
String columnData = jsonObj.getString(columns.get(m));
log.info("Column Data for column " + columns.get(m) + " Value: " + columnData);
if (m == 0) {
whereClause = whereClause + columns.get(m) + " = " + "'" + columnData + "'";
} else {
whereClause = whereClause + " AND " + columns.get(m) + " = " + "'" + columnData + "'";
}
}
log.info("WHERE CLAUSE: " + whereClause);
whereClause is being logged as this:
WHERE CLAUSE: CLIENT_END_DT = '9998-12-31' AND CLIENT_START_DT = '2017-04-06' AND FUND_CODE = 'TEST_CODE'
ERROR that I got:
com.microsoft.sqlserver.jdbc.SQLServerException: An expression of non-boolean type specified in a context where a condition is expected, near '#P1'.
After google a bit, I noticed that it might be related to how I configure WHERE clause. Any exact problem with the way I use this ps?
With PreparedStatement you cannot use database object names i.e. table, columns as parameter.
In your sql query, use specific names for both table and columns, in order to avoid SQL injections.
DELETE FROM table_name WHERE column1 = ? and column2 = ?
Perhaps, you may use your custom placeholder for the same as work around, beware of SQL attacks !
config.sql.statement.delete=DELETE FROM $table WHERE $whereClause
Later build your sql as:
String sql = ...; /* your logic */
sql = sql.replace("$table",(schemaName == "F" ? "FUNDS" : "MANDATE" + "." + tableName));
sql = sql.replace("$whereClause",whereClause);
ps=conn.prepareStatement(sql);

How to concatenate a character to a SQL string in a preparedstatemenet

When I concatenate a character namely tid
to this string in Java. I will get
where sc.CategoryCode = C
But I actually need
where sc.CategoryCode = 'C'
when I add a single quote both on the start
and on the end side of tid
like
where sc.CategoryCode = '" + tid + "'";
I'll get
where sc.CategoryCode = 'C"
And it seems very strange.
Thanks
Jack
String str = "select sc.* from SubCategory sc"
+ " where sc.CategoryCode = " + tid;
You shouldn't build up your SQL like this - you should use parameterized SQL instead:
// TODO: Closing the statement etc
String sql = "select sc.* from SubCategory sc where sc.CategoryCode = ?";
PreparedStatement statement = conn.prepareStatement(sql);
statement.setString(tid); // Or statement.setString(String.valueOf(tid))
ResultSet results = statement.executeQuery();
Reasons for using parameterized SQL:
Avoiding SQL injection attacks
Avoiding unnecessary (and potentially problematic) conversions, particularly with dates/times
Keeping your code (the SQL) separate from the data (the parameters), which aids readability
I don't know if you are working with jdbc template... i post you an example.
select :
private static final String SELECT_SANCIONS_GIM = "SELECT * " + "FROM "
+ Constants.T_GPT_V_DETALL_SANCIO_GIM + " WHERE "
+ Constants.EXP_ID_ENS + " =:idEns" + " AND " + Constants.EXP_ANY_EXP
+ " =:anyExp" + " AND " + Constants.EXP_NUM_EXP + " =:numExp" + " AND "
+ Constants.ACRONIM_EXP + " =:acr";
//=:variable
I use Stringbuilder, take a look:
StringBuilder sql = new StringBuilder();
sql.append(SELECT_SANCIONS_GIM);
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("idEns",clauExpedient.getIdEns() );
parameters.addValue("anyExp",clauExpedient.getAnyExp());
parameters.addValue("numExp",clauExpedient.getNumExp() );
parameters.addValue("acr", clauExpedient.getAcrProcediment());
I hope it helps.

Categories

Resources