i have a listview in my project that links to database and shows its context from database but my problem is whenever my application runs it records goes twice (ex.2records first run, 4records with same context,...) and i do not know that wat is problem
this is my database class:
new File(DIR_DATABASE).mkdirs();
dataBase = SQLiteDatabase.openOrCreateDatabase(DIR_DATABASE + "/information.sqlite", null);
dataBase.execSQL("CREATE TABLE IF NOT EXISTS information (" +
"information_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE ," +
"information_title TEXT)");
dataBase.execSQL("INSERT INTO information (information_title) VALUES ('قسمت اول')");
dataBase.execSQL("INSERT INTO information (information_title) VALUES ('قسمت دوم')");
and its my main class that shows listview:
ListView lstC findViewById(R.id.lstContent);
adapter = new AdapterNote(title);
lstContent.setAdapter(adapter);
readFromDataBase();
adapter.notifyDataSetChanged();
}
private void readFromDataBase() {
Cursor cursor = G.dataBase.rawQuery("SELECT * FROM information", null);
while (cursor.moveToNext()) {
StructNote detail = new StructNote();
detail.title = cursor.getString(cursor.getColumnIndex("information_title"));
title.add(detail);
}
cursor.close();
adapter.notifyDataSetChanged();
}
You need to understand SQLiteOpenHelper for this. This is workflow issue, so it would be better you should go through some tutorial. This is a nice tutorial where you can learn the concept.
In very short i am listing few points that may be useful for you:
In your application you create a subclass of the SQLiteOpenHelper class.SQLiteOpenHelper is a helper class to manage database creation and version management. In subclass override, onCreate() and onUpgrade().
public class MySQLiteHelper extends SQLiteOpenHelper {
public void onCreate(SQLiteDatabase database) {
// create database command.
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// upgrade database command here.
}
}
Create a DAO class that will manage the interaction with the database. Your CRUD methods will go here. See DAO pattern for details. This class will centralize the access to database, hence your activity, fragment will interact with this class to perform operation. Direct access to database won't be allowed.
public class ModelDataSource {
// needed to perform operation on database
private SQLiteDatabase database;
//needed to retrieve database object
private MySQLiteHelper dbHelper;
public boolean insertModel(Model model) {
// perform insert operation on database
}
}
In your activity, you can interact with DAO to perform some action.
public class YourActivity extends Activity {
private ModelDataSource datasource;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
datasource = new ModelDataSource(this);
datasource.open();
boolean insertion_status = datasource.insert(modelobject);
}
}
Related
I've added a column(description) to my table account. I also read this guild to upgrade my database. However, I had a bit confusing of what is getHelper() method in this code:
Dao<Account, Integer> dao = getHelper().getAccountDao();
// change the table to add a new column named "description"
dao.executeRaw("ALTER TABLE `account` ADD COLUMN description INTEGER;");
and where did it come from? I didn't see getHelper() was declared in my DatabaseHelper class. Can someone help me?
You should have a class extending OrmLiteSqliteOpenHelper where you create the tables (in onCreate) and update them (in onUpgrade):
public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
public DatabaseHelper(Context context) {
super(context, "database.db", null, 1);
}
#Override
public void onCreate(SQLiteDatabase database, ConnectionSource connectionSource) {
try {
TableUtils.createTable(connectionSource, Account.class);
} catch (SQLException e) {
throw new RuntimeException("Error when create database tables", e);
}
}
#Override
public void onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion) {
database.execSQL("ALTER TABLE `account` ADD COLUMN description INTEGER;");
//same as:
//AccountDAO dao = getDao(Acount.class);
//dao.executeRaw("ALTER TABLE 'account' ADD COLUMN description INTEGER;");
}
}
OrmLiteBaseListActivity, OrmLiteBaseService and
OrmLiteBaseTabActivity provide a method getHelper to access the
database helper whenever it is needed and will automatically create
the helper in the onCreate() method and release it in the onDestroy()
method.
Furthermore, if you are using classes above, you should have something like this:
public class MyACtivity extends OrmLiteBaseActivity<DatabaseHelper> {
//In this class, you can call getHelper() to obtain the DatabaseHelper instance
}
In my already created and deployed application, I've created a database MainDB, using a single class file which extended SQLiteOpenHelper, viz.
public class BaseSQLiteOpenHelper extends SQLiteOpenHelper {
private final static String DATABASE_NAME = "MainDB";
....
....
}
Issue is I've tied this class, too much to a particular functionality.
Now I'm adding a totally new module in application, which will also interact with DB, but with different new tables.
Issue is I can't use the same class, as it is conflicting in more than one way. And even if I redesign the code, it will only add complexity from functional/understanding point of view.
So, I've decided to use same DB, but different tables.
Now I've already created DB in BaseSQLiteOpenHelper class.
So, how can I create new tables in seprate class using same DB?
One approach is to use separate Database as well, Or,
Create my new table in onCreate() in BaseSQLiteOpenHelper class only (issue with this is mentioning new table in same class seems awkward, as this class has nothing to do with my new table).
Please suggest.
Thank You
First check the current database version for this database
private final static String DATABASE_NAME = "MainDB";
private static final int DATABASE_VERSION = 1;
public BaseSQLiteOpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
and increment the database version(DATABASE_VERSION), and add your new table query in on Upgrade and oncreate method like below.
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("old query no need to change");
db.execSQL("Create your new table here");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
db.execSQL("Create your new table here as well this for update the old DB");
}
}
Done!!!
I have used SQLiteCipher to add security to my application database, the problem is, it is way too slow than the default android SQLite API. So I switched back to the default SQLite API, but I really need the security provided by SQLCipher, so What I did is:
Upon opening my app
Open the encrypted database file
Create a normal database file
Transfer records from encrypted to normal database
Delete the encrypted database file
Use the normal database file
When my app is closed,
Open the normal database file
Create a encrypted database file
Transfer records from normal to encrypted database
Delete the normal database file
This works without any problems, but transferring records takes some time and will consume more(I am assuming that my database file will contain thousands of records during its real-life usage). So is there any other way of what I have done?
Are there any more efficient ways of doing this? TIA!
EDIT : Here are some code using the SQLCipher
Main Activity
private final static String phrase = "passW0rd3r";
private EditText StudNum, StudName, StudCrse;
private DBHelper dbIns;
SQLiteDatabase DBFile;
private MainDBHelper DBHelp;
private MenuItem msg_app;
final Context con = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
StudNum = (EditText)findViewById(R.id.txtsNum);
StudName = (EditText)findViewById(R.id.txtsName);
StudCrse = (EditText)findViewById(R.id.txtCourse);
StudNum.requestFocus();
SQLiteDatabase.loadLibs(this);
dbIns = new DBHelper(this, DB_NAME);
DBFile = dbIns.getWritableDatabase(phrase);
DBHelp = new MainDBHelper(this);
//Takes too much time to load, loads around 3 seconds, compared to
//the default SQLite API, which is half a second only
}
MainDBHelper class
public class MainDBHelper {
private static final String DB_NAME = "StudentInfo.db";
private static final String TABLE_NAME = "StudentInfo";
private final static String phrase = "passW0rd3r";
private DBHelper openHelper;
private SQLiteDatabase database;
public MainDBHelper(Context context) {
openHelper = new DBHelper(context, DB_NAME);
database = openHelper.getWritableDatabase(phrase);
}
}
DBHelper class
public class DBHelper extends SQLiteOpenHelper{
private String DBName;
public DBHelper(Context context, String DBname) {
super(context, DBname, null, 5);
this.DBName = DBname.substring(0,DBname.length()-3);
// TODO Auto-generated constructor stub
}
#Override
public void onCreate(SQLiteDatabase arg0) {
// TODO Auto-generated method stub
arg0.execSQL("CREATE TABLE " + DBName + " (_id INTEGER PRIMARY KEY, " +
"Stud_Num TEXT, Stud_Name TEXT, Stud_Crse TEXT)");
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// TODO Auto-generated method stub
arg0.execSQL("DROP TABLE IF EXISTS " + DBName);
onCreate(arg0);
}
}
Extra class that uses the same Database Connection
DisplayMessageActivity class - this one loads around 2 seconds just to show 3 records in the list view. If I use the default SQLite API, it can load up to 10 records in just a second
public class DisplayMessageActivity extends Activity {
private ListAdapter listAdapt;
private MainDBHelper DBHelp;
private EditText edtName, edtSnum, edtCrse;
private TextView dName, dSnum, dCrse, ssn;
private ListView lst_snum;
private SearchView sView;
private MenuItem sItem;
private View lView, diagView;
private AlertDialog.Builder DBuilder;
private AlertDialog ADiag;
private String StudentNumber, edSname, edSnum, edCrse;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_message);
DBHelp = new MainDBHelper(this);
lst_snum = (ListView)findViewById(R.id.lv_test);
refreshList();
createADiag(this);
lst_snum.setOnItemLongClickListener(new OnItemLongClickListener(){
#Override
public boolean onItemLongClick(AdapterView<?> parent, View view,
int position, long id) {
// TODO Auto-generated method stub
lView = view;
ssn = (TextView)view.findViewById(R.id.studNum);
ADiag.show();
return false;
}
});
}
private void refreshList(){
listAdapt = null;
listAdapt = new ListAdapter(this, DBHelp.getTimeRecordList());
lst_snum.setAdapter(listAdapt);
}
}
Update: I've read some questions related to mine and I really see that some are also facing performance issues by using SQLCipher, one answer said that to "Cache the database" for one time connection only that can be used on multiple activities on the same app, problem is, I don't have any idea to do that
There are a few very important guidelines for optimal SQLCipher performance:
Do not repeatedly open and close connections, as key derivation is very expensive, by design
Use transactions to wrap insert / update / delete operations. Unless executed in a transaction scope, every operation will occur within it's own transaction which slows things down by several orders of magnitude
Ensure your data is normalized (i.e., using good practices for separation of data into multiple tables to eliminate redundancy). Unnecessary duplication of data leads to database bloat, which means more pages for SQLCipher to operate on
Ensure that any columns that are used for searches or join conditions are indexed. If you don't, SQLCipher will need to execute full database scans across large numbers of pages
Vacuum periodically to ensure databases are compact if you do large deletes, updates etc.
Finally, to diagnose further the performance of your specific query statements, there are a couple of options. First, I would recommend running PRAGMA cipher_profile as CommonsWare mentioned above, you can read more about the usage here. This will give you a log of the queries performed on the database and their respective execution times in milliseconds. Next, you run an explain query plan command against some of your queries that may be performing poorly? The output of the explain query command is described here.
My application has 7 activities and all are highly dependent on database and each activity when accessed will query database a lot.
For this issue I have created a singleton database object for the database class and for every operation I am calling below statement.
Database.getInstance(c1).getWritableDatabase()
for inserts, selects and deletes I am using like below
-- insert Database.getInstance(c1).getWritableDatabase().insert
-- select Database.getInstance(c1).getWritableDatabase().rawquery
-- delete Database.getInstance(c1).getWritableDatabase().rawquery
I am using this way because by this only one object is used in whole application and multiple objects won't be created.
But when searching the web, I learned that getWritableDatabase() will open a connection to database. Now I am confused whether object to database class will open a connection or calling getwritabledatabase will open a connection.
I am worrying because I feel I am creating too many connections unknowingly.
Edit
I have tried to create a writable database object as below in every activity and use that object in that actovity but it returned me database locked exception hence I was forced to use the Database.getInstance(c1).getWritableDatabase() for every db operation
I have created as below
Sqldatabase sd=Database.getInstance(c1).getWritableDatabase()
I am wanting to discover how the database should be opened and where it should be closed to make a application stable and avoid memory leaks. I am am a beginner to Android, and keen to find out if my design is a valid one.
Edit
Database file
public class Database extends SQLiteOpenHelper{
private static String dbname="Director";
private static int dbversion=1;
SQLiteDatabase db;
private Context m1Context;
private static Database minstance;
public Database(Context context) {
super(context, dbname, null, dbversion);
// TODO Auto-generated constructor stub
}
public synchronized static Database getInstance(Context m1Context){
if (minstance==null){
minstance=new Database(m1Context);
}
return minstance;
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
STable st=new StockTable(m1Context);
BTable bt=new BrokerageTable(m1Context);
SList sl=new StockList(m1Context);
db.execSQL(st.stocktable);
db.execSQL(bt.Brokerage);
db.execSQL(sl.Create());
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
class file:
public class SList {
Context c1;
Cursor getid;
StockList(Context mContext){
c1=mContext;
}
**//SQLiteDatabase sd=Database.getInstance(c1).getWritableDatabase();**
String Ctable;
//String ,selectIDgetstocks,deletestock;
public String tablename="Stocklist";
public String Column1="_id";
public String Column2="Sname";
ContentValues cv=new ContentValues();
String getstocks="Select " + Column1 + " as _id, " + Column2 + " From "+ tablename;
String selectID="Select Max("+ Column1 + ") from " + tablename;
public String Create(){
Ctable="Create Table " + tablename + " (" + Column1 + " INTEGER PRIMARY KEY , " + Column2 + " Text" + ")";
return Ctable;
}
public void insert(int stockid,String name){
cv.put(Column1, stockid);
cv.put(Column2, name);
++Database.getInstance(c1).getWritableDatabase().insert(tablename,null,cv);++
}
}
You should have a function to open the database, a function to close the database, and other functions to query. Consider the following class:
public void openDatabase(){
database = Database.getInstance(c1).getWritableDatabase();
}
public void closeDatabase(){
database.close()
}
public void insert(){
//your insertion code here using database object
}
public String select(){
//your selection code here using database object
}
make database class variable. This way you can call openDatabase() at first and never have to open again until you close it or instantiate the class for the first time.
Then you can use insert or select as required.
Hope this helps
I believe class Database is wrapper for SQLiteOpenHelper, right? Since you have a singleton, each time you call getWritableDatabase() it's utilizing existing connection. The new connection is being called only at the first call. You can open new connection at onResume or onCreate and close it at onDestroy or onPause, for example.
From the docs on getWritableDatabase():
Once opened successfully, the database is cached, so you can call this method every time you need to write to the database
I'm still very new to Object Oriented programming and am having problems with the class inheritance and the scope of variables from one instantiated class to another.
I'm trying to build an android application that can read multiple XML feeds and save them to the phone's SQLite database. Each feed has a different name ("news", "audio_mixes" etc.) and I want to use these names to save each feed's data into separate database tables - each one named after the feed title.
In diagrammatic terms this is what my classes look like:
(source: baroquedub.co.uk)
The Main activity displays two buttons each of which starts an activity - both of which are instances of the TitlesBrowser class. Extras are used to pass different values of the_url and the_feed_type variables.
#Override
public void onCreate(final Bundle icicle) {
super.onCreate(icicle);
this.setContentView(R.layout.main);
this.getNewsButton = (Button) findViewById(R.id.get_news_button);
this.getNewsButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
Intent doGetNews = new Intent(Main.this, TitlesBrowser.class);
doGetNews.putExtra("the_url", Main.this.getString(R.string.titles_url));
doGetNews.putExtra("the_feed_type", Main.this.getString(R.string.news_type_var));
startActivity(doGetNews);
}
});
this.getMixtapesButton = (Button) findViewById(R.id.get_mixtapes_button);
this.getMixtapesButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
Intent doGetMixtapes = new Intent(Main.this, TitlesBrowser.class);
doGetMixtapes.putExtra("the_url", Main.this.getString(R.string.titles_url));
doGetMixtapes.putExtra("the_feed_type", Main.this.getString(R.string.mixtapes_type_var));
startActivity(doGetMixtapes);
}
});
}
The TitlesBrowser class, in its onCreate method, gets the Extras and saves them to private local variables.
Intent i = getIntent();
private String theUrl = (String) i.getStringExtra("the_url");
private String theFeedType = (String) i.getStringExtra("the_feed_type");</pre>
This class does two things,
1/ it creates an instance of the DatabaseHelper class, and uses a public set method in that class to pass on the value of the locally help theFeedType variable. Having done that it queries the database for any existing data (the theFeedType is the name of each table)
db=new DatabaseHelper(this);
db.setTableName(theFeedType);
dbCursor=db.getReadableDatabase().rawQuery("SELECT _ID, id, title FROM "+theFeedType+" ORDER BY id", null);
2/ then it loads new data from the feed URL by instantiating another class HTTPRequestHelper :
HTTPRequestHelper helper = new HTTPRequestHelper(responseHandler);
helper.performPost(theUrl, theFeedType);
The HTTP requests work fine, and depending on which of the two buttons is clicked, each of the two different activities display the appropriate XML (i.e. retrieve the correct data)
- therefore I know that theUrl and theFeedType variables are local to each instance of the TitlesBrowser class.
One calls:
http://baroquedub.co.uk/get-feed.php?feed=news
and the other one:
http://baroquedub.co.uk/get-feed.php?feed=audio_mixes
The problem is with the DatabaseHelper class:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME="baroquedub.db";
private String table_name;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+ table_name + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"id INTEGER, " +
"title TEXT, " +
"details TEXT, " +
"picture TEXT, " +
"viewed BOOLEAN DEFAULT '0' NOT NULL);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
android.util.Log.w("Baroquedub", "Upgrading database, which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS mixtapes");
onCreate(db);
}
public void setTableName(String theName){
this.table_name = theName;
}
}
I would expect it to create a new table each time it is instantiated (whether "news" or "audio_mixes" was passed from its parent TitlesBrowser class.
But it only works once - if I start the application and click on "news" a table called news is created and each time I return to that activity it successfully retrieves data from that same database.
But if I then start the other activity by clicking on the other button (i.e access the other feed) I get an SQL error telling me that a database of that name doesn't exist. In other words the onCreate db.execSQL("CREATE TABLE... method doesn't appear to get called again.
It's as if multiple copies of the DatabaseHelper class aren't being created, although there are two instances of TitlesBrowser.
--
Here's a demonstration of the problem:
http://screencast.com/t/NDFkZDFmMz
This has been really tricky to explain (especially for a newbie!!!) and I hope it makes some sense. I'd be very grateful for any help, advice or guidance.
When you instanciate a SQLiteOpenHelper class you actually access a singleton in the Activity's context. The helper decides when to call the OnCreate / onUpgrade methods - specifically, if the DB doesn't exist or is stale.
Either way, the onCreate method is not called each time you create a new instance of the class - that would really make no sense. You should put the two CREATE TABLE commands in the onCreate method.
(PS I assume the error you're getting is that a table is missing and not the entire database).
A big thank you to adamk for providing the guidance necessary to solve this, in the answer he provided.
He suggested that I add two CREATE TABLE commands in the DatabaseHelper's onCreate method (on for each of my feeds). However, I needed to abstract things a little more so that I could still use multiple instantiations of the TitlesBrowser activity and so that I wouldn't have to rewrite the helper class every time a new feed came on board.
For those interested, here's the solution I came up with.
First of all, I removed the CREATE TABLE commands in the DatabaseHelper's onCreate method. I also removed the private table_name variable (and its associated set method) and I replaced this with a public method called makeTable():
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME="baroquedub.db";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
android.util.Log.w("Baroquedub", "Upgrading database, which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS mixtapes");
onCreate(db);
}
public void makeTable(String theTableName){
SQLiteDatabase thisDB = DatabaseHelper.this.getWritableDatabase();
thisDB.execSQL("CREATE TABLE IF NOT EXISTS "+ theTableName + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"id INTEGER, " +
"title TEXT, " +
"details TEXT, " +
"picture TEXT, " +
"viewed BOOLEAN DEFAULT '0' NOT NULL);");
}
}
In TitlesBrowser, rather than instantiating the DatabaseHelper, setting the table name and then reading in the data:
db=new DatabaseHelper(this);
db.setTableName(theFeedType);
dbCursor=db.getReadableDatabase().rawQuery("SELECT _ID, id, title FROM "+theFeedType+" ORDER BY id", null);
Instead, I reworked all this so that it would more elegantly handle the creation and loading of the database data from each table:
The database Helper is created as before,
then the new databaseLoad() method tries to read from the required table
and if it can't, it calls the DatabaseHelper's public makeTable() method,
before finally trying to load the data again:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = getIntent();
theUrl = (String) i.getStringExtra("the_url");
theFeedType = (String) i.getStringExtra("the_feed_type");
// show saved in DB
db=new DatabaseHelper(this);
databaseLoad();
}
private void databaseLoad(){
try { // table exists
dbCursor=db.getReadableDatabase()
.rawQuery("SELECT _ID, id, title FROM "+theFeedType+" ORDER BY id", null);
displayContent();
} catch (Exception e) { // table doesn't exist
db.makeTable(theFeedType);
databaseLoad(); // try again
}
}