call dao in another for sqlite transaction - android

i have 2 dao and i need to call method between them
I did something like this but it s really bad because it does a infinite loop and it's does not respect quality standard
What is the proper way to do this please ?
Thank you very much
DAO 1
public class InfractionDAO {
private SQLiteDatabase database;
private SqLiteManager dbHelper;
private OffenderDAO offenderDAO;
public InfractionDAO(Context context) {
dbHelper = new SqLiteManager(context);
offenderDAO= new OffenderDAO (context);
}
List<Infractions> getInfractions(int id) {
offenderDao.getOffender(id);
}
}
DAO 2
public class OffenderDAO {
private SQLiteDatabase database;
private SqLiteManager dbHelper;
private InfractionDAO infractionDAO;
public OffenderDAO (Context context) {
dbHelper = new SqLiteManager(context);
infractionDAO = new InfractionDAO(context);
}
Offender getOffender(int id) {
infractionDAO.getInfractions(id);
}
}

What I would do, is the following:
Solution 1.
create a base class(maybe make abstract?) for both, where they share the common things and in each of the separate classes, offernder and infraction put the different stuff.
Solution 2.
I would create a singleton class that in the constructor initializes both.
like in the following linke:
http://pastebin.com/84g9SgFT

Related

Use local db when testing with Robolectric 3 and ORMLite

I am working on an Android app that uses OrmLite to connect to the SQLite db.
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
...
private static final String DATABASE_NAME = "db.sqlite";
private static DatabaseHelper helper = null;
private DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, BuildConfig.DATABASE_VERSION);
}
public static synchronized DatabaseHelper getHelper(Context context) {
if (helper == null) {
helper = new DatabaseHelper(context);
}
return helper;
}
}
To fetch data from the db, I have some helper classes, they use some DAO.
public class AccountsDBHelper {
public List<Account> getAllAccounts(Context context) {
DatabaseHelper dbHelper = DatabaseHelper.getHelper(context);
Dao<Acount, Integer> daoAccounts = dbHelper.getAccountsDao();
...
...
...
}
}
I have in place Robolectric 3 to test my code, but I am having hard time to understand how to use together Robolectric with ORMLite.
My idea is to have a mock database.sqlite in assets, following the same structure as the one I have in production.
This database will be prefilled with data from test accounts, and use that for all my tests.
For example, if I want to test the ProductsProvider class, I should do:
#RunWith(MyTestRunner.class)
public class AccountsDBHelperTest {
#Test
public void testGetAllAccounts() {
List<Accounts> accounts= AccountsDBHelper.getAllAccounts(getTestContext());
assertNotNull(accounts);
assertFalse(accounts.isEmpty());
}
}
Notice that AccountsDBHelper.getAllAccounts() will use the DatabaseHelper, which will use the db in the Android app assets, and not my local production database file. How can I modify my code of the tests to have them using a local db added as an asset ? Without touching the real code of the app? Any help will be very welcome, thank you...
The missing link was to point to the path of the the local database
String dbPath = RuntimeEnvironment.application.getPackageResourcePath() + DB_FOLDER + dbName;

Use mock db when testing with Robolectric and ORMLite

I am working on an Android app that uses OrmLiteSqliteOpenHelper to connect to the SQLite db.
public class MyDatabaseHelper extends OrmLiteSqliteOpenHelper {
...
private static final String DATABASE_NAME = "mydb.sqlite";
private static MyDatabaseHelper helper = null;
private MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, BuildConfig.DATABASE_VERSION);
}
public static synchronized MyDatabaseHelper getHelper(Context context) {
if (helper == null) {
helper = new MyDatabaseHelper(context);
}
return helper;
}
}
To fetch data from the db, I have some provider classes, they use some DAO.
public class ProductsProvider {
public static List<Products> getProducts(Context context) {
MyDatabaseHelper helper = MyDatabaseHelper.getHelper(context);
Dao<Product, String> daoProducts = helperDatabase.getProductDao();
...
...
...
}
}
I have in place Robolectric to test my code, but I am having hard time to understand how to use together Robolectric with ORMLite.
My idea is to have a mock database.sqlite, prefilled with the same structure and data I normally have, and use that for all my tests. For example, if I want to test the ProductsProvider class, I should do:
#RunWith(MyTestRunner.class)
public class ProductsProviderTest extends MyTestCase {
#Test
public void testDb() {
List<Products> products = ProductsProvider.getProducts(getTestContext());
assertNotNull(products);
assertFalse(products.isEmpty());
}
}
Notice that ProductsProvider.getProducts() will use MyDatabaseHelper, which will use the db in the standard location of the Android app, and not my local file.
How can modify my code for the tests to have the tests using a local db added as an asset or a resource, without touching the real code of the app?
For anyone who can't stand the 6-12-second startup times of Robolectric 3 + Gradle, the following works with Android's built-in JUnit4 tests (DbHelper extends OrmLiteSqliteOpenHelper).
#RunWith(AndroidJUnit4.class)
public class DbTest {
private DbHelper dbHelper;
#Before
public void setUp() throws Exception {
Context context = new RenamingDelegatingContext(
InstrumentationRegistry.getTargetContext(), "test_");
dbHelper = new DbHelper(context);
}
}
There are probably many optimisations you could make, but I can run a small, I/O-heavy suite that wipes the DB each time on GenyMotion in 2.5 seconds.
If you haven't been keeping up-to-date with it, I definitely recommend checking out what Google has done with its testing features recently. The intro on the Robolectric site is a flat-out lie at this point.
Here how we are doing it.
We put sample DB file in src/test/res folder
We run next code before DB test:
private void copyTestDatabase( String resourceDBName )
throws URISyntaxException, IOException
{
String filePath = getClass().getResource( resourceDBName ).toURI().getPath();
String destinationPath = new ContextWrapper( Robolectric.application.getApplicationContext() ).getDatabasePath(
DatabaseHelper.DATABASE_NAME ).getAbsolutePath();
Files.copy( new File( filePath ), new File( destinationPath ) );
}
Be careful since these tests are super long

