How to convert hibernate join query to ormlite query - android

I have a hibernate query which selects particular data from two tables using its model class based on certain conditions. Now i want to convert that query to ORMLite query to use in my android application, but I didn't know how to do it using queryBuilder() in ORMLite because I am a beginner in ORMLite.
Query query = getCurrentSession().createQuery(
"SELECT A.accountID,A.name,B.allowTransactions from Payments A,Accounts B "
+ "WHERE A.accountID=B.id AND B.id NOT IN (5,55,602) AND A.active=1 AND "
+ "B.parentID=:pID AND B.date BETWEEN :sDate AND :eDate GROUP BY A.id "
+ "ORDER BY CASE WHEN A.name=:name THEN 1 ELSE 0 END,A.name");
query.setParameter("pID",4);
query.setParameter("name", "Others");
query.setParameter("sDate",startDate);
query.setParameter("eDate", endDate);
Below are the getter method for Payments and Accounts classes.
dbHelper.getPaymentDao() and dbHelper.getAccountDao()

I have a hibernate query which selects particular data from two tables using its model class based on certain conditions. Now i want to convert that query to ORMLite query...
This query is not supported by ORMLite's QueryBuilder given its complexity. You can certainly use the dao.queryRaw(...) method and then process the results yourself by hand.
GenericRawResults<String[]> results = paymentDao.queryRaw(
"SELECT A.accountID,A.name,B.allowTransactions from Payments A,Accounts B "
+ "WHERE A.accountID=B.id AND B.id NOT IN (5,55,602) AND A.active=1 AND "
+ "B.parentID=? AND B.date BETWEEN ? AND ? GROUP BY A.id "
+ "ORDER BY CASE WHEN A.name = ? THEN 1 ELSE 0 END,A.name",
parentId, fromDate, toDate, name);
Notice that I've converted the : args into ? arguments.
That returns String results but you can also use the other Dao.queryRaw(...) methods to properly convert the results into Java entities.
See: http://ormlite.com/docs/raw-queries

Related

How to write INSERT, UPDATE, DELETE,SELECT,Nested SELECT command using a prepared statement in SQLite for Android Application?

Recently I got to know that raw query in android can not prevent SQL injection and thus I decided to convert all queries in Prepared statement which is SQL injection prevention. But I don't know how to convert complex queries in Prepared Statement.
I want to convert below queries:
1.
select
*
FROM
TableName
where
(tab1col1 in(SELECT tab2Col2 FROM MasterTable where tab2col1='Y')
or tab1col2 = CV.TRUE)
order by
tab1col3, tab1col4, tab1col5,tab1col6
2.
Select
* ,count(*) as TOTAL_COUNT ,
SUM(CASE WHEN tabCol1 LIKE '%todayDate%' THEN 1 ELSE 0 END) as TOTAL_COL1_COUNT
from
TableName
group by tabCol2;
You can use rawQuery to prevent injection by passing any arguments via the selectionargs (2nd parameter).
SQL injection, wouldn't apply to either of the queries, as they are hard coded and have no user generated/supplied inputs.
e.g. your first query could be (assuming that, 'Y' and CV.TRUE are passed as parameters (i.e. user generated/supplied) for the sake of demonstration) :-
public Cursor query1raw(String indicator1,String indicator2) {
String sql = "SELECT * " +
" FROM TableName " +
" WHERE (tab1col1" +
" IN(" +
" SELECT tab2col2 " +
" FROM MasterTable " +
" WHERE tab2col1=?)" +
" OR tab1col2=?)" +
" ORDER BY tab1col3, tab1col4,tab1col5,tab1col6";
String[] args = new String[]{indicator1,indicator2};
return mDB.rawQuery(sql,args);
}
However, the convenience methods are generally recommended rather than rawQuery or execSQL when they can be used, again using bound strings via arguments, the above, using the query convenience method could be :-
public Cursor query1(String indicator1, String indicator2) {
String whereclause = "(tab1col1 IN(SELECT tab2col2 FROM MasterTable WHERE tab2col1=?) OR tab1col2=?)";
String[] whereargs = new String[] {indicator1,indicator2};
String order_columns = "tab1col3,tab1col4,tab1col5,tab1col6";
return mDB.query("TableName",null,whereclause,whereargs,null,null,order_columns);
}
You wouldn't use prepared statements themselves as they are restricted to returning single values, not a row or rows with multiple columns.
Warning not advised
However, you could, if you really wanted, use :-
public Cursor query1ps(String indicator1,String indicator2) {
String[] whereargs = new String[] {indicator1,indicator2};
SQLiteStatement stmnt = mDB.compileStatement("SELECT * " +
" FROM TableName " +
" WHERE (tab1col1" +
" IN(" +
" SELECT tab2col2 " +
" FROM MasterTable " +
" WHERE tab2col1=?)" +
" OR tab1col2=?)" +
" ORDER BY tab1col3, tab1col4,tab1col5,tab1col6");
stmnt.bindAllArgsAsStrings(whereargs);
Log.d("PREPAREDSQL",stmnt.toString());
String sql = stmnt.toString().replace("SQLiteProgram:","");
return mDB.rawQuery(sql,null);
}
As you can see all the prepared statement is doing as such, is substituting the arguments, so has little benefit over the other methods. This would also be dependant upon SQLIteProgram: remaining constant.
The only way to prevent SQL injections is to use parameters. (In some PHP APIs, the only way to get parameters is to use prepared statements, but that is not one of the warts in the Android database API.)
Just write ? for any string, and pass the values separately:
String name = ...;
String password = ...;
cursor = db.rawQuery("SELECT SomeCol FROM Users WHERE Name = ? AND Password = ?",
new String[]{ name, password });
Please not that SQL injection could happen only if you have string values that are controlled by the (potentially-hostile) user. Your queries above do not look as if this were the case.

