is it save to begin and commit a transaction by retrieving the database two times with getWriteableDatabase(). I have this scenario if I want to use two DAOs in one transaction
Something like this:
SqliteDatabase db = userDao.getWriteableDatabase();
try{
userDao.insert(firstname, lastname);
addressDao.insert(street);
// commit
db.setTransactionSuccessful();
db.endTransaction();
catch(Exception e){
// rollback
db.endTransaction();
}
Where:
class UserDao extends Dao {
public void insert(String firstname, String lastname){
SqliteDatabase db = getWriteableDatabase();
...
db.insertOrThrow(...);
}
}
class AdressDao extends Dao {
public void insert(String street){
SqliteDatabase db = getWriteableDatabase();
...
db.insertOrThrow(...);
}
}
getWriteableDatabase() is just a shortcut for SQLiteOpenHelper.getWriteableDatabase()
So is it safe to work in this way in a transaction or does getWriteableDatabase() returns another instance and therefore the transaction is no longer in the right scope?
If so i could pass the SqliteDatabase as parameter to the DAO, like
SqliteDatabase db = userDao.getWriteableDatabase();
try{
userDao.insert(db, firstname, lastname);
addressDao.insert(db, street);
// commit
db.setTransactionSuccessful();
db.endTransaction();
catch(Exception e){
// rollback
db.endTransaction();
}
getWritableDatabase() creates the instance the first time that gets called and cache the returned value. So you're not opening the db twice and yes, it's safe.
Related
I'm practicing android, I was able to insert using db.insert() without the use of getWritableDatabase() why is that? I thought we need db = getWritableDatabase(); before we can insert to the database
private SQLiteDatabase db;
#Override
public void onCreate(SQLiteDatabase db) {
this.db = db;
//some table creation
db.execSQL(TABLE);
fillQuestionsTable();
}
private void insertQuestion(Question question){
ContentValues cv = new ContentValues();
//some code
db.insert(QuestionsTable.TABLE_NAME, null, cv);
}
I thought it should be like this?
private void insertQuestion(Question question){
db = getWritableDatabase();
ContentValues cv = new ContentValues();
//some code
db.insert(QuestionsTable.TABLE_NAME, null, cv);
}
here's my implementation:
private void fillQuestionsTable(){
Question q1 = new Question("Programming, Easy: A is correct", "A", "B", "C",
1, Question.DIFFICULTY_EASY, Category.PROGRAMMING);
insertQuestion(q1);
}
your code works, because you are storing reference to db object in onCreate and then re-using this reference further. that should work in most of common cases, but getWritableDatabase() and getReadableDatabase() methods are there for a reason - you should use them in your insert/update/remove methods just like you posted. For safer, more reliable access in future app releases when there may/probably will be more code, more tables and more complicated lifecycle of app. and get rid of private SQLiteDatabase db; declaration on top of your SQLiteOpenHelper, use only locally fetched (with above methods) database object
I'm creating a forum application and I currently if I delete a thread I'm deleting all threads.
Is there a good method or query to check if the UserId == ThreadId?
My current code:
public void deleteThread() {
SQLiteDatabase db = this.getWritableDatabase();
// Delete All Rows
db.delete(TABLE_THREAD, null, null);
db.close();
Log.d(TAG, "Deleted all Thread info from sqlite");
}
You need to pass correct value to the well-documented delete method to narrow down the scope of deletion to a subset of all entries in the DB table.
public void deleteThreadById(String threadId) {
SQLiteDatabase db = this.getWritableDatabase();
String whereClause = "threadId = " + threadId;
db.delete(TABLE_THREAD, whereClause, null);
db.close();
}
Deleting all threads of a given user via their userId would be similar but probably doesn't make sense in a forum software.
This is how SQL works in general and it's a bit scary you started development without familiarising yourself with the very basics.
Something like this;
public void deleteThread(String threadName) {
SQLiteDatabase db = this.getWritableDatabase();
try {
db.delete(MYDATABASE_TABLE, "name = ?", new String[]{threadName});
} catch (Exception e) {
e.printStackTrace();
} finally {
db.close();
}
}
Something long these lines, querying database to find the specific row that has column which matches the parameter.
For example to delete a row which the name column is "Hello World";
deleteThread("Hello World");
I am trying to work out how best to implement an SQLite database in my Android app. Advice from this answer suggests using SQLiteOPenHelper, which I already have, but recommends only having 1 instance of it.
In my app I have an abstract BaseDAO class which extends SQLiteOpenHelper and creates the database tables if necessary. E.g:
public abstract class BaseDAO extends SQLiteOpenHelper{
public BaseDAO(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_TABLE = "CREATE TABLE " + ...
db.execSQL(CREATE_TABLE);
}
I have other DAO classes set up to access the database, which all extend this BaseDAO class, e.g. a MessageDAO class for putting message objects into the db.
public class MessageDAO extends BaseDAO {
public MessageDAO(Context context) {
super(context, DBContract.DB_NAME, null, DBContract.DB_VERSION);
}
public long addMessage(Message msg) throws Exception {
ContentValues values = new ContentValues();
values.put(DBContract.MessagesTable.COLUMN_TEXT, msg.getText());
values.put(DBContract.MessagesTable.COLUMN_FILE, msg.getFile());
values.put(DBContract.MessagesTable.COLUMN_TYPE, msg.getType());
values.put(DBContract.MessagesTable.COLUMN_TIMESTAMP, msg.getTimeStamp());
SQLiteDatabase db = this.getWritableDatabase();
long rowId = db.insert(DBContract.MessagesTable.TABLE_NAME, null, values);
db.close();
if (!(rowId > 0)) {
throw new Exception("Error inserting message into DB");
}else{
return rowId;
}
}
}
And a ContactDAO class to put Contact objects into the db:
public class ContactDAO extends BaseDAO {
public ContactDAO(Context context) {
super(context, DBContract.DB_NAME, null, DBContract.DB_VERSION);
}
public long addContact(Contact contact) throws Exception {
ContentValues values = new ContentValues();
values.put(DBContract.ContactTable._ID, contact.getId());
values.put(DBContract.ContactTable.COLUMN_CONTACT_NAME, contact.getContactName());
values.put(DBContract.ContactTable.COLUMN_CONTACT_TYPE, contact.getContactType());
SQLiteDatabase db = this.getWritableDatabase();
long rowId = db.insert(DBContract.ContactTable.TABLE_NAME, null, values);
db.close();
if (rowId <= 0) {
throw new Exception("Error inserting contact into DB");
} else {
return rowId;
}
}
}
My question is: should I refactor this so that all my DAO classes are put together into one class (and make that one class a singleton), or is this setup generally OK? It's really hard to find the right information on how to do this online as there seems to be multiple ways to handle db's in Android.
Too much OOP. There is no reason that all your DAO classes should derive from the open helperclass.
Your DAO classes correspond more or less to tables, so there is a N:1 relationship between DAO classes and the database object.
This means that the DAO classes should use a common instance of the open helper class (or that everything should be put into a single class, but you probably don't want that).
i am working on sqlite insertion using contentvalues with transaction . The following code does not generate any exception however the data is not inserted.
Did i miss somethings ? Thanks.
public boolean addRecord(Rec rec) {
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
ContentValues values = new ContentValues();
values.put(KEY_ID, rec.get());
// Inserting Row
try {
db.insertOrThrow(TABLE_RECORDS, null, values);
} catch (SQLException e) {
e.printStackTrace();
}
db.endTransaction();
db.close();
return true;
}
After calling beginTransaction, you must call setTransactionSuccessful to ensure that the transaction gets committed. Without that call, any changes in the transaction are rolled back.
db.beginTransaction();
try {
...
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
This particular construction ensures that any exception in the actual database code will result in an rollback.
(If you get an exception, it is a bad idea to just swallow it; the entire function must fail.)
So I have a database, SQLiteDatabase db I am writing a couple private methods in my manager class that will be called by a public method:
public void updateData (MakeabilityModel newData){
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
try {
reWriteSVTable(db, list);
db.setTransactionSuccessful();
} catch (Exception e){
//TODO through rollback message?
e.printStackTrace();
} finally {
db.endTransaction();
}
}
//Private Methods
private void clearTable(SQLiteDatabase db, String table){
db.delete(table, null, null);
}
private void reWriteSVTable(SQLiteDatabase db, List<MakeabilityLens> lenses){
clearTable(db, singleVision);
ContentValues cv;
for(int i=0; i<lenses.size(); i++){
cv = new ContentValues();
cv.put(colScreenID, hsID);
cv.put(colIconID, id);
cv.put(colRank, hsTotal);
db.insert(isLookUp, colID, cv);
}
}
My question is this.. i want to be able to throw sql exceptions back to the public method so that if there is an exception, it will kill the transaction and rollback ALL data..
it appears that using delete() and insert() methods are cleaner than execSQL() but don't throw sqlExceptions. execSQL() on the other hand does? do i need to uses execSQL and how do I insure that should it throws an exception in any of the private methods that it will catch it and roll it back in the private method
first of all execSQL() throws an exception if the sql string is not valid. that is the exception is on the sql string syntax NOT the sql operation. that is, it will not throw an exception if the sql statement is valid but the operation failed (because of a constraint for example).
So ..
basically the only difference between execSQL() and delete() is that delete() returns the number of rows affected (in your case, the number of deleted rows), but execSQL() doesn't.
Note:
for delete() to return the number of rows affected, you have to pass any value other than null in the where clause parameter. In your case, pass "1".