Recently, I've had a few bugs because of code like this:
Cursor myCursor = myDb.rawQuery(
"SELECT ... " +
" FROM ...complicated join... " +
" WHERE field1 = ? AND (field2 = ? OR field3 = ?) ",
new String[] {myField1, myField2}); // Oops, forgot about field3
When this happens, the query just silently ignores the missing parameter, causing bugs to go unnoticed. Is there some pedantic setting or anything else that I can use to make SQLite scream (at run-time) when the number of placeholders and the number of fields do not match?
I know that I could build my own wrapper, but I'm wondering if there's something built-in...
Android is basically just passing the args unchecked to native sqlite, see http://www.sqlite.org/c3ref/bind_blob.html
If something is not bound it is simply considered to be bound to NULL. Binding too much should result in an error though
I don't know of / have not seen any debug option in Android's source for that kind of checks but you could probably write some code that checks your sql syntax:
SQLiteChecker mDbChecked = new SQLiteChecker(mDb);
Cursor c = mDbChecked.rawQuery("select complicated from table where stuff=?",
new String[] {"one", "two"});
where SQLiteChecker would be something along the lines of
/**
* Simple Delegate for SQLiteDatabase
*/
public class SQLiteChecker {
private final SQLiteDatabase mDbDelegate;
public SQLiteChecker(SQLiteDatabase db) {
mDbDelegate = db;
}
// ------------ Delegate methods --------------------//
public int delete(String table, String whereClause, String[] whereArgs) {
checkSQL(whereClause, whereArgs);
return mDbDelegate.delete(table, whereClause, whereArgs);
}
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
checkSQL(whereClause, whereArgs);
return mDbDelegate.update(table, values, whereClause, whereArgs);
}
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
checkSQL(sql, bindArgs);
mDbDelegate.execSQL(sql, bindArgs);
}
public Cursor rawQuery(String sql, String[] selectionArgs) {
checkSQL(sql, selectionArgs);
return mDbDelegate.rawQuery(sql, selectionArgs);
}
// add more if you need
// -------------- checking logic -------------------//
private static void checkSQL(String query, Object[] args) {
// bit unreliable but simple:
// just check if amount of ? matches args.length
int expected = countChar(query, '?');
int actual = args != null ? args.length : 0;
if (expected != actual) {
Log.e("CHECK", "You seem to have messed up [" + query + "]");
Log.e("CHECK", "expected:" + expected + " actual:" + actual);
}
}
private static int countChar(String string, char ch) {
if (string == null) return 0;
int count = 0;
for (int i = 0; i < string.length(); i++) {
if (string.charAt(i) == ch)
count++;
}
return count;
}
}
Related
As written in the title, I check my SQL database with following method:
public String[] getRecord(String category){
String[] record = new String[3];
Cursor crsRecord = sqliteDatabase.rawQuery(QUERY_GET_RECORD + category, null);
int i=0;
while(crsRecord.moveToNext()){
record[i] = crsRecord.getString(0);
i++;
}
return record;
}
Now it could be that the line:
Cursor crsRecord = sqliteDatabase.rawQuery(QUERY_GET_RECORD + category, null);
has no result, because I have no appropriate data in my database. How can I check that I have no result?
cursor.moveToFirst();
if (cursor.isAfterLast()){
// You have no results
}
Or, you could just change your code to this:
while(!crsRecord.isAfterLast()){
// Instead of using an int literal to get the colum index,
// use the getColumnIndex method
int index = crsRecord.getColumnIndex(COLUMN_NAME);
if (index == -1) {
// You don't have the column-- do whatever you need to do.
}
else {
record[i] = crsRecord.getString(index);
i++;
}
crsRecord.moveToNext();
}
If there are no records, the while loop never starts.
Here is my simple function:
public int countCats(String tableName) {
int catCount = 0;
Cursor cursor = database.rawQuery("SELECT COUNT(*) FROM " + MySQLiteHelper.TABLE_CAT, null);
if (cursor != null) {
catCount = cursor.getColumnIndex("COUNT");
}
return catCount;
}
There are 11 rows int this table. But this function returns -1. How is appropriate way to handle this?
EDIT:
I have updated to this:
public int countCats(String tableName) {
int catCount = 0;
Cursor cursor = database.rawQuery("SELECT COUNT("+ MySQLiteHelper.COLUMN_CAT+ ") FROM " + MySQLiteHelper.TABLE_CAT, null);
if (cursor.moveToFirst()) {
catCount = cursor.getInt(0);
}
cursor.close();
return catCount;
}
Now I get 0;
You're only asking for the column index for a column COUNT that does not exist in the cursor. Hence -1 is returned.
To retrieve the count value, move the cursor to the first row and get the first column value:
if (cursor.moveToFirst()) {
catCount = cursor.getInt(0);
}
(A COUNT(*) query will always have a result row but it's a good habit to check the result of moveToFirst() anyway.)
You forget to call
cursor.moveToFirst();
You are just looking for the index of the result and not the value itself.
So now you know that the value you like to know is in the first column. Now you have to go to the first row and take that value.
cursor.moveToFirst();
int count=cursor.getInt(1);
I am getting NullPointerException whle performing COUNT() in SQLite. please see the following code -
public int getrcofpersons() {
// TODO Auto-generated method stub
SQLiteDatabase myDB;
int values = 0;
int count = 0;
try {
myDB=this.openDataBase();
Cursor c=myDB.rawQuery("select count(PersonID) from Persons;",null);
if (c != null ) {
String h = "";
c.moveToFirst();
count = c.getInt(c.getColumnIndex("PersonID"));
// put your code to get data from cursor
}
if(c != null) {
c.close();
myDB.close();
}
} catch(SQLException sqle) {
throw sqle;
}
System.out.println(count + " is the rowcount of persons.");
return count;
}
This function is returning Null value. Even System.out.println(count + " is the rowcount of persons."); also showing the count value as 0, which is the initialized value. Now I cannot post the logcat, because this code snippet is associated with many other functionalities, that you might not understand. So, please, tell me if I did any mistake in this code.
Please see the following code. This code is calling the above method(which is in the helper.java).
adapter.java :
public int getrowcountofpersons() {
// TODO Auto-generated method stub
int rc = 0;
try {
//open();
rc = mDbHelper.getrcofpersons(); //Here the nullPointerException is raised.
// close();
}
catch (Exception e)
{Log.v("getrowcountofpersons()","5");
Log.v("Error in getrowcountofpersons() of adapter : ",e.toString());
}
Log.v("getrowcountofpersons()","6");
System.out.println(rc + " is the rowcount of persons in adapter.");
return rc;
}
Thanks in advance.
Change
count = c.getInt(c.getColumnIndex("PersonID"));
to
count = c.getInt(0);
There is no column name PersonID into your Cursor object c. And this will have only one column so you can retrieve that column using 0 (ZERO) index.
After code update :-
Check if mDbHelper is null or not. This might be null. So check if you are initiating the mDbHelper object.
I am working on a multithreaded project in which I need to randomly find columns that I will be using in my SELECT sql and then I will be executing that SELECT sql query.
After finding those columns-
I need to see whether the id is between the valid range, if it in between the Valid Range, then loop around the resultset using those columns from the columnsList and get the data back and store in a variable.
else if id is not in the valid range, I need to check I am not getting any data back from the resultset. But somehow if I am getting the data back and flag is true to stop the program, then exit the program. Else if I am getting the data back but flag is false to stop the program, then count how many of those happening.
Below is my code-
private volatile int count;
#Override
public void run() {
.....
final String columnsList = getColumns(table.getColumns());
....
rs = preparedStatement.executeQuery();
....
if (Read.endValidRange < id && id < Read.startValidRange) {
while (rs.next()) {
for(String column: columnsList.split(",")) {
System.out.println(column + ": " + rs.getString(column));
}
}
} else {
if(rs.next() && Read.flagTerminate) {
System.exit(1);
} else {
count++;
}
}
....
}
/**
* A simple method to get the column names in the order in which it was
* inserted
*
* #param columns
* #return
*/
private String getColumns(final List<String> columns) {
List<String> copy = new ArrayList<String>(columns);
Collections.shuffle(copy);
int rNumber = random.nextInt(columns.size());
List<String> subList = copy.subList(0, rNumber);
Collections.sort(subList, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return columns.indexOf(o1) < columns.indexOf(o2) ? -1 : 1;
}
});
return StringUtils.join(subList, ",");
}
Problem Statement-
I am not able to understand how should I iterate through the resultset as every time I will be having different columns in my SELECT sql depending on what is getting generated. And apart from that how can I make it more cleaner in the if else loop.
OK..
You can have the Randomly generated columns saved in a String array..and then while iterating via resultset you use that array...as follows:
UPDATE
You should not include those columns in select statement..Instead you should select all columns in select statement using *...and then get the value of that column in rs.next() loop...
rs = con.executeQuery("Select * from tablename");
while(rs.next())
{
for (String col : columnsList )
{
System.out.print(rs.get(col));
}
System.out.print("\n");
}
UPDATE1
You need your critical code section written within run() to be wrapped in synchronized block so as to avoid Race Condition as follows:
private volatile int count;
#Override
public void run() {
.....
while (!Read.flagTerminate)
{
synchronized(this)
{
if (!Read.flagTerminate)
{
String columnsList = getColumns(table.getColumns());
....
rs = preparedStatement.executeQuery();
....
if (Read.endValidRange < id && id < Read.startValidRange)
{
while (rs.next())
{
for(String column: columnsList.split(","))
{
System.out.println(column + ": " + rs.getString(column));
}
}
}
else
{
while (rs.next())
{
count++;
}
}
}
}
}
System.exit(0);//it implies that flag = true;
....
}
/**
* A simple method to get the column names in the order in which it was
* inserted
*
* #param columns
* #return
*/
private String getColumns(final List<String> columns) {
List<String> copy = new ArrayList<String>(columns);
Collections.shuffle(copy);
int rNumber = random.nextInt(columns.size());
List<String> subList = copy.subList(0, rNumber);
Collections.sort(subList, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return columns.indexOf(o1) < columns.indexOf(o2) ? -1 : 1;
}
});
return StringUtils.join(subList, ",");
}
What about this:
if (Read.endValidRange < id && id < Read.startValidRange) {
while (rs.next()) {
for (String column : columnsList.split(",")) {
System.out.println(column + ": " + rs.getString(column));
}
}
} else if (rs.next()) {
if (Read.flagTerminate) {
System.exit(1);
} else {
count++;
}
}
I have also updated the if-else conditions as per my understanding of the question.
here is my problem : at some point in my Java program, I get a (very) big List of Events from a database using the SimpleJdbcTemplate class from Spring.
List<Event> events =
this.simpleJdbcTemplate.query(myQuery,
myMapper(),
new Object[] {
filter.getFirst(),
filter.getSecond(),
filter.getThird()}
);
The problem is that the list may contain something like 600,000 Events ... Therefore using a lot of memory (and also taking time to be processed).
However I don't really need to retrieve all the Events at once. Actually I would like to be able to iterate over the list, read only a few events (linked to a specific KEY_ID - the sql query myQuery is ordered by KEY_ID), process them and finally get back iterating, letting the garbage collector get rid of the previous and already processed Events so that I never exceed a certain amount of memory.
Is there a nice way to do so using the Spring library (or any library)?
Cheers,
Vakimshaar.
I think part of your problem is that your query is executing all at once and you're getting the result set in a big lump that hogs memory and network bandwidth. In addition to needing a way to iterate through the result set you need a way to get the results back from the database a bit at a time.
Take a look at this answer about lazy-loading resultsets. It looks like you could set the fetch size in combination with using a ResultSetExtractor and possibly get the desired behavior (depending on the database).
You should construct your SQL query to return limited set of items starting with particular number. It is database specific operation (in Oracle and MySql you will manipulate rownum in some form) . Then you repeat the call increasing start number until all elements are processed.
Oracle example
SELECT * FROM ([your query]) WHERE rownum>=[start number]
AND rownum<[start number + chunk size];
If I understand correctly, you would like to iterate over the result set, but are not interested in building the full list of results.
Just use the query method with a ResultSetExtractor as argument. The ResultSetExtractor can use your mapper to transform the current row into an Event. Put every event into a list until you reach a different KEY_ID or the end of the result set, then proceed with your list of events and clear the list.
Maybe the following code might be useful for you?
protected <T> List<T> queryPagingList(final String query, final PagingQueryContext context, final ParameterizedRowMapper<T> mapper, final SqlParameter... parameters) throws DataAccessException {
final Integer count = context.getCount();
final Integer beginIndex = context.getBeginIndex();
final List<SqlParameter> parameterList = Arrays.asList(parameters);
final PreparedStatementCreatorFactory preparedStatementCreatorFactory = new PreparedStatementCreatorFactory(query, parameterList);
preparedStatementCreatorFactory.setResultSetType(ResultSet.TYPE_SCROLL_INSENSITIVE);
preparedStatementCreatorFactory.setNativeJdbcExtractor(new NativeJdbcExtractorAdapter() {
#Override
public PreparedStatement getNativePreparedStatement(final PreparedStatement ps) throws SQLException {
ps.setFetchSize(count + 1);
ps.setMaxRows((beginIndex * count) + 1);
return ps;
}
#Override
public Statement getNativeStatement(final Statement stmt) throws SQLException {
stmt.setFetchSize(count + 1);
stmt.setMaxRows((beginIndex * count) + 1);
return stmt;
}
});
final PreparedStatementCreator psc = preparedStatementCreatorFactory.newPreparedStatementCreator(parameterList);
final ResultSetExtractor<List<T>> rse = new ResultSetExtractor<List<T>>() {
public List<T> extractData(final ResultSet rs) throws SQLException {
if (count > 0) {
rs.setFetchSize(count + 1);
if (beginIndex > 0) {
rs.absolute((beginIndex - 1) * count);
}
}
rs.setFetchDirection(ResultSet.FETCH_FORWARD);
final List<T> results = new ArrayList<T>(count + 1);
for (int rowNumber = 0; rs.next(); ++rowNumber) {
if (count > 0 && rowNumber > count) {
break;
}
results.add(mapper.mapRow(rs, rowNumber));
rs.last();
context.setTotalResults(rs.getRow());
}
return results;
}
};
return this.simpleJbcTemplate.query(psc, null, rse);
}
Here is PagingQueryContext:
public class PagingQueryContext implements Serializable {
private static final long serialVersionUID = -1887527330745224117L;
private Integer beginIndex = 0;
private Integer count = -1;
private Integer totalResults = -1;
public PagingQueryContext() {
}
public Integer getBeginIndex() {
return beginIndex;
}
public void setBeginIndex(final Integer beginIndex) {
this.beginIndex = beginIndex;
}
public Integer getCount() {
return count;
}
public void setCount(final Integer count) {
this.count = count;
}
public Integer getTotalResults() {
return totalResults;
}
public void setTotalResults(final Integer totalResults) {
this.totalResults = totalResults;
}
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((beginIndex == null) ? 0 : beginIndex.hashCode());
result = prime * result + ((count == null) ? 0 : count.hashCode());
return result;
}
#Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof PagingQueryContext)) {
return false;
}
final PagingQueryContext other = (PagingQueryContext) obj;
if (beginIndex == null) {
if (other.beginIndex != null) {
return false;
}
} else if (!beginIndex.equals(other.beginIndex)) {
return false;
}
if (count == null) {
if (other.count != null) {
return false;
}
} else if (!count.equals(other.count)) {
return false;
}
return true;
}
}
It adds one to the fetch size so that you can peek to see if there will be more results. Also, depending on how the JDBC driver you are using implements rs.last(), you might not want to use that call in the ResultSetExtractor and forgo using totalRows. Some drivers might load all of the data when last() is invoked.