Fetching data from multiple table

I need to get data from two tables in Android. I am using OrmLite for the database.
My query is next:
SELECT m.*, r.campaign_name, r.description, r.terms_condition
FROM mycampaignlist m, redeemlanguagedata r
WHERE r.lang_type = 2
How could I create this type of query in OrmLite.
I need to get data from two tables in Android... How could I create this type of query in OrmLite.
ORMLite does not support JOIN queries using the internal QueryBuilder if you are selecting a combination of fields from each table. In your example, you have some fields from mycampaignlist and some from redeemlanguagedata so ORMLite can't return a object without more help.
I'd recommend using the raw queries functionality and then you can get the output as a List<String[]>, List<Object[]> if you specify the data-types, or as a list of your own objects if you specify a RawRowMapper<Foo>.
For example, to quote from the docs:
GenericRawResults<Foo> rawResults = orderDao.queryRaw(
"SELECT account_id,sum(amount) FROM orders GROUP BY account_id",
new RawRowMapper<Foo>() {
public Foo mapRow(String[] columnNames,
String[] resultColumns) {
return new Foo(Long.parseLong(resultColumns[0]),
Integer.parseInt(resultColumns[1]));
}
});
// page through the results
for (Foo foo : rawResults) {
System.out.println("Account-id " + foo.accountId + " has "
+ foo.totalOrders + " total orders");
}
rawResults.close();
Hope this helps.

Getting raw SQL query after a prepared statement is built on android

I'm making an Android app and using a SQLite database. In particular I'm using the rawQuery method on a database obtained through a SQLiteOpenHelper. The query I build makes use of the ? marks as placeholders for the real values, which are passed along as an array of objects (e.g., select * from table where id = ?).
The question is, is it possible to get the query with the marks already replaced, at least from the cursor returned from the rawQuery method? I mean something like select * from table where id = 56. This would be useful for debugging purposes.
It's not possible. The ? values are not bound at the SQL level but deeper, and there's no "result" SQL after binding the values.
Variable binding is a part of the sqlite3 C API, and the Android SQLite APIs just provide a thin wrapper on top. http://sqlite.org/c3ref/bind_blob.html
For debugging purposes you can log your SQL with the ?, and log the values of your bind arguments.
You could form it as a string like this
int id = 56;
String query = "select * from table where id = '" + id + "'";
and then use it as a rawQuery like this (if I understood your question properly)
Cursor mCursor = mDb.rawQuery(query, null);
You can also use the SQLiteQueryBuilder. Here is an example with a join query:
//Create new querybuilder
SQLiteQueryBuilder _QB = new SQLiteQueryBuilder();
//Specify books table and add join to categories table (use full_id for joining categories table)
_QB.setTables(BookColumns.TABLENAME +
" LEFT OUTER JOIN " + CategoryColumns.TABLENAME + " ON " +
BookColumns.CATEGORY + " = " + CategoryColumns.FULL_ID);
//Order by records by title
_OrderBy = BookColumns.BOOK_TITLE + " ASC";
//Open database connection
SQLiteDatabase _DB = fDatabaseHelper.getReadableDatabase();
//Get cursor
Cursor _Result = _QB.query(_DB, null, null, null, null, null, _OrderBy);

How to construct a SQLite query to GROUP by ORDER?

