I am trying to use a SQLite database to store information for a recycler view. I would like the database to contain only unique records according to their description. I have tried to set the description field to unique when I create the database and it does not seem to help. I instead decided to create a function that checks if the description already exists inside the database and then insert the new record with this information. This function works fine, after running the debug tool to see where I went wrong. Where I think the error lies is in the db.insert function that does not seem to execute when inside the if statement. Any help is appreciated.
P.S. I'm a total noob at SQLite and not very familiar with the jargon around it.
Here is part of the code for the class that helps with database handling
public class DataBaseHandler extends SQLiteOpenHelper {
private final Context context;
public DataBaseHandler(#Nullable Context context) {
super(context, Constants.DB_NAME, null, Constants.DB_VERSION);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_FOOD_TABLE = "CREATE TABLE " + Constants.TABLE_NAME + "("
+ Constants.KEY_ID + " INTEGER PRIMARY KEY,"
+ Constants.KEY_FOOD_ITEM + " INTEGER,"
+ Constants.KEY_PRICE + " TEXT,"
+ Constants.KEY_QTY_NUMBER + " INTEGER,"
+ Constants.KEY_DESCRIPTION + " TEXT,"
+ Constants.KEY_DATE_NAME + " LONG);";
db.execSQL(CREATE_FOOD_TABLE);
}
Here is the code for the hasObject function:
public boolean hasObject(String foodCode) {
SQLiteDatabase db = getWritableDatabase();
String selectString = "SELECT * FROM " + Constants.TABLE_NAME + " WHERE " + Constants.KEY_DESCRIPTION + " =?";
// Add the String you are searching by here.
// Put it in an array to avoid an unrecognized token error
Cursor cursor = db.rawQuery(selectString, new String[] {foodCode});
boolean hasObject = false;
if(cursor.moveToFirst()){
hasObject = true;
}
cursor.close();
db.close();
return hasObject;
}
The constants are defined in a seperate class as a static final.
Here is my code where the problem exists.
public void addItem(Item item) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(Constants.KEY_FOOD_ITEM, item.getItemName());
values.put(Constants.KEY_PRICE, item.getPrice_double());
values.put(Constants.KEY_QTY_NUMBER, item.getItemQuantity());
values.put(Constants.KEY_DESCRIPTION, item.getDescription());
values.put(Constants.KEY_DATE_NAME,
java.lang.System.currentTimeMillis());//timestamp of the system
// db.insert(Constants.TABLE_NAME, null, values);
// Log.d("DBHandler", "added Item: ");
//Check if the record already exists in the database
if(!hasObject(item.getDescription())){
//Insert the row
db.insert(Constants.TABLE_NAME, null, values); // This is not running as planned
Log.d("DBHandler", "added Item: ");
}
else {
//Don't insert the row
Log.d("DBHandler", "Item exists");
}
}
As you noticed, I tried running the db.insert function just as it is, without checking if the record already exists in the database, and it works fine.
But if I keep adding records regardless if they are in the database, there will too many duplicates and this messes up the recycler view.
The additem function is being called here:
databaseHandler= new DataBaseHandler(this);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
itemList= new ArrayList<>();
//tempitemlist = new ArrayList<>();
//Get items from Firebase
mfoodRef.addValueEventListener(new ValueEventListener() {
//Will run everytime there is an update to the condition value in the database
//So this will run when the .setValue function runs in the button onClickListener classes
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//tempitemlist.clear();
Iterable<DataSnapshot> databaseMenu = dataSnapshot.getChildren();
for (DataSnapshot data:databaseMenu){
Menu tempMenu = data.getValue(Menu.class);
Item tempItem = new Item(tempMenu.getFoodName(),tempMenu.getFoodCode(),tempMenu.getFoodPrice());
//itemList.add(tempItem);
databaseHandler.addItem(tempItem);
}
}
// In case we run into any errors
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
And yes, I am using the information on my Firebase realtime database to update the contents of the databasehandler in the onDataChange method. Which is why I need to check for duplicates when I am inserting a new record.
EDIT:
This is what I found in the debug log:
Process: com.example.vendorwrecycler, PID: 12882
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.example.vendorwrecycler/databases/foodList
at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:57)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1567)
at android.database.sqlite.SQLiteDatabase.insertOrThrow(SQLiteDatabase.java:1494)
at com.example.vendorwrecycler.data.DataBaseHandler.addItem(DataBaseHandler.java:68)
at com.example.vendorwrecycler.ListActivity.saveItem(ListActivity.java:184)
at com.example.vendorwrecycler.ListActivity.access$500(ListActivity.java:34)
at com.example.vendorwrecycler.ListActivity$4.onClick(ListActivity.java:159)
at android.view.View.performClick(View.java:7125)
at android.view.View.performClickInternal(View.java:7102)
at android.view.View.access$3500(View.java:801)
at android.view.View$PerformClick.run(View.java:27336)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Maybe it might be something to do with the IllegalStateException ?
You are trying to execute operation on a closed DB instance. Try moving SQLiteDatabase db = getWritableDatabase(); inside if statements.
if(!hasObject(item.getDescription())){
//Insert the row
SQLiteDatabase db = getWritableDatabase();
db.insert(Constants.TABLE_NAME, null, values);
Log.d("DBHandler", "added Item: ");
}
else {
//Don't insert the row
Log.d("DBHandler", "Item exists");
}
You closed the connection of DB inside hasObject function.
there's no item description in the tempItem variable.
Item tempItem = new Item(tempMenu.getFoodName(),tempMenu.getFoodCode(),tempMenu.getFoodPrice());
so item.getDescription() must be null or empty
log all the tempItem object variables and make sure none of the variables are null or empty.
hope this helps
Related
I haven't found a good example for updating all data in your SQLite database.
I have a model class called ItemDatabaseModel that contains values such as item_id, category, description, image_url, etc. When updating a list of these ItemDatabaseModels, I am currently updating every attribute one at a time but the end result is that all the data is overwritten with the last item that was updated.
Here is how I am doing my update. I start by iterating through my list of ItemDatabaseModels that I want to update.
for (int i = 0; i < alItemDb.size(); i++) {
dbProvider.update(alItemDb.get(i));
}
The update method that is being called accepts the model and then calls the update method for updating the database.
public void update(ItemDatabaseModel itemDatabaseModel) {
mProvider.update(itemDatabaseModel, ItemSchema.CATEGORY + " =?", new String[]{itemDatabaseModel.category});
mProvider.update(itemDatabaseModel, ItemSchema.ITEM_ID + " =?", new String[]{itemDatabaseModel.itemId});
mProvider.update(itemDatabaseModel, ItemSchema.DESCRIPTION + " =?", new String[]{itemDatabaseModel.description});
mProvider.update(itemDatabaseModel, ItemSchema.IMAGE_URL_1 + " =?", new String[]{itemDatabaseModel.imageUrl1});
mProvider.update(itemDatabaseModel, ItemSchema.IMAGE_URL_2 + " =?", new String[]{itemDatabaseModel.imageUrl2});
mProvider.update(itemDatabaseModel, ItemSchema.IMAGE_URL_3 + " =?", new String[]{itemDatabaseModel.imageUrl3});
}
Then I update the database:
public void update(T model, String where, String[] whereArgs) {
mDatabase = getWritableDatabase();
if (mDatabase.isOpen()) {
mDatabase.update(model.getTableName(), model.getContentValues(), where, whereArgs);
}
}
From my observation the issue could be the where, but I do not know how to adjust it. Also, is there a better implementation for updating a list of new data?
For those of you wondering what the necessary corrections were to resolve this issue, here are the final changes.
Iterate through your list of items to udpate
for (int i = 0; i < alItemDb.size(); i++) {
dbProvider.update(alItemDb.get(i));
}
Pass in your entire model so that you can later retrieve contentValues from it, and perform your update based upon an unique identifier (id, email, ect)
public void update(ItemDatabaseModel itemDatabaseModel) {
mProvider.update(itemDatabaseModel, ItemSchema.ITEM_ID + " = ?",
}
Update your database
public void update(T model, String where, String[] whereArgs) {
mDatabase = getWritableDatabase();
if (mDatabase.isOpen()) {
mDatabase.update(model.getTableName(), model.getContentValues(), where, whereArgs);
}
}
I'm having an issue with rawQuery. Sometimes it doesn't find an item by its ID while other times it does.
My logic is as follows: I retrieve some data from a webservice. I sync it with the local database and then I return the response to the user from the local database (not from what I receive from the webservice).
My issue is that when I sync the data, the rawQuery retrieves the item from the local database to see if I already have it. If it can't find it, it inserts a new row, if it does, then it just updates it. Here's the method:
#Override
public ShopItem syncShop(ShopItem remoteShop) {
SQLiteDatabase db = DbManager.instance().openDatabase();
Cursor c = db.rawQuery(
DBContract.SHOPS.GET_SHOP_BY_REMOTE_ID,
new String[]{String.valueOf(remoteShop.remote_id)});
if (c.moveToFirst()) {//Item(s) exist
ShopItem localShop = new ShopItem(c);
if (localShop.updateDate == null || !localShop.updateDate.equals(remoteShop.updateDate)) {
localShop.sync(remoteShop);
db.update(DBContract.SHOPS.TABLE_NAME, localShop.getDbContentValues(),
DBContract.SHOPS.REMOTE_OBJ_ID + " = ?", new String[]{localShop.remote_id});
}
remoteShop = localShop;
} else {//No item(s) found
ContentValues values = remoteShop.getDbContentValues();
long rowId = db.insert(DBContract.SHOPS.TABLE_NAME, null, values);
if (rowId != -1) {
remoteShop.local_id = String.valueOf(rowId);
}
}
free(db, c);
return remoteShop;
}
I tried calling this method (just for debug purposes) 20 times in a row, in rapid succession and some of those times c.moveToFirst() is false, so it inserts a new item instead of updating it.
On the UI-thread, the 20 calls work properly, they don't create new items ... BUT I've recently moved the calls to the above method to non-UI threads and it's since then that it started acting up. I'm 100% positive it's an issue of multi-threading, but I can't figure out where the problem is.
Here's the DbManager
/*package*/ class DbManager {
private int mOpenCounter;
private static DbManager mInstance;
private static DbHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(DbHelper helper) {
if (mInstance == null) {
mInstance = new DbManager();
mDatabaseHelper = helper;
}
}
public static synchronized DbManager instance() {
if (mInstance == null) {
throw new IllegalStateException(DbManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return mInstance;
}
public synchronized SQLiteDatabase openDatabase() {
mOpenCounter++;
if (mOpenCounter == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized void closeDatabase() {
mOpenCounter--;
if (mOpenCounter == 0) {
// Closing database
mDatabase.close();
}
}
}
Does anyone know what the problem might be?
Cheers!
You should never use the same SQLiteDatabase object from multiple threads, because one connection can have only one transaction.
Furthermore, you must wrap the query/update/insert calls into a single transaction to prevent other threads from modifying the database in between.
Yeah. So it wasn't a multi-threading issue. Well, it was, but wasn't really.
I had this query:
static final String GET_VISIT_BY_REMOTE_ID = "SELECT " + TABLE_NAME + ".*" + ", " +
SHOPS.TABLE_NAME + "." + SHOPS.NAME + " AS " + SHOPS.TABLE_NAME + "_" + SHOPS.NAME +
" FROM " + TABLE_NAME +
" INNER JOIN " + SHOPS.TABLE_NAME + " ON " + TABLE_NAME + "." + REMOTE_SHOP_ID + "=" + SHOPS.TABLE_NAME + "." + SHOPS.REMOTE_OBJ_ID +
" WHERE " + TABLE_NAME + "." + REMOTE_OBJ_ID + " = ?";
The problem is is that while the above query was running, another query that synched the shops was running as well, in fact multiple queries. One that cleared the shops and one that synched the data back. So from time to time, even if the delete+sync_shop queries were immediately after one another, being in separate threads, the delete query would run first, then the above query (which didn't find shops, so the query failed, even if it had visits to return), and only then it repopulated the shops with data.
Basically I was deleting data, checking if it existed (from another thread) then populated the data, but it would be too late as the other thread finished already.
I have an app that gets JSON data from a server. I then put the parsed data into the android SQLite database and use the data as needed. This all works great, however, I am unable to find a method to update the whole table.
The scenario would be that this Json Data feed gets updated every week on the server. I have two Questions:
What am I missing or what is the method for updating the SQLite table? (currently this just duplicates the data)
public void updateTable(Product product){
SQLiteDatabase db = this.getWritableDatabase();
try{
ContentValues values = new ContentValues();
values.put(KEY_TYPE_NAME, product.getmProductTypeName());
// more columns here...
db.update(TABLE_NAME, values, null,null);
db.close();
}catch(Exception e){
Log.e("error:",e + "in updateData method")
}
What is an ideal system for updating the data? Would it be silly and bad practice to just call the method when connected to internet?
Related Code in "Main Activity":
handler = new DBHandler(this);
NetworkUtils utils = new NetworkUtils(MainActivity.this);
if (handler.getProductCount() == 0 && utils.isConnectingToInternet()) {
new JsonDataParse().execute();
}`
Related Code "DBhandler" Activity:
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DROP_TABLE);
onCreate(db);
}
String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + "(" + KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + KEY_TYPE_NAME + " TEXT" + ")"
That is basically my CREATE TABLE String format. I just condensed to because it has 16 columns.
This is the code I added to only delete the stored data only if there was data:
if(handler.getProductCount() == 0) {
}else{
handler.deleteData();
}
Then I just just added the delete the method as suggested:
public void deleteData() {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_NAME, "1", null);
}
I'm not sure what you mean by "update the whole table". It sounds to me like you just need to delete the data in the table and then use your current method to add the new data. To delete the contents you can use:
db.delete(TABLE_NAME, "1", null);
Then call your existing method to re-populate the table from the server.
What is an ideal system for updating the data? Would it be silly and bad practice to just call the method when connected to internet?
No it wouldn't be bad practice. That makes sense, as you'll only be able to reach the server if you're connected to the internet anyway.
I am new in android app developement. I tried to insert values to SQLite database through the below code;
public class cashbook extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SQLiteDatabase db;
db = openOrCreateDatabase(
"cashbookdata.db"
, SQLiteDatabase.CREATE_IF_NECESSARY
, null
);
final String Create_CashBook =
"CREATE TABLE CashData ("
+ "id INTEGER PRIMARY KEY AUTOINCREMENT,"
+ "Description TEXT,"
+ "Amount REAL,"
+ "Trans INTEGER,"
+ "EntryDate TEXT);";
db.execSQL(Create_CashBook);
final String Insert_Data="INSERT INTO CashData VALUES(2,'Electricity',500,1,'04/06/2011')";
db.execSQL(Insert_Data);
It shows error on emulator - The application CashBook has stopped unexpectedly.
The database and table created , but the value insertion is not working.
Please help me to resolve this issue.
Thanks.
Seems odd to be inserting a value into an automatically incrementing field.
Also, have you tried the insert() method instead of execSQL?
ContentValues insertValues = new ContentValues();
insertValues.put("Description", "Electricity");
insertValues.put("Amount", 500);
insertValues.put("Trans", 1);
insertValues.put("EntryDate", "04/06/2011");
db.insert("CashData", null, insertValues);
okk this is fully working code edit it as per your requirement
public class TestProjectActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SQLiteDatabase db;
db = openOrCreateDatabase( "Temp.db" , SQLiteDatabase.CREATE_IF_NECESSARY , null );
try {
final String CREATE_TABLE_CONTAIN = "CREATE TABLE IF NOT EXISTS tbl_Contain ("
+ "ID INTEGER primary key AUTOINCREMENT,"
+ "DESCRIPTION TEXT,"
+ "expirydate DATETIME,"
+ "AMOUNT TEXT,"
+ "TRNS TEXT," + "isdefault TEXT);";
db.execSQL(CREATE_TABLE_CONTAIN);
Toast.makeText(TestProjectActivity.this, "table created ", Toast.LENGTH_LONG).show();
String sql =
"INSERT or replace INTO tbl_Contain (DESCRIPTION, expirydate, AMOUNT, TRNS,isdefault) VALUES('this is','03/04/2005','5000','tran','y')" ;
db.execSQL(sql);
}
catch (Exception e) {
Toast.makeText(TestProjectActivity.this, "ERROR "+e.toString(), Toast.LENGTH_LONG).show();
}}}
Hope this is useful for you..
do not use TEXT for date field may be that was casing problem still getting problem let me know :)Pragna
You'll find debugging errors like this a lot easier if you catch any errors thrown from the execSQL call. eg:
try
{
db.execSQL(Create_CashBook);
}
catch (Exception e)
{
Log.e("ERROR", e.toString());
}
I recommend to create a method just for inserting and than use ContentValues.
For further info https://www.tutorialspoint.com/android/android_sqlite_database.htm
public boolean insertToTable(String DESCRIPTION, String AMOUNT, String TRNS){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("this is",DESCRIPTION);
contentValues.put("5000",AMOUNT);
contentValues.put("TRAN",TRNS);
db.insert("Your table name",null,contentValues);
return true;
}
public class TestingData extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SQLiteDatabase db;
db = openOrCreateDatabase(
"TestingData.db"
, SQLiteDatabase.CREATE_IF_NECESSARY
, null
);
}
}
then see this link link
okkk you have take id INTEGER PRIMARY KEY AUTOINCREMENT and still u r passing value...
that is the problem :)
for more detail
see this
still getting problem then post code and logcat
Since you are new to Android development you may not know about Content Providers, which are database abstractions. They may not be the right thing for your project, but you should check them out: http://developer.android.com/guide/topics/providers/content-providers.html
I see it is an old thread but I had the same error.
I found the explanation here:
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
void execSQL(String sql)
Execute a single SQL statement that is NOT a SELECT or any other SQL statement that returns data.
void execSQL(String sql, Object[] bindArgs)
Execute a single SQL statement that is NOT a SELECT/INSERT/UPDATE/DELETE.
I have built a database helper class with an open() method and extended sqlite helper with onCreate() overridden. (shown below). Despite all of this, I am getting 'SQLiteException, no such table' error. I do not understand, why is the openHelper not helping?
public void open() {
try{
db = openHelper.getWritableDatabase();
} catch (SQLiteException e) {
db = openHelper.getReadableDatabase();
}
}
//other stuff
public static final String database_create = "create table " + database_table + " (" + primary_key + " integer primary key autoincrement, "
+ company_column + " text not null, " + product_column + " text not null);";
#Override
public void onCreate(SQLiteDatabase _db) {
_db.execSQL(database_create);
}
the following code is meant to insert an entry temporarily, because the database cannot be empty for other reasons. It seems to execute perfectly, yet the last bit of code, which comes after is what throws the error
CompanyAndProductDatabaseAdapter cpdAdapter = new CompanyAndProductDatabaseAdapter(this);
cpdAdapter.open();
errorguard = cpdAdapter.insertPair("Loading", "...");
cpdAdapter.close();
//other stuff
cpdAdapter.open();
Cursor cursor = cpdAdapter.getAllPairsCursor(); //error here
cursor.requery();
startManagingCursor(cursor);
I don't know why you implemented a open-method, also the database_create is not what it should be.
I assume the first code is part of CompanyAndProductDatabaseAdapter.
Take a look here:
Android - Sqlite database method undefined fot type
That's almost all you need to create/get a DB with inherted SQLiteOpenHelper.
Your problem is this function:
db = openHelper.getWritableDatabase();
db = openHelper.getReadableDatabase();
First: check your path/name of the database is correct. It can create a default database, an empty database ( no tables, no nothing) if the database is not found.
Second: try to open your database this way:
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE); // or OPEN_READONLY, depending on situation.