Access SQLite database simultaneously from different threads

Here is my problem. My app starts several threads, each for a particular object to be updated. The update of the object happens with a query to a single database. There is a single database and a single OpenHelper. The behavior of my app suggests me that the calls to the database are non simultaneous as well as I would like. How can I access the same database from different threads simultaneously? If the data for each object are in different tables is more efficient to split the database in several databases, one for each object?
public class SomethingToBeUpdated implements Runnable {
private SQLiteDatabase db;
#Override
public void run() {
db.rawQuery( ... bla bla
}
}
public class MainActivity extends Activity {
private SomethingToBeUpdated[] list = bla bla...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
for( SomethingToBeUpdated x : list ) {
new Thread(x).start();
}
}
}
For the sake of accessing the database in various threads you need to have a Database manager which keeps an object of your database class and pass it to any thread that needs it. In android you cannot access database simultaneously in several threads with different objects. It may just block your UI (the problem i was facing a few days ago).
So to overcome this problem you can use the the database manager i used which is defined as follows:
public class DatabaseManager {
private AtomicInteger mOpenCounter = new AtomicInteger();
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase openDatabase() {
if(mOpenCounter.incrementAndGet() == 1) {
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized void closeDatabase() {
if(mOpenCounter.decrementAndGet() == 0) {
mDatabase.close();
}
}
}
Then you initialize it once like this:
DatabaseManager.initializeInstance(new ChatSQL(c));
And then you can get the database object wherever you want with this syntax:
SQLiteDatabase db = DatabaseManager.getInstance().openDatabase(); //in your methods which are querying the database
With this method your database is now thread safe. Hope this helps.
If the data for each object are in different tables is more efficient to split the database in several databases, one for each object?
No it is not efficient. It has a lot of overheads to define, access, make object and query different databases. And what if you want to join tables? you just cannot.

SQLiteOpenHelper variable initialization/use throughout the app

I have a little concern about how I handle my database in my apps, here is basically what I do:
I have a custom class extending SQLiteOpenHelper that handles all the transactions with the DB.
In my app, I have one single Activity and several Fragments that are created, deleted, hidden or shown during the process.
In every Fragment where I need to modify or access data from the DB, I declare a static variable:
private static DatabaseHandler mDB;
And I initialize it this way on the onCreate() methods:
mDB = DatabaseHandler.getInstance(getActivity());
All this is working, my concern is about the variable itself, is it a good idea to declare it as a static variable in all my custom fragment classes?
I also use the same way a class containing the main parameters of the app using mParams = Parameters.getInstance(getActivity());, should I also declare it as static?
I want to avoid memory leaks and NPE but I am not sure what is the right way to handle that.
FYI, the beginning of my DatabaseHandler class:
public class DatabaseHandler extends SQLiteOpenHelper {
private static DatabaseHandler sInstance = null;
(...)
private Resources mResources;
public static DatabaseHandler getInstance(Context context) {
if (sInstance == null) {
sInstance = new DatabaseHandler(context);
}
return sInstance;
}
private DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mResources = context.getResources();
}
(...)
Thank you
I normally create a SQLiteDatabase static instance in my Application class and access across the app.
So , I have a custom class which returns SQLiteDatabase instance on application create.
This is my Application class
public class MainApplication extends Application {
private static SQLiteDatabase mSQLiteDatabase = null;
#Override
public void onCreate(){
super.onCreate();
SQLiteAsyncTask sqLiteAsyncTask = new SQLiteAsyncTask(getApplicationContext());
mSQLiteDatabase = (SQLiteDatabase) sqLiteAsyncTask.loadInBackground();
}
// method to get the sqlite db instance
public SQLiteDatabase getSQLiteInstance(){
return mSQLiteDatabase;
}
}
Now you can get the SQLiteDatabase instance from Activity or Fragment , by
MainApplication.getSQLiteInstance()

Using singleton class for sharing instance of database between activities?

Hey! I want to use a singleton class, because if I open the database every activity I get "Leak found"( that happens because I open the database even if it is already open ) . I create a singleton class , but I don't know how should I use it.
Here is my class:
package com.ShoppingList;
import com.ShoppingList.databases.DbAdapter;
public class DbManager {
DbAdapter db;
// singleton
private static DbManager instance = null;
private DbManager() {
}
public static DbManager getInstance() {
if (instance == null)
instance = new DbManager();
return instance;
}
public void setinstance(DbAdapter db){
this.db=db;
}
public DbAdapter getinstancedb(){
return db;
}
}
In the first activity I put :
db = new DbAdapter(this);
db.open();
DbManager.getInstance().setinstance(db);
and for the next activity : DbManager.getInstance().getinstancedb(); but I get force close for second activity.
Can anyone help me how to use it? Thanks...
You can extend Application class and create there an instance of DbAdapter. This way it will be shared by all your activities.
Because db has the same context and life cycle of your first activity. Make your methods public and make them do all the setup/teardown necessary to return your desired result.
regarding the leak warning. Are you closing your db manager connection in onDestroy()?

Categories

Resources