Android SQLite Transaction, two datasources - android

im having doubts on how to procede with database manipulation using Begin Transaction
I have 2 data source objects: ClientDataSource and MovementsDataSource
each of theres has its own SQLitedabase object which is set with the Open() method with sets its database private Object with a Writable Database.
Now, i have to send the Records from the Movements Table to a server and then ask for the updated Clients from the same server.
Inside my class i first Send the new Movements, then if sucessful, delete all clients from the SQLite, and finally retrieve the new Updated Clients from the server and insert them into the database.
each data source has a Get() method for obtainting their respective Database Object, BUT it is in fact the same database they are operating and I am not sure of how to use the BaginTransction, Endtransaction methods to ensure data consistency
this is the code from the Async Task
#Override
protected Boolean doInBackground(String... url) {
try {
clientDataSource.open();
movementDataSource.open();
} catch (SQLException e) {
//Treat SQL Exception
}
try {
if(sendMovements()) {
clientDataSource.deleteAllClients();
}
updateDatabase(url[0]);
} catch (JSONException e) {
//Treat Json Exception
} catch (IOException e) {
//Treat IOException
}
return true;
}

i have to give one example then how begintransaction works...
public static void Insert(ArrayList<Model_CategoryMaster> categoryMasters) {
SQLiteDatabase sqldb = EGLifeStyleApplication.sqLiteDatabase;
sqldb.beginTransaction();
for (Model_CategoryMaster model : categoryMasters) {
ContentValues values = new ContentValues();
values.put(CATEGORY_ID, model.Category_Id);
values.put(CATEGORYNAME, model.CategoryName);
values.put(DESCRIPTION, model.Description);
values.put(ISACTIVE, model.IsActive);
values.put(CREATEDON, model.CreatedOn);
values.put(CREATEDBY, model.CreatedBy);
values.put(UPDATEDON, model.UpdatedOn);
values.put(UPDATEDBY, model.UpdatedBy);
values.put(ISDELETED, model.IsDeleted);
values.put(DELETEDON, model.DeletedOn);
values.put(DELETEDBY, model.DeletedBy);
values.put(PK_CATEGORYMASTER, model.PK_CategoryMaster);
if (!CommonMethod.CheckIsDataAlreadyInDBorNot(Table_Name,
CATEGORY_ID, model.Category_Id)) {
sqldb.insert(Table_Name, null, values);
} else {
sqldb.update(Table_Name, values, "Category_Id=?",
new String[] { model.Category_Id });
}
}
sqldb.setTransactionSuccessful();
sqldb.endTransaction();
}// End insert method
begin transaction means data will get arraylist then db will open one time and sql.begintansaction() use to get faster database operation.because db is not open or close more times.

Related

How to display SQLException while insert or update operation

I am working with SQLite android where I am using an sqLiteDatabase.insert function to insert customers and sqLiteDatabase.update to update customers and getting the result which is always a long variable, When result is -1, I know the database operation was not successful but I want to display the actual SQLException that occurred. As the return type of these methods is long so they only print the exception in log. Is there any way to get the message of SQLException and display in Toast etc.
I tried to use try catch to catch the exception and show message but when exception occurs it doesnt go to catch block. Below is my code
public void insertData(SQLiteDatabase sqLiteDatabase, Context context, ContentValues values, DatabaseOperation dbOperation, DatabaseOperationCallback dbOperationCallback) {
JSONObject resultObj = new JSONObject();
try {
if (dbOperation != null) {
sqLiteDatabase.insert(dbOperation.getTableName(), null, values);
}
}
} catch (SQLException ex) {
ex.printStackTrace();
//showing in toast; this is not triggered
} catch (Exception ex) {
ex.printStackTrace();
//showing in toast; this is not triggered
}
}
Please see below picture for reference. Any help will be appreciated
You can use
e.printStackTrace(); // (directly to the log)
or
e.getMessage(); // returns message as a String
or others as per the Java Exception Class.
However, using the insertOrThrow method, is probably the method that you want to use.

Replacing database with another database at runtime.

