I'm looking at the tutorial for Spatialite-Android, and I'm noticing the following code samples:
String query = "SELECT AsText(Transform(MakePoint(" + TEST_LON + ", " + TEST_LAT + ", 4326), 32632));";
sb.append("Execute query: ").append(query).append("\n");
try {
Stmt stmt = db.prepare(query);
if (stmt.step()) {
String pointStr = stmt.column_string(0);
sb.append("\t").append(TEST_LON + "/" + TEST_LAT + "/EPSG:4326").append(" = ")//
.append(pointStr + "/EPSG:32632").append("...\n");
}
stmt.close();
} catch (Exception e) {
e.printStackTrace();
sb.append(ERROR).append(e.getLocalizedMessage()).append("\n");
}
In particular, I noticed that poor practice is done of simply stringing together a SQL query, instead of a more proper method, such as is used by the Android SQLite library. Is there a way that I can make Spatialite use true prepared statements?
Just to be clear, I'm looking for something like this, using the standard SQLite database in Android:
String query="SELECT * FROM table WHERE _id=?";
Cursor data=db.rawQuery(query,new String[]{id});
There are a few tricks. They all use the exec() call, which has 3 arguments for this version. The statement from the source code is:
public void exec(String sql, jsqlite.Callback cb, String args[])
A jsqlite.Callback is an interface, of which there can be several. But the best way seems to be using a db.get_table(query,args) function. %q is the effective replacement for ? in the Android SQLite representation. Here's the transformation of the given code:
String query = "SELECT AsText(Transform(MakePoint(%q, %q, 4326), 32632));";
TableResult result=db.get_table(query,new String[]{""+TEST_LONG,""+TEST_LAT});
From there, you just have to get the results from TableResult. There isn't a method call to get the results from here, you actually have to grab the publicly declared variable and manually parse through it. Here's an example of how that can be done.
TableResult result=db.get_table(query,new String[]{""+lng,""+lat});
Vector<String[]> rows=result.rows;
for (String[] row:rows)
{
for (String val:row)
{
Log.v(TAG,val);
}
}
If you aren't doing a select, try something like this:
TableResult result=new TableResult();
db.exec("ATTACH DATABASE %q AS newDb",result,new String[]{path});
I assume the same pattern will work for INSERTS and the like
Related
I'm trying to do some Sqlite querying but I don't know if I'm doing it correctly because this feels really unsave to do. So my question is how do I fix this. I'm new to the whole Xamarin and Sqlite usage.
I'm only making a Android project so it is not a cross platform application. I also cant seem to figure out where to get Mono.Data.Sqlite if I even need it. Everything is welcome.
static public List<Users> SelectUser(string name)
{
try
{
var dbConn = new SQLiteConnection(DatabasePath);
{
return dbConn.Query<Users>("SELECT name, email FROM TblUsers where name = " + name+ ";");
}
}
catch (SQLiteException ex)
{
return null;
}
}
You should use Prepared Statements.
There is an official java documentation about Prepared Statements from Oracle here.
You can also search it on google. There are a lot of guides on how to use prepared statements.
Android application is already developed using ActiveAndroid
public static List<ModelNames> search(String pattern) {
return new Select().from(ModelNames.class)
.where("title LIKE '%" + pattern + "%' or content LIKE '%" + pattern + "%'")
.orderBy("title")
.execute();
}
Now its prone to SQL injections.
Has anyone faced a similar problem and found a solution or could anyone provide a solution for the same?
Found a issue on github, but could not get a proper solution.
The examples on the website show how to use placeholders:
public static List<ModelNames> search(String pattern) {
pattern = "%" + pattern + "%";
return new Select().from(ModelNames.class)
.where("title LIKE ? or content LIKE ?", pattern, pattern)
.orderBy("title")
.execute();
}
What I do is assuming, that everything a user inputs is a threat, so I would save everything to variables like usUsername, where "us" means unsafe. After that I check every "us"-variable for injections, what results in sUsername (s means safe). So when I build a query I can only use s-varaibles and should be safe in most cases.
This idea is totally taken from here: http://www.joelonsoftware.com/articles/Wrong.html
Already mentioned in this question.
I found out a workaround, But I let other people to answer with better proposal with respect ActiveAndroid ORM.
following is the workaround:
public static List<ModelNames> search(String pattern) {
return new Select().from(ModelNames.class)
String pattern = DatabaseUtils.sqlEscapeString(searchBar.getText().toString());
pattern = pattern.substring(1, pattern.length());
pattern = pattern.substring(0, pattern.length()-1);
.where("title LIKE '%" + pattern + "%' or content LIKE '%" + pattern + "%'")
.orderBy("title")
.execute();
}
Ref: sqlEscapeString
i am using ormlite version 4.46 I am able to get the desired result when i run a raw query but somehow the result is null when i am trying it in ormlite can someone please explain where i am making a mistake.
Snippet:
String query=
"SELECT Products.* FROM "+DBConst.TABLE_PRODUCTS
+" INNER JOIN "+DBConst.TABLE_OFFERS_MAPPING
+" ON Products."+DBConst.PROD_ID+" = OffersMapping."+DBConst.OFFERS_PRODUCT_ID
+" WHERE "+DBConst.OFFERS_OFFER_ID+ " = "+offerId+
" GROUP BY "+DBConst.PROD_PARENT_PRODVAR_ID;
GenericRawResults<Product> rawResults = productDao.queryRaw(query, productDao.getRawRowMapper());
//produces this query:SELECT Products.* FROM Products INNER JOIN OffersMapping ON Products._id = OffersMapping.product_id WHERE offer_id = 141 GROUP BY variant_id
List<Product> prodList = rawResults.getResults();
rawResults.close();
Gives me desired result....
Now to ormlite
Dao<Product, String> productDao = helper.getProductDao();
Dao<OfferMapping, String> offerMappingDao = helper.getOfferMappingDao();
try {
QueryBuilder<Product, String> productQb = productDao.queryBuilder();
QueryBuilder<OfferMapping, String> offerQb = offerMappingDao.queryBuilder();
//to sort the offer id accordingly
offerQb.where().eq(DBConst.OFFERS_OFFER_ID, offerId);
productQb.where().eq(DBConst.PROD_ID, new ColumnArg(DBConst.OFFERS_PRODUCT_ID));
productQb.join(offerQb);
productQb.groupBy(DBConst.PROD_PARENT_PRODVAR_ID);
Constants.showLog("Query", "Query is "+productQb.query());//gets null here
List<Product> prodList = productQb.query();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
Dont know where i am making a mistake...
i am using ormlite version 4.46 I am able to get the desired result when i run a raw query but somehow the result is null when i am trying it in ormlite can someone please explain where i am making a mistake.
Sorry for the late response. I would log the generated query from the ORMLite query-builder and then compare it with your hand generated query. You can log the results of productQb.prepareStatementString() before the query is performed. For more information see the logging documentation.
Let me know if ORMLite is doing something wrong.
I am testing the capabilities of the device -- to show the customer the size of data that can be stored inside the device, how fast it can be retrieved, how fast the search works, etc.
I am using my content provider to access the product database table with few columns. I have already moved the code to the content provider to avoid the extra communication when inserting the test records. The following code is called via menu from an activity to fill the table with the test content
Uri uri = Uri.parse(DemoContentProvider.PRODUCTS_CONTENT_URI + "/insertdemo");
getContentResolver().insert(uri, null);
The URI is recognized in the .insert method of the content provider and the following private method (of the same content provider) is called to fill the table (notice the 100 thousands of items):
private void insertDemoProducts() {
for (int i = 1; i <= 100000; ++i) {
String id = Integer.toString(i);
insertProduct(id, "Test product " + id, "100", "75.50", "70.27");
}
}
The inner insertProduct() looks like that:
private void insertProduct(String code, String name, String stock,
String price, String listprice) {
SQLiteDatabase sqlDB = database.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ProductTable.COLUMN_CODE, code);
values.put(ProductTable.COLUMN_NAME, name);
values.put(ProductTable.COLUMN_STOCK, stock);
values.put(ProductTable.COLUMN_PRICE, price);
values.put(ProductTable.COLUMN_LISTPRICE, listprice);
sqlDB.insert(ProductTable.TABLE_PRODUCT, null, values);
}
It works, but it takes "forever". How can I make it faster? What is the fastest method you know to fill the table?
Just some numbers to consider: 1000 items takes about 20 seconds to be created.
You need to use transactions when writing to a sqlite-database, otherwise it will persist the data for every insert i.e save it to sd which will take "forever".
for instance, make insertProduct take a list of products and save them in one transaction:
private void insertProducts(List<Product> products) {
try {
db.beginTransaction();
for(Product product : products) {
insertProduct(...);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
This is how you can implement it in your existing code:
private void insertDemoProducts() {
SQLiteDatabase sqlDB = database.getWritableDatabase();
try {
sqlDB.beginTransaction();
for (int i = 1; i <= 100000; ++i) {
String id = Integer.toString(i);
insertProduct(id, "Test product " + id, "100", "75.50", "70.27");
}
sqlDB.setTransactionSuccessful();
} finally {
sqlDB.endTransaction();
}
}
Anyway, I am not completely satisfied with the accepted question because I do not understand the reason why adding the transaction makes it faster.
Looking at the Android sources, I have found that the sqlDB.insert(...) calls insertWithOnConflict(...) and that one construct the string for the SQL command using the StringBuilder class (with questionmarks as placeholders for the inserted values). Only then the string is passed to the SQLiteStatement constructor together with array of the inserted values. This means that string with the SQL command is being built again and again.
Further, a string representation of an SQL command template can be precompiled thus avoiding also the repeated compilation of the command. Then .bindXxx and .execute methods can be used for inserting the wanted records into the table. When put together, I did use the followig code (iside the outer transaction as Dean suggested):
StringBuilder sql = new StringBuilder();
sql.append("INSERT INTO ");
sql.append(ProductTable.TABLENAME);
sql.append("(");
sql.append(ProductTable.COLUMN_CODE);
sql.append(",");
sql.append(ProductTable.COLUMN_NAME);
sql.append(",");
sql.append(ProductTable.COLUMN_STOCK);
sql.append(",");
sql.append(ProductTable.COLUMN_PRICE);
sql.append(",");
sql.append(ProductTable.COLUMN_LISTPRICE);
sql.append(") VALUES (?, ?, 100, 75.50, 70.27)");
SQLiteStatement insert = sqldb.compileStatement(sql.toString());
for (int i = 1; i <= 300000; ++i) {
insert.bindLong(1, i);
insert.bindString(2, "Test product " + i);
insert.execute();
}
When compared with adding the transaction only, the result is about 3-times faster. The 300 thousands records were inserted in about 3 minutes and 15 seconds on Nexus 7.
I am new to this, please help me.
I am trying to use ormlite like(column name,value) function, but this is not working for me. But when I test full text it is working like "eq" function.
My Code is,
try {
QueryBuilder<MakeDTO, Integer> qb = makeDao.queryBuilder();
qb.where().like("madeCompany", filterKey);
PreparedQuery<MakeDTO> pq = qb.prepare();
return makeDao.query(pq);
} catch (SQLException e) {
throw new AppException(e);
}
Thanks.
An old question, but something I just solved (ORMLite's documentation isn't that clear). you need to wrap your query parameter in "%"'s in order to tell ORMLite which side of your query string can match any number of characters.
For example, if you want your query to match any madeCompany that contains your string use the following:
try {
QueryBuilder<MakeDTO, Integer> qb = makeDao.queryBuilder();
qb.where().like("madeCompany", "%"+filterKey+"%");
PreparedQuery<MakeDTO> pq = qb.prepare();
return makeDao.query(pq);
} catch (SQLException e) {
throw new AppException(e);
}
Pretty simple, you're asking it to be exactly the string 'madeCompany', if you want to do partial matching you need to use the % wildcard etc.
public Where<T,ID> like(java.lang.String columnName,
java.lang.Object value)
throws java.sql.SQLException
Add a LIKE clause so the column must mach the value using '%' patterns.
Throws:
java.sql.SQLException
Where.like(java.lang.String, java.lang.Object)
The answers above can resolved the like query problem, but has SQL injection risk. If the value of 'filterKey' is ', it will cause SQLException, because the SQL will be SELECT * FROM XXX WHERE xxx LIKE '%'%'. You could use SelectArg to avoid, example for this case:
try {
String keyword = "%"+filterKey+"%";
SelectArg selectArg = new SelectArg();
QueryBuilder<MakeDTO, Integer> qb = makeDao.queryBuilder();
qb.where().like("madeCompany", selectArg);
PreparedQuery<MakeDTO> pq = qb.prepare();
selectArg.setValue(keyword);
return makeDao.query(pq);
} catch (SQLException e) {
throw new AppException(e);
}
Reference: http://ormlite.com/javadoc/ormlite-core/doc-files/ormlite_3.html#index-select-arguments