I've got a rather interesting situation. I have a SQLite database full of addresses and messages (addresses are not unique; messages are). Each message also has a date associated with it. What I want to do is select the first message's address, message, date, and how many messages are associated with the address.
So, I thought, "I can just GROUP by the address to only get one message per address, then ORDER these by the date, and also fetch the COUNT of the address column."
I did that, and it works... kinda. It fetches the correct count, fetches only one message per address, and orders them by date--but it does not select the most recent message for the address. It appears to be arbitrary.
As an example, I have three messages (earliest to latest) A, B, C from address Y, and three messages D, E, F from address Z. The query may fetch messages B and E, then sort them by date. It should fetch messages C and F, and sort those by date.
Here is what I have so far:
// Expanded version:
Cursor cursor = db.query(
/* FROM */ "messages_database",
/* SELECT */ new String[]{ "*", "COUNT(address) AS count" },
/* WHERE */ null,
/* WHERE args */ null,
/* GROUP BY */ "address",
/* HAVING */ null,
/* ORDER BY */ "date DESC"
);
// Or, same code on one line:
Cursor cursor = db.query("messages_database", new String[]{ "*", "COUNT(address) AS count" }, null, null, "address", null, "date DESC");
I feel like this may have to do with the HAVING clause, but I really don't know. I've used MySQL a lot with PHP, but never had to touch HAVING before. I tried setting my HAVING clause to "MAX(date)", but it had no effect. If I set my GROUP BY clause to be "address, date", then they are sorted by date, but of course they are all individual instead of grouped (since the dates differ).
Google searches have proved fruitless; queries like "android sqlite order before group" and "android sqlite group by order" yield no related results.
How can I select the one latest message for each address without removing my GROUP clause (as COUNT() relies upon this)? Do I need two queries?
Edit: Based on the answer #Adrian linked me to in the comments, I came up with two queries which both produced the same result; one row, in which the count was 7 (which is the total number of addresses, not messages per address), and the address shown was not that of the latest message.
The two queries were:
Cursor cursor = db.rawQuery(
"SELECT t.*, COUNT(t.message_content) AS count "
+ "FROM messages_database t "
+ "INNER JOIN ("
+ " SELECT address, MAX(date) AS maxdate "
+ " FROM messages_database "
+ " GROUP BY address "
+ ") ss ON t.address = ss.address AND t.date = ss.maxdate",
null
);
Cursor cursor = db.rawQuery(
"SELECT t1.*, COUNT(t1.message_content) AS count "
+ "FROM messages_database t1 "
+ "LEFT OUTER JOIN messages_database t2 "
+ "ON (t1.address = t2.address AND t1.date < t2.date) "
+ "WHERE t2.address IS NULL",
null
);
SQLite has an extension that makes greatest-n-per-group problems much easier:
If you are using the MAX() or MIN() aggregate functions, and if you are selecting other columns at the same time without using them in an aggregate function or grouping by them, then the resulting values for those columns are guaranteed to come out of the same record that has the maximum/minimum value. (This is not allowed in other SQL dialects, and was introduced in SQLite 3.7.11.)
So, for your problem, you can use a query like this:
SELECT *, COUNT(address) AS count, MAX(date)
FROM messages_database
GROUP BY address
If you don't have SQLite 3.7.11 (which is likely on most Android versions) or using another SQL engine, the following query will work:
SELECT *,
(SELECT COUNT(address) AS count
FROM messages_database m2
WHERE m1.address = m2.address)
FROM messages_database m1
WHERE date = (SELECT MAX(date)
FROM messages_database m3
WHERE m1.address = m3.address)
GROUP BY address
Solved it! I ended up using a combination of #CL.'s method and the methods I outlined in my edited post (clarified in this answer, posted by #Adrian).
Because I didn't want to use 3 SELECT statements (as #CL.'s answer described), I used the same INNER JOIN concept as in the other statements, while retaining his methodology.
The result is this:
Cursor cursor = db.rawQuery(
"SELECT t.*, ss.count AS count "
+ "FROM messages_database t "
+ "INNER JOIN ("
+ " SELECT address, MAX(date) AS maxdate, COUNT(address) AS count "
+ " FROM messages_database "
+ " GROUP BY address "
+ ") ss ON t.address = ss.address AND t.date = ss.maxdate "
+ "GROUP BY t.address "
+ "ORDER BY t.date DESC ",
null
);
And it's working perfectly!

ContactsContract API - fetch display name and organization title

How can we fetch displayname and organization.data through ContactsContract APIs using impicit joins so that I can both these values in a single cursor?
You can use this code to get the organization name and display name:
Cursor organizationNameCursor = cr.query(ContactsContract.Data.CONTENT_URI,new String[] {Organization.TITLE,Organization.DISPLAY_NAME}, ContactsContract.Data.CONTACT_ID + " = " + contactId + " AND ContactsContract.Data.MIMETYPE = '"
+ ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE
+ "'",null,null);
organizationNameCursor.moveToNext();
organizationName.setText(organizationNameCursor.getString(organizationNameCursor.getColumnIndex(Organization.TITLE))+" "+organizationNameCursor.getString(organizationNameCursor.getColumnIndex(Organization.DISPLAY_NAME)));
The ContactsContact data can only be fetched by using content providers which does not allow us to have explicit joins in the query.
You can however have both the values using a single query on Data database as follows:
Cursor c = getContentResolver().query(Data.CONTENT_URI,new String[] {StructuredName.DISPLAY_NAME,Organization.COMPANY}, Data..CONTACT_ID + " = " + contactId,null,null)
In that case you wont be able to get the values directly.
You can very well fetch all the details using a single query by adding a parameter
Data.MIMETYPE IN (StructuredName,CONTENT_ITEM_TYPE, Organization.CONTENT_ITEM_TYPE)
however you need to have that logic in your code which would reorder your data.
Each MIMETYPE will fetch a separate record.
Similarly you can use RawContactsEntity for the same. It provides a join between Contacts and Data database internally.

Categories

Resources