I've been trying to make an SQLite database which creates a new table every year automatically with the name along the lines of abc_2018. The problem is that every time a new table has to be added (i.e. the year changes) I need to update the DATABASE_VERSION. This probably requires storage of the current DATABASE_VERSION and incrementing its value every time a new table has to be added. I tried using SharedPreferences but I keep getting random errors.
So my question is how do I make a mechanism that automatically creates a table when the user's system date changes, or more precisely, when a new year starts?
EDIT
Solved my problem by doing this:
try {
cur = db.query(TABLE_NAME, PROJECTION, SELECTION, ARGS, null, null, null);
} catch (SQLiteException e) {
if (e.getMessage().contains("no such table")){
// create new table and execute query
}
}
Instead of :-
try {
cur = db.query(TABLE_NAME, PROJECTION, SELECTION, ARGS, null, null, null);
} catch (SQLiteException e) {
if (e.getMessage().contains("no such table")){
// create new table and execute query
}
}
Regarding the potential issues as pointed out by pskink's comment:-
if (e.getMessage().contains("no such table")) no no no, its so ugly
workaround that i dont know what to say... what if they change it to
"No such table" someday? or "that table does not exist"?
The following would be more resilient (considering how SQLite caters for backwards compatibility) :-
cur = db.query(sqlite_master,new String{"tbl_name"},"tbl_name=?",new String[]{TABLE_NAME},null,null,null);
if (cur.getCount < 1) { // ==0 if you prefer
//Create new table
}
You can use broadcast receiver like ACTION_DATE_CHANGED
More details can be found at https://developer.android.com/reference/android/content/Intent.html#ACTION_DATE_CHANGED
Code:
public void createDynamicDatabase(Context context,String tableName,ArrayList<String> title) {
Log.i("INSIDE createLoginDatabase() Method","*************creatLoginDatabase*********");
try {
int i;
String queryString;
myDataBase = context.openOrCreateDatabase("Db",Context.MODE_WORLD_WRITEABLE, null); //Opens database in writable mode.
//System.out.println("Table Name : "+tableName.get(0));
queryString = title.get(0)+" VARCHAR(30),";
Log.d("**createDynamicDatabase", "in oncreate");
for(i = 1; i < title.size() - 1; i++)
{
queryString += title.get(i);
queryString +=" VARCHAR(30)";
queryString +=",";
}
queryString+= title.get(i) +" VARCHAR(30)";
queryString = "CREATE TABLE IF NOT EXISTS " + tableName + "("+queryString+");";
System.out.println("Create Table Stmt : "+ queryString);
myDataBase.execSQL(queryString);
} catch (SQLException ex) {
Log.i("CreateDB Exception ",ex.getMessage());
}
}
public void insert(Context context,ArrayList<String> array_vals,ArrayList<String> title,String TABLE_NAME) {
Log.d("Inside Insert","Insertion starts for table name: "+TABLE_NAME);
myDataBase = context.openOrCreateDatabase("Db",Context.MODE_WORLD_WRITEABLE, null); //Opens database in writable mode.
String titleString=null;
String markString= null;
int i;
titleString = title.get(0)+",";
markString = "?,";
Log.d("**createDynamicDatabase", "in oncreate");
for(i = 1; i < title.size() - 1; i++)
{
titleString += title.get(i);
titleString +=",";
markString += "?,";
}
titleString+= title.get(i);
markString += "?";
//System.out.println("Title String: "+titleString);
//System.out.println("Mark String: "+markString);
INSERT="insert into "+ TABLE_NAME + "("+titleString+")"+ "values" +"("+markString+")";
System.out.println("Insert statement: "+INSERT);
//System.out.println("Array size iiiiii::: "+array_vals.size());
//this.insertStmt = this.myDataBase.compileStatement(INSERT);
int s=0;
while(s<array_vals.size()){
System.out.println("Size of array1"+array_vals.size());
//System.out.println("Size of array"+title.size());
int j=1;
this.insertStmt = this.myDataBase.compileStatement(INSERT);
for(int k =0;k< title.size();k++)
{
//System.out.println("Value of column "+title+" is "+array_vals.get(k+s));
//System.out.println("PRINT S:"+array_vals.get(k+s));
System.out.println("BindString: insertStmt.bindString("+j+","+ array_vals.get(k+s)+")");
insertStmt.bindString(j, array_vals.get(k+s));
j++;
}
s+=title.size();
}
insertStmt.executeInsert();
}
Related
I am trying to put json object in sqlite database
private void addList(String info){
try {
JSONObject reader=new JSONObject(info);
String COMMAND = reader.getString("COMMAND");
String PARAMS = reader.getString("PARAMS");
int id = vpictures.InsertList(COMMAND,info);
} catch (Exception e){
if (DEBUG_FLAG)Log.d("Log1 ", "adding Exception :"+ e.getMessage());
}
return;
}
The json object info looks like this
{
"COMMAND":"ADD_NEW",
"PARAMS":{
"deviceID":"1234",
"custID":"41701",
"description":"Ddd",
"colTranType":"ABS",
}
}
This is my sqlite table
private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME + " (ID INTEGER, DEVICEID TEXT, LTIME INTEGER, LATITUDE REAL,"+
"LONGITUDE REAL, THEPICTURE BLOB, SENT INTEGER, NOTES TEXT, COMMAND TEXT, PARAMS TEXT);";
I am trying to insert COMMAND and PARAMS.
And my sqlite code looks like this
public int InsertList(String COMMAND, String info){
try {
JSONObject reader = new JSONObject(info);
String param_str = reader.getString("info");
if (_Db == null)
_Db = getWritableDatabase();
if (_LastId == -1)
{
Cursor c = _Db.query(TABLE_NAME, new String[] {"max(ID)"}, null, null, null, null, COMMAND, param_str);
if (c != null && c.moveToFirst()) {
_LastId = c.getInt(0);
c.close();
}
else
_LastId = 0;
c.close();
}
try {
ContentValues cv = new ContentValues();
cv.put("ID",++_LastId);
cv.put("COMMAND",String.valueOf(COMMAND));
cv.put("PARAMS",PARAMS);
_Db.insert(TABLE_NAME, "", cv);
} catch (Exception e){
Log.d("Log2","Error:"+e.getMessage());
}
} catch (JSONException e1) {
Log.d("Log2","Error:"+e1.getMessage());
}
return _LastId;
}
Basically the exception i am getting from the addList function
adding Exception : Exception invalid LIMIT clauses
How to consider inserting json object into sqlite
These are the parameters of the query() method:
Cursor c = _Db.query(
TABLE_NAME, // table
new String[] {"max(ID)"}, // columns
null, // selection
null, // selectionArgs
null, // groupBy
null, // having
COMMAND, // orderBy
param_str); // limit
The orderBy and limit parameters do not make sense. To find the largest ID in the entire table, these parameters must be null.
Anyway, there is a helper function that makes it reasier to read a single number from the database without having to muck around with a cursor:
long lastID = DatabaseUtils.longForQuery(_Db, "SELECT max(ID) FROM "+TABLE_NAME, null);
And if you had declared the ID column as INTEGER PRIMARY KEY, it would be autoincremented, and you would not have to set it manually.
I am running a background task that goes out and downloads a JSON file, pareses it, then adds it to the contents to a SQLite database.
I am getting a couple of errors when it runs.
Caused by: android.database.CursorWindowAllocationException: Cursor
window allocation of 2048 kb failed. # Open Cursors=728 (# cursors
opened by this proc=728)
E/CursorWindow: Could not allocate CursorWindow
'/data/data/com.mycompany.inventory/databases/dbInventory.sql' of size
2097152 due to error -12.
The JSON has about 1500 items in it.
Here is the method my async task calls:
public void addModelsToDB(JSONObject dict){
String insertQuery = "";
String deleteQuery = "DROP TABLE IF EXISTS 'tblModels'";
String createQuery = "CREATE TABLE 'tblModels' ('modelsID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,'makeKey' INTEGER, 'modelName' TEXT, 'modelKey' INTEGER)";
Cursor cursor = dbLocals.executeRawQuery(deleteQuery, null);
cursor.moveToFirst();
cursor = dbLocals.executeRawQuery(createQuery, null);
cursor.moveToFirst();
try {
JSONArray dicRecordSet = dict.getJSONArray("Recordset");
JSONObject dicRecords = dicRecordSet.getJSONObject(0);
JSONArray arrRecords = dicRecords.getJSONArray("Record");
for (int i = 0; i < arrRecords.length(); i++) {
JSONObject record = arrRecords.getJSONObject(i);
insertQuery = "INSERT INTO 'tblModels' VALUES(" + null + ", "
+ record.getString("MODMAKlMakeKey") + ", '"
+ record.getString("MODvc50Name").replaceAll("'", "''") + "', "
+ record.getString("MODlModelKey")
+")";
cursor = dbLocals.executeRawQuery(insertQuery, null);
cursor.moveToFirst();
}
} catch (JSONException e) {
e.printStackTrace();
}
cursor.close();
}
My database a manager returns a cursor.
public Cursor executeRawQuery(String query, String[] selectionArgs) {
Cursor cursor = databaseConn.rawQuery(query, selectionArgs);
return cursor;
}
What am I doing wrong?
You can't reuse the cursor variable because it shadows the original one, and therefore you can't close it:
Cursor cursor = dbLocals.executeRawQuery(deleteQuery, null);
then
cursor = dbLocals.executeRawQuery(insertQuery, null);
this second assignment means you can't close the original cursor.
Also, why are you creating the table here?
Edit:
Use it like this:
public void addModelsToDB(JSONObject dict){
String insertQuery = "";
String deleteQuery = "DROP TABLE IF EXISTS 'tblModels'";
String createQuery = "CREATE TABLE 'tblModels' ('modelsID' INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,'makeKey' INTEGER, 'modelName' TEXT, 'modelKey' INTEGER)";
Cursor cursor = dbLocals.executeRawQuery(deleteQuery, null);
try {
cursor.moveToFirst();
cursor = dbLocals.executeRawQuery(createQuery, null);
cursor.moveToFirst();
} finally {
cursor.close();
}
try {
JSONArray dicRecordSet = dict.getJSONArray("Recordset");
JSONObject dicRecords = dicRecordSet.getJSONObject(0);
JSONArray arrRecords = dicRecords.getJSONArray("Record");
for (int i = 0; i < arrRecords.length(); i++) {
JSONObject record = arrRecords.getJSONObject(i);
insertQuery = "INSERT INTO 'tblModels' VALUES(" + null + ", "
+ record.getString("MODMAKlMakeKey") + ", '"
+ record.getString("MODvc50Name").replaceAll("'", "''") + "', "
+ record.getString("MODlModelKey")
+")";
cursor = dbLocals.executeRawQuery(insertQuery, null);
try {
cursor.moveToFirst();
} finally {
cursor.close();
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
You should close cursors after using them. Inside the loop, you are creating a cursor every iteration, without ever closing it. Apparently there is a limit for the number of open cursors and you hit that limit.
I'm trying to create a score database that increments the players 'score' by one when they win by calling updateScore(). The primary key and player number are identical (I may need to restructure the DB at some point) and the final column is 'score'.
Below is the code that initially sets the score (this works), the method that gets the score (also works fine) and the method that updates the score, incrementing the relevant players score by 1. This is the part the doesn't work, is there something I should be doing differently here? Thanks.
/** Add a record to the database of two player scores
* #param playerId
* #param playerScore
**/
public void addScore (int playerId, int playerScore) {
SQLiteDatabase database = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(ID, playerId);
values.put(PLAYERNUM, playerId);
values.put(SCORE, playerScore);
database.insert(TABLE_2PSCORES, null, values);
database.close();
}
// Get the score
public int getScore (int playerId) {
SQLiteDatabase database = this.getReadableDatabase();
Cursor cursor = database.query(TABLE_2PSCORES, COLUMNS, " player = ?", new String[] {String.valueOf(playerId) }, null, null, null, null); //null = groupby, having, orderby, limit
if (cursor !=null) { cursor.moveToFirst(); }
int output = cursor.getInt(2);
return output;
}
// Increment score by 1
public void updateScore (int playerId) {
SQLiteDatabase database = this.getWritableDatabase();
int playerScore = getScore(playerId);
int playerScoreInc = playerScore ++;
ContentValues values = new ContentValues();
values.put("score", playerScoreInc);
database.update(TABLE_2PSCORES, values, PLAYERNUM+" = ?", new String[] {String.valueOf(playerId)} );
database.close();
}
int playerScoreInc = playerScore ++;
This assigns playerScore to playerScoreInc and only after that increments playerScore. To first increment and then assign, change to ++playerScore.
However, you can do it all in SQL, no need to fetch score, increment it in code and then update the database table separately:
database.execSQL("UPDATE " + TABLE_2PSCORES + " SET " + SCORE + "=" + SCORE + "+1" + " WHERE " + PLAYERNUM + "=?",
new String[] { String.valueOf(playerId) } );
The other answers solve the original question, but the syntax makes it hard to understand. This is a more general answer for future viewers.
How to increment a SQLite column value
SQLite
The general SQLite syntax is
UPDATE {Table} SET {Column} = {Column} + {Value} WHERE {Condition}
An example of this is
UPDATE Products SET Price = Price + 1 WHERE ProductID = 50
(Credits to this answer)
Android
Now that the general syntax is clear, let me translate that into Android syntax.
private static final String PRODUCTS_TABLE = "Products";
private static final String ID = "ProductID";
private static final String PRICE = "Price";
String valueToIncrementBy = "1";
String productId = "50";
String[] bindingArgs = new String[]{ valueToIncrementBy, productId };
SQLiteDatabase db = helper.getWritableDatabase();
db.execSQL("UPDATE " + PRODUCTS_TABLE +
" SET " + PRICE + " = " + PRICE + " + ?" +
" WHERE " + ID + " = ?",
bindingArgs);
db.close();
TODO
This answer should be updated to use update rather than execSQL. See comment below.
Change
int playerScoreInc = playerScore ++;
to
int playerScoreInc = ++ playerScore;
I think this will work
// Increment score by 1
public void updateScore (int playerId) {
SQLiteDatabase database = this.getWritableDatabase();
int playerScore = getScore(playerId);
int playerScoreInc = ++ playerScore;
ContentValues values = new ContentValues();
values.put("score", playerScoreInc);
database.update(TABLE_2PSCORES, values, PLAYERNUM+" = ?", new String[] {String.valueOf(playerId)} );
database.close();
}
Have you tried debugging? Try debugging this line:
int playerScoreInc = playerScore ++;
The playerScoreInc doesn't increment.
I'm Parsing a JSON WebService and creating a array with data to INSERT and DELETE entries in a database.
I found the solution bulkInsert to insert multiple rows using database transactions inside a content provider, however, I am trying to do the same procedure to delete multiple lines.
The INSERT solution:
#Override
public int bulkInsert(Uri uri, ContentValues[] allValues) {
SQLiteDatabase sqlDB = mCustomerDB.getWritableDatabase();
int numInserted = 0;
String table = MyDatabase.TABLE;
sqlDB.beginTransaction();
try {
for (ContentValues cv : allValues) {
//long newID = sqlDB.insertOrThrow(table, null, cv);
long newID = sqlDB.insertWithOnConflict(table, null, cv, SQLiteDatabase.CONFLICT_REPLACE);
if (newID <= 0) {
throw new SQLException("Error to add: " + uri);
}
}
sqlDB.setTransactionSuccessful();
getContext().getContentResolver().notifyChange(uri, null);
numInserted = allValues.length;
} finally {
sqlDB.endTransaction();
}
return numInserted;
}
Using this call:
mContext.getContentResolver().bulkInsert(ProviderMyDatabase.CONTENT_URI, valuesToInsertArray);
Is there any way to delete multiple rows (with this array ID's) of database using content provider.
UPDATE:
I found this solution, using the `IN clause:
List<String> list = new ArrayList<String>();
for (ContentValues cv : valuesToDelete) {
Object value = cv.get(DatabaseMyDatabase.KEY_ROW_ID);
list.add(value.toString());
}
String[] args = list.toArray(new String[list.size()]);
String selection = DatabaseMyDatabase.KEY_ROW_ID + " IN(" + new String(new char[args.length-1]).replace("\0", "?,") + "?)";
int total = mContext.getContentResolver().delete(ProviderMyDatabase.CONTENT_URI, selection, args);
LOGD(TAG, "Total = " + total);
The problem is that, if the JSON return more than 1000 rows to insert, occurs error, because the SQLITE_MAX_VARIABLE_NUMBER is set to 999. It can be changed but only at compile time.
ERROR: SQLiteException: too many SQL variables
Thanks in advance
I solved this issue with this code:
if (!valuesToDelete.isEmpty()) {
StringBuilder sb = new StringBuilder();
String value = null;
for (ContentValues cv : valuesToDelete) {
value = cv.getAsString(kei_id);
if (sb.length() > 0) {
sb.append(", ");
}
sb.append(value);
}
String args = sb.toString();
String selection = kei_id + " IN(" + args + ")";
int total = mContext.getContentResolver().delete(uri, selection, null);
LOGD(TAG, "Total = " + total);
} else {
LOGD(TAG, "No data to Delete");
}
Thanks
User ContentResolver object to delete multiple rows.
// get the ContentResolver from a context
// if not from any activity, then you can use application's context to get the ContentResolver
// 'where' is the condition e.g., "field1 = ?"
// whereArgs is the values in string e.g., new String[] { field1Value }
ContentResolver cr = getContentResolver();
cr.delete(ProviderMyDatabase.CONTENT_URI, where, whereArgs);
So any row with (field1 = field1Value) will be deleted.
If you want to delete all the rows then
cr.delete(ProviderMyDatabase.CONTENT_URI, "1 = 1", null);
I built a table in the database, and now I want to access the all of the values in a certain column. Finally, I want to put the data into byte[].
Part of my code:
db.execSQL("create table thing(id integer primary key" +
" autoincrement, name varchar(20))");
List<Integer> all = new ArrayList<Integer>();
String sql = " SELECT id from " + DB_NAME;
Cursor result = this.db.rawQuery(sql, null);
for (result.moveToFirst(); result.isAfterLast(); result.moveToNext()) {
all.add(result.getInt(0));
}
String[] fstr = (String[]) all.toArray();
for (String bstr : fstr) {
byte[] bbs = bstr.getBytes();
}
Use this code
String[] fstr = new String[result.getCount()] ;
do {
int posi = result.getPosition();
fstr[posi] =result.getString(0);
}while (result.moveToNext());
try this
Cursor c=this.db.rawQuery(sql, null);
if(c.getCount()>0)
{
for(c.moveToFirst();!c.isAfterLast();c.moveToNext())
{
String str=c.getInt(0));// or c.getString();
}
}
else
{
Log.d(" Null value in cursor ", " null ");
}
c.close();
dbhelper.close();