I have two database to support two languages for my application.
I want to change database when I change language from settings.
Is it possible?? How can i do this?
It should be possible quite easily when you provide a string resource with the name of the database:
In /res/values/strings.xml put a line like this:
<string name="db_name">database</string>
In /res/values-de/strings.xml put that line:
<string name="db_name">database_de</string>
And in your DBHelper class use the database name of the strings file currently active according to language settings:
public class DBHelper extends SQLiteOpenHelper {
private final static int DB_VERSION = 1;
private static DBHelper sInstance;
/**
* Provides access to DBHelper singleton.
* #param context
* #return
*/
public static DBHelper getInstance(Context context) {
// Use the application context, which will ensure that you
// don't accidentally leak an Activity's context.
// See this article for more information: http://bit.ly/6LRzfx
if (sInstance == null) {
sInstance = new DBHelper(context.getApplicationContext());
}
return sInstance;
}
/**
* Constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DBHelper(Context context) {
// here comes the magic:
String dbName = context.getString(R.string.db_name);
super(context, db_name, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
// ...
}
// ...
}
using Ridcully solution, you will need to add only the database name part,
all you need to add is the class you extend.
this example from the library you mentioned:
public class MyDatabase extends SQLiteAssetHelper {
private static final int DATABASE_VERSION = 1;
public MyDatabase(Context context) {
// here comes the magic:
String dbName = context.getString(R.string.db_name);
super(context, dbName , null, DATABASE_VERSION);
}
}
The other database class can be:
public class MyDatabase2 extends SQLiteAssetHelper {
private static final int DATABASE_VERSION = 1;
public MyDatabase(Context context) {
// here comes the magic:
String dbName = context.getString(R.string.db_name_2);
super(context, dbName , null, DATABASE_VERSION);
}
}
NOTE: to use SQLiteAssetHelper, u need to create the database yourself, and put in the assets/databases folder.
you can create sqlite database with this app Sqlitebrowser
Related
I would like to have a database for each user of my app. Usernames are stored in SharedPreferences. So I'm looking for something like this:
public class DatabaseHelper extends SQLiteOpenHelper {
final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
String LoggedInUser = sp.getString("user","");
public static final String DATABASE_NAME = LoggedInUser + ".db";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
public void onCreate(SQLiteDatabase db) {
...
}
...
}
But this doesn't work, because SharedPreferences need context (using getApplicationContext() instead of this doesn't work either. This can be solved by something like this:
private Context appContext;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
this.appContext = context;
}
And then accessing SharedPreferences afterwards.
But I need to access SharedPreferences before this method ("DATABASE_NAME" is used in the above method and I want to define "DATABASE_NAME" using SharedPreferences).
Is there any way for doing this?
Since call to super() must be first statement, the only solution to achieve it would be to pass the DATABASE_NAME as constructor parameter:
public DatabaseHelper(final Context context, final string dbName) {
super(context, dbName, null, 1);
}
You can then either implement factory or Facade pattern to construct or pass the value to DatabaseHelper or simply pass the dbName value from Activity/Fragment.
An example of Factory could be:
public final class DatabaseFactory {
public static DatabaseHelper getDataBaseHelper(final Context context){
final SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
final String loggedInUser = sp.getString("user","");
return new DatabaseHelper(context,loggedInUser);
}
}
In Activity you can access the Helper as follows:
public class MainActivity extends Activity{
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
final DatabaseHelper myDB = DatabaseFactory.getDataBaseHelper(this);
...
}
}
Must you use SharedPreference in you SQLiteOpenHelper?
I think you maybe use SQLite in your Activity, then how about declare SharedPreference in your Activity first, and get that in your SQLite through database's constructor.
04-11 05:05:17.837: W/SQLiteConnectionPool(6454): A SQLiteConnection object for database Please fix your application to end transactions in progress properly and to close the database when it is no longer needed.
this is an error return by my logcat ive searched for the possible solution for this error in here andhere saying you should close database but i have this code
#Override
protected void onPause() {
super.onPause();
if (db.isOpen()) {
db.close();
}
}
on all my ativity so im wondering why i get this error
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper sInstance;
private static final String DATABASE_NAME = "database_name";
private static final String DATABASE_TABLE = "table_name";
private static final int DATABASE_VERSION = 1;
public static DatabaseHelper getInstance(Context context) {
// Use the application context, which will ensure that you
// don't accidentally leak an Activity's context.
// See this article for more information: http://bit.ly/6LRzfx
if (sInstance == null) {
sInstance = new DatabaseHelper(context.getApplicationContext());
}
return sInstance;
}
/**
* Constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
im currently doing the same thing im doing this tutorial if you want to try it you can you can find it here
I'm using Ormlite has my database for my android application. I have now been given requirements to create a container to be able to hold multiple databases. I do not know before hand how many databases I will need as the number of databases by the container will be dynamic. I've reviewed ormlite's multiple database code, but that is if you know how many databases you have before hand and you create one class per database. I don't have that information available to me. So the question is, how can I create a DatabaseHelper class with ormlite to handle multiple different databases and still be thread safe? All the databases hold the exact same type of data, just different data per container.
DatabaseHelper code:
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
private static final int DATABASE_VERSION = 1;
private Dao<MyData, Integer> myDataDao;
private static final AtomicInteger usageCounter = new AtomicInteger(0);
private volatile static DatabaseHelper helper = null;
public DatabaseHelper(Context context, String dynamicDBName)
{
super(context, dynamicDBName, null, DATABASE_VERSION);
}
public static DatabaseHelper getHelper(Context context, String dynamicDBName)
{
if (helper == null)
{
synchronized (DatabaseHelper.class)
{
// double check lock to prevent method from being synchronized
// requiring sync lock only being used once
if (helper == null)
helper = new DatabaseHelper(context, dynamicDBName);
}
}
usageCounter.incrementAndGet();
return helper;
}
#Override
public void close()
{
if (usageCounter.decrementAndGet() == 0)
{
super.close();
helper = null;
}
}
}
I have database class. The class and its constructor are shown below.
public class LatLogDBAdapter {
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
I want to use the database in the static method. So that I declare as private static LatLogDBAdapter dbHelper;. Then when i initialize, i have problem. dbHelper = new LatLogDBAdapter(this); dbHelper = new LatLogDBAdapter(DetailMapView.this); make compile error. How can I use this in static method?
If you want to create static method that returns your dbhelper i suggest you to create normal subclass of SQLiteOpenHelper and in this class create public static method that will return new instance. This also is sounds like good reason to use design pattern Singleton
Update:
I mean I want to use this database class inside another java class.
That class has static method and use the database.
Here i create for you basic snippet of code:
public class AdapterWrapper {
private static SQLiteOpenHelper instance;
public static SQLiteOpenHelper getInstance(Context c) {
if (instance == null) {
instance = new DatabaseHelper(c);
}
return instance;
}
private static class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "Example";
private static final int DB_START_VERSION = 1;
public DatabaseHelper(Context cntx) {
super(cntx, DB_NAME, null, DB_START_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// creating tables
}
#Override
public void onUpgrade(SQLiteDatabase db, int old, int new) {
/// drop an upgrading db
}
}
}
I've implemented access to a database using SQLiteOpenHelper from the android.database package within some classes (with pattern DAO).
I wrote some junit tests for these classes using an AndroidTestCase but this causes the tests to use the same database as the application.
I read that the ProviderTestCase2 or RenamingDelegatingContext can be used to test the database separately. Unluckily I couldn't find any nice tutorial/example that shows how to test a database with ProviderTestCase2/RenamingDelegatingContext.
Can anyone point me somewhere OR give me some tip OR share some code for database testing?!
Cheeerrrrsss!!
Giorgio
Both the ProviderTestCase and RenamingDelegatingContext will destroy the database if one already exists before opening it within it's context, so in that sense they both have the same low-level approach towards opening a SQLite database.
You leverage this by opening the database in your fixture in setUp(), which will then ensure that your working with a fresh database before each test case.
I would suggest that you go for writing content providers rather than creating database adapters. You can use a common interface for accessing data, be it stored in the DB or somewhere over the network, the design of content providers can be accommodated to access such data at the cost of a bit of IPC overhead involved that most of us shouldn't have to care about.
If you did this for accessing a SQLite database, the framework would completely manage the database connection for you in a separate process. As added beef, the ProviderTestCase2<ContentProvider> completely bootstraps a test context for your content provider without you having to a write a single line of code.
But, that's not said it isn't such a huge effort to do the bootstrapping yourself. So supposing you had a database adapter as such; we'll just focus on open() for getting write access to our database, nothing fancy:
public class MyAdapter {
private static final String DATABASE_NAME = "my.db";
private static final String DATABASE_TABLE = "table";
private static final int DATABASE_VERSION = 1;
/**
* Database queries
*/
private static final String DATABASE_CREATE_STATEMENT = "some awesome create statement";
private final Context mCtx;
private SQLiteDatabase mDb;
private DatabaseHelper mDbHelper;
private static class DatabaseHelper extends SQLiteOpenHelper {
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE_STATEMENT);
}
#Override
public void onUpgrade(SQLiteDatabase db, int a, int b) {
// here to enable this code to compile
}
}
/**
* Constructor - takes the provided context to allow for the database to be
* opened/created.
*
* #param context the Context within which to work.
*/
public MyAdapter(Context context) {
mCtx = context;
}
/**
* Open the last.fm database. If it cannot be opened, try to create a new
* instance of the database. If it cannot be created, throw an exception to
* signal the failure.
*
* #return this (self reference, allowing this to be chained in an
* initialization call)
* #throws SQLException if the database could be neither opened or created
*/
public MyAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
mDbHelper.close();
}
}
Then you could write your test as such:
public final class MyAdapterTests extends AndroidTestCase {
private static final String TEST_FILE_PREFIX = "test_";
private MyAdapter mMyAdapter;
#Override
protected void setUp() throws Exception {
super.setUp();
RenamingDelegatingContext context
= new RenamingDelegatingContext(getContext(), TEST_FILE_PREFIX);
mMyAdapter = new MyAdapter(context);
mMyAdapter.open();
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
mMyAdapter.close();
mMyAdapter = null;
}
public void testPreConditions() {
assertNotNull(mMyAdapter);
}
}
So what's happening here is that the context implementation of RenamingDelegatingContext, once MyAdapter(context).open() is called, will always recreate the database. Each test you write now will be going against the state of the database after MyAdapter.DATABASE_CREATE_STATEMENT is called.
I actually use database with SQLiteOpenHelper and I have a trick for testing.
The idea is to use standard on-file stored DB during the normal use of the app and an in-memory DB during tests. In this way you can use a clear DB for each test without insert/delete/update data in your standard DB. It works fine for me.
Keep in mind you can use in-memory database, just passing null as name of database file. This is clearly documented in the API documentation.
Advantages of using in-memory DB during tests is explained here:
https://attakornw.wordpress.com/2012/02/25/using-in-memory-sqlite-database-in-android-tests/
In my project I have the DBHelper class wich extends SQLiteHelper. As you can see, there are the standard methods. I simply added a constructor with two parameters. The difference is that when I call the super constructor, I pass null as DB name.
public class DBHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "mydatabase.db";
public DBHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public DBHelper(Context context, boolean testMode) {
super(context, null, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
//create statements
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//on upgrade policy
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//on downgrade policy
}
}
Every "model" in the project extends DBModel that is an abstract class.
public abstract class DBModel {
protected DBHelper dbhelper;
public DBModel(Context context) {
dbhelper = new DBHelper(context);
}
//other declarations and utility function omitted
}
As discussed here: How can I find out if code is running inside a JUnit test or not?
there is a way to establish if you are running JUnit tests, simply searching in stack trace elements.
As a conseguence, I modified DBModel constructor
public abstract class DBModel {
protected DBHelper dbhelper;
public DBModel(Context context) {
if(isJUnitTest()) {
dbhelper = new DBHelper(context, true);
} else {
dbhelper = new DBHelper(context);
}
}
private boolean isJUnitTest() {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
List<StackTraceElement> list = Arrays.asList(stackTrace);
for (StackTraceElement element : list) {
if (element.getClassName().startsWith("junit.")) {
return true;
}
}
return false;
}
//other declarations and utility function omitted
}
Note that
startsWith("junit.")
may be
startsWith("org.junit.")
in your case.
I have an application that uses a ContentProvider backed by an sqlite database to provide data to the application.
Let PodcastDataProvider be the actual dataprovider used by the application.
Then you can set up a test provider with something like the following:
public abstract class AbstractPodcastDataProvider extends ProviderTestCase2<PodcastDataProvider>{
public AbstractPodcastDataProvider(){
this(PodcastDataProvider.class, Feed.BASE_AUTH);
}
public AbstractPodcastDataProvider(Class<PodcastDataProvider> providerClass,
String providerAuthority) {
super(providerClass, providerAuthority);
}
public void setUp() throws Exception{
super.setUp();
//clear out all the old data.
PodcastDataProvider dataProvider =
(PodcastDataProvider)getMockContentResolver()
.acquireContentProviderClient(Feed.BASE_AUTH)
.getLocalContentProvider();
dataProvider.deleteAll();
}
}
to setup a test data provider that will be backed by a different database than the actual application.
To test the DAO, create another class which extends AbstractPodcastDataProvider and use the
getMockContentResolver();
method to get an instance of a content resolver that will use the test database instead of the application database.
private static String db_path = "/data/data/android.testdb/mydb";
private SQLiteDatabase sqliteDatabase = null;
private Cursor cursor = null;
private String[] fields;
/*
* (non-Javadoc)
*
* #see dinota.data.sqlite.IDataContext#getSQLiteDatabase()
*/
public SQLiteDatabase getSQLiteDatabase() {
try {
sqliteDatabase = SQLiteDatabase.openDatabase(db_path, null,
SQLiteDatabase.OPEN_READWRITE);
sqliteDatabase.setVersion(1);
sqliteDatabase.setLocale(Locale.getDefault());
sqliteDatabase.setLockingEnabled(true);
return sqliteDatabase;
} catch (Exception e) {
return null;
}
}
if you give the exact location of the sqlite db(in my case it's db_path), using the above method you can find-out whether it returns an sqlitedatabase or not.
A possible solution can be to open database using this method
myDataBase = SQLiteDatabase.openDatabase(DATABASE_NAME, null, SQLiteDatabase.OPEN_READWRITE);
And change database name in your tests. Here you can find some info about this method.