I have two databases, one database is the primary. This primary DB is responsible for holding the current data which is up to date and my secondary DB is populated via a cron job, once the primary DB gets obsolete I want to replace it with the secondary DB via a file operation of just over writing the existing DB and refreshing my views. Is it possible to do this, is there a better way?
So far what I have done is:
public void writeToSD() throws IOException {
File f=new File("/mnt/sdcard/dump.db");
FileInputStream fis=null;
FileOutputStream fos=null;
try{
fis=new FileInputStream(f);
fos=new FileOutputStream("/data/data/com.one.two/databases/Bdr");
while(true){
int i=fis.read();
if(i!=-1){
fos.write(i);
}
else{
break;
}
}
fos.flush();
}
catch(Exception e){
e.printStackTrace();
}
finally{
try{
fos.close();
fis.close();
}
catch(IOException ioe){
System.out.println(ioe);
}
}
How about always using the same database files (let's say dbA, dbB) with two instances of SQLiteOpenHelper and using an utility class like this instead of using raw SQLiteOpenHelper:
class Db {
private SQLiteOpenHelper mPrimaryDb;
private SQLiteOpenHelper mSecondaryDb;
public Db(Context context) {
mPrimaryDb = new MyDbHelper(context, "db_a");
mSecondaryDb = new MyDbHelper(context, "db_b");
}
public SQLiteOpenHelper getPrimaryDb() {
return mPrimaryDb;
}
public SQLiteOpenHelper getSecondaryDb() {
return mSecondaryDb;
}
public void swapDb() {
SQLiteOpenHelper tmp = mPrimaryDb;
mPrimaryDb = mSecondaryDb;
mSecondaryDb = tmp;
// TODO: notify data users that data has changed, cleanup the old primary database, etc.
}
{
If you want to use file operations, renaming the data base files is faster. But during file operations all connections have to be closed before any action.
If insertion is too slow, I would not overwrite the database file. I would generate the new database with a temp name and the same table and view structure. After finishing writing to the temp file I would rename the file to the same name as the invariant part of the old database plus a version number or a timestamp . And in my application I would look periodically for a new version, if found I would close all connections to the old file and open the new database.

How to insert bulk data in android sqlite database using ormlite efficiently

I'm trying to insert 100000 records in android sqlite database at a time. I'm using following two different methods.
private void bulkInsertDataBySavePoint(final List<User> users) {
log.debug("bulkInsertDataBySavePoint()");
DatabaseConnection conn = null;
Savepoint savepoint = null;
try {
conn = userDao.startThreadConnection();
savepoint = conn.setSavePoint("bulk_insert");
for (User user : users) {
userDao.create(user);
}
} catch (SQLException e) {
log.error("Something went wrong in bulk Insert", e);
} finally {
if (conn != null) {
try {
conn.commit(savepoint);
userDao.endThreadConnection(conn);
} catch (SQLException e) {
log.error("Something went wrong in bulk Insert", e);
}
}
}
}
And
private void bulkInsertDataByCallBatchTasks(final List<User> users) {
log.debug("bulkInsertDataByCallBatchTasks()");
try {
userDao.callBatchTasks(new Callable<Void>() {
#Override
public Void call() throws Exception {
for (User user : users) {
userDao.create(user);
}
return null;
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
Both methods work fine. On average they take 140 seconds and take 60-65% CPU which is not ok, I think.
The idea is, I have to consume an api which will provide json data. I have to parse that json data and then insert into sqlite database for offline usage.
I'm looking for an efficient way to solve this issue.
Any thought?
I'm trying to insert 100000 records in android sqlite database at a time... On average they take 140 seconds and take 60-65% CPU which is not ok in my opinion.
Unfortunately I don't have an easy answer for you. You may have to do this sort of insert directly using raw SQL to achieve faster performance on the limited Android CPU. Once you have the data inserted then you can turn to ORMLite to query or manipulate the data faster.
I've had the same problem, and found a reasonable workaround. This took insert time from 2 seconds to 150ms:
final OrmLiteSqliteOpenHelper myDbHelper = ...;
final SQLiteDatabase db = myDbHelper.getWritableDatabase();
db.beginTransaction();
try{
// do ormlite stuff as usual, no callBatchTasks() needed
db.setTransactionSuccessful();
}
finally {
db.endTransaction();
}
Hrm. Good idea #FarrukhNajmi. I've just added it to trunk. It will be in version 4.49.
#Gray Is it still unstable? when can we see it in maven?
And if com.j256.ormlite.dao.ForeignCollection#addAll make only one request it would be nice too.

Ormlite ObjectCache returning old data

I'm using Ormlite on Android and with the ObjectCache enabled, I get old data back after updating the table with an UpdateBuilder and a ColumnExpression. I have read through the doc and it does not warn against using the UpdateBuilder with the cache enabled.
The settings table should have just 1-5ish rows max. The updateColumnExpression seems like an easy way to allow only one of the rows to be true.
Is this the expected behavior?
public void setActiveSetting(String id)
{
try {
UpdateBuilder<Settings, Integer> updateBuilder2 = getHelper().getSettingsDao().updateBuilder();
updateBuilder2.updateColumnExpression("active", "id = " + id );
updateBuilder2.update();
} catch (SQLException e){
e.printStackTrace();
}
}
And this is the call that returns the outdated data:
public List<Settings> getSettings()
{
List<Settings> settings = null;
try {
settings = getHelper().getSettingsDao().queryForAll();
} catch (SQLException e) {
e.printStackTrace();
}
return settings;
}
And the settings DAO:
public Dao<Settings, Integer> getSettingsDao()
{
if (null == settingsDao) {
try {
settingsDao = getDao(Settings.class);
settingsDao.setObjectCache(true);
} catch (java.sql.SQLException e) {
e.printStackTrace();
}
}
return settingsDao;
}
Disabling the ObjectCache does return the correct data, but this data is fetched quite frequently, so I'd like to keep it.
Thanks
Is this the expected behavior?
Unfortunately, yes. If you had updated the object using dao.update(...); then the cache would know that the object needed to be refreshed. By using the UpdateBuilder to make mass changes to the table, there is no way for the cache to know which objects were affected.
You will need to clear the cache after your call to the UpdateBuilder finishes.

Performance SQLite Issue - Can a codechange speed up things?

I use the following code to add rows to my database :
public void insert(String kern, String woord) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KERN, kern);
values.put(WOORD, woord);
db.insertOrThrow(TABLE_NAME, null, values);
return;
Currently, I'm invoking this insert() 3.455 times, to add all words to the database, using : insert("Fruits", "Banana"); It takes forever.
How can I change this code to work faster? I'm thinking in the line of foreach, but don't know how to implement.. Thanks!
/Edit; The solution provided by #hovanessyan works and will do the job. AND.. note that if you have a lot of lines that have to be put in, you might be confronted with the method exceeds max byte limit error. In that case, review the other solution, that suggests packing the database in the actual .APK file.
You can wrap-up those inserts into transaction.
db.beginTransaction();
try {
// do all the inserts here
//method call here, that does 1 insert; For example
addOneEntry(kern,woord);
...
db.setTransactionSuccessful();
} catch (SQLException e) {
//catch exceptions
} finally {
db.endTransaction();
}
private void addOneEntry(String kern, String woord) {
//prepare ContentValues
//do Insert
}
You can use bulkInsert:
ContentValues[] cvArr = new ContentValues[rows.size()];
int i = 0;
for (MyObject row : rows) {
ContentValues values = new ContentValues();
values.put(KERN, myObject.getKern());
values.put(WOORD, myObject.getWoord);
cvArr[i++] = values;
}// end for
resolver.bulkInsert(Tasks.CONTENT_URI, cvArr);
Using the tips of both hovanessyan and Damian (remind me to rep+1 you as soon as I reach 15 ;), I came up with the following solution:
For relatively small databases (<1,5Mb)
I created the database using SQLite Database Browser, and put it in my Assets folder.
Then, the following code copies the database to the device, if it's not already there:
boolean initialiseDatabase = (new File(DB_DESTINATION)).exists();
public void copyDB() throws IOException{
final String DB_DESTINATION = "/data/data/happyworx.nl.Flitswoorden/databases/WoordData.db";
// Check if the database exists before copying
Log.d("Database exist", "" + initialiseDatabase);
Log.d("Base Context", "" + getBaseContext());
if (initialiseDatabase == false) {
// Open the .db file in your assets directory
InputStream is = getBaseContext().getAssets().open("WoordData.db");
// Copy the database into the destination
OutputStream os = new FileOutputStream(DB_DESTINATION);
byte[] buffer = new byte[1024];
int length;
while ((length = is.read(buffer)) > 0){
os.write(buffer, 0, length);
}
os.flush();
os.close();
is.close();
}}
In my app, a portion of the database is User-customizable.
I call the code above in onStart() with :
try {
copyDB();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
So, when the user presses "reset database to standard" (in preferences screen), I just set the Boolean initialiseDatabase to "false" and wait for the user to go back to the main activity. (thus calling onstart and copying the original database).
I tried to call the Activity.copyDB() from the preferences.java. It's neater, because it doesn't require the user to go back to the main activity to rebuild the database. However, I get an error about not being able to call static references to non-static methods. I don't understand that, but will look into it.

Categories

Resources