I am following a tutorial online, but i am getting an illegal state exception.
Link to tutorial:
http://www.developerfeed.com/android/tutorial/building-todo-list-app-android-using-sqlite
Here is the Database class:
public class TaskerDbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "taskerManager";
// tasks table name
private static final String TABLE_TASKS = "tasks";
// tasks Table Columns names
private static final String KEY_ID = "id";
private static final String KEY_TASKNAME = "taskName";
private static final String KEY_STATUS = "status";
public TaskerDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE IF NOT EXISTS " + TABLE_TASKS + " ( "
+ KEY_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + KEY_TASKNAME
+ " TEXT, " + KEY_STATUS + " INTEGER)";
db.execSQL(sql);
db.close();
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldV, int newV) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TASKS);
// Create tables again
onCreate(db);
}
// Adding new task
public void addTask(Task task) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_TASKNAME, task.getTaskName()); // task name
// status of task- can be 0 for not done and 1 for done
values.put(KEY_STATUS, task.getStatus());
// Inserting Row
db.insert(TABLE_TASKS, null, values);
db.close(); // Closing database connection
}
public List<Task> getAllTasks() {
List<Task> taskList = new ArrayList<Task>();
// Select All Query
String selectQuery = "SELECT * FROM " + TABLE_TASKS;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
// looping through all rows and adding to list
if (cursor.moveToFirst()) {
do {
Task task = new Task();
task.setId(cursor.getInt(0));
task.setTaskName(cursor.getString(1));
task.setStatus(cursor.getInt(2));
// Adding contact to list
taskList.add(task);
} while (cursor.moveToNext());
}
// return task list
return taskList;
}
public void updateTask(Task task) {
// updating row
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_TASKNAME, task.getTaskName());
values.put(KEY_STATUS, task.getStatus());
db.update(TABLE_TASKS, values, KEY_ID + " = ?",new String[] {String.valueOf(task.getId())});
db.close();
}
}
And here is what the log file says:
08-14 14:21:42.133: E/AndroidRuntime(10366): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.tasker/com.example.tasker.ViewTask}: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.example.tasker/databases/taskerManager
There are two problems in your code:
A) you must not close the db in onCreate. That method is part of opening the db so the db should be open afterwards. source-code
public void onCreate(SQLiteDatabase db) {
...
db.close();
}
B) You close the database at the end of every task.
public void updateTask(Task task) {
db = get..
...
db.close();
}
But while doing that you are still using the same database via a Cursor
db = db.get..
Cursor cursor = db.rawQuery
while (cursor.moveToNext() {
updateTask();
}
db.close();
SQLiteOpenHelper will always give you the same db object since there is only one database. Closing the connection in one method while using it in another one can lead to your problem as well.
You could probably solve the task problem by using the existing db object from the outer loop or simply not closing the db in updateTask but it is in practice much better not to close the database at all.
Especially if you hand out references to Cursor. You must not close the database before the cursor is closed because Cursor can re-query it's data using the database connection it knows about. That will happen in two cases:
If your code uses cursor.requery() probably indirectly through a bad CursorAdapter.
The second case is not as common. Cursor can only see a limited windows of the current query. If you move the cursor outside of that window it will re-query the required part using the database connection as well. Typical small databases are usually much smaller than the window so there is no need to move the window and no hidden requery will happen.
Closing resources is usually a good thing. Closing the database is in most cases not. It is also no problem not doing it. SQLite makes sure nothing bad happens since every modification to the database is guaranteed by the transaction safety of SQLite.
you are closing the databse-connection at the end of each method with db.close().
So if you start another method you will always have to re-enable the connection.
with
getWritableDatabase();
you should get it done.
Or you don't close the connection at the end of every method. That totally depends on what your app is meant for.
you have not called db.close(); in
public List<Task> getAllTasks() {
.....
}
It may lead that problem because when you try to call getWritableDatabase(); in next function at that time database is already being open. so call db.close(); and close it in getAllTasks() method also.
Related
So I'm trying to setup my SQLite database but I'm receiving errors which I'm finding difficult to correct.
See the error log here
Theres an image of the error being thrown and heres my code setup for my DatabaseHelper
I've also received some other errors in my log which I'm not sure what they are related to - any info be great
see the logcat output here
code of the error anyway
E/SQLiteLog: (1) table my_manager has no column named
location_description in "INSERT INTO my_manager(location_name,location_county,location_description)
VALUES (?,?,?)"
2020-11-28 19:56:32.986 5665-5665/? E/SQLiteDatabase: Error inserting location_name=Blarney Stone
location_county=Cork location_description=Stone in big wall
android.database.sqlite.SQLiteException: table my_manager has no column named location_description
(code 1 SQLITE_ERROR): , while compiling: INSERT INTO
my_manager(location_name,location_county,location_description) VALUES (?,?,?)
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1045)
a....
And here is my source code:
public class MyDatabaseHelper extends SQLiteOpenHelper {
private Context context;
private static final String DATABASE_NAME = "my_manager.db";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "my_manager";
private static final String COLUMN_ID = "location_id";
private static final String COLUMN_LOCATION = "location_name";
private static final String COLUMN_COUNTY = "location_county";
private static final String COLUMN_DESCRIPTION = "location_description";
public MyDatabaseHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE " + TABLE_NAME +
" (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COLUMN_LOCATION + " TEXT, " +
COLUMN_COUNTY + " TEXT, " +
COLUMN_DESCRIPTION + " TEXT );";
db.execSQL(query);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
void addLocation(String location, String county, String description){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_LOCATION, location);
cv.put(COLUMN_COUNTY, county);
cv.put(COLUMN_DESCRIPTION, description);
long result = db.insert(TABLE_NAME, null, cv);
if (result == -1){
Toast.makeText(context, "Failed!" , Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "Added Successfully" , Toast.LENGTH_SHORT).show();
}
}
Cursor readAllData(){
String query = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = null;
if(db != null){
cursor = db.rawQuery(query, null);
}
return cursor;
}
}
When you first run your app the onCreate is executed and the schema at the time of first execution is set and the DATABASE_VERSION is associated with that schema.
Every time the super is invoked in the constructor (super(context, DATABASE_NAME, null, DATABASE_VERSION);) the SQLiteOpenHelper code looks at the database version number and if its greater than last defined than the onUpgrade is invoked.
During initial development it is common to alter the schema as you are adding to your App before first release. In this mode you have two choices:
Uninstall and reinstall App everytime the schema changes (loss of data). This causes the initial onCreate to be invoked with new schema again associated with version.
Bump the version (just needs to be higher than previous version) and the onUpgrade is invoked where you'd likely ALTER TABLE (to add/modify columns) or CREATE TABLE to add new tables.
The second option is normally used when an app is already released and the schema needs to be altered (without data loss). The end user would receive an App update and the onUpdate is invoked (because of the bumped version) and any alterations are applied to their existing database without loss of data (assuming you onUpgrade did not intentionally destroy data).
If data loss during initial development is not an issue than the uninstall/reinstall option seems easiest.
If you just alter schema without bumping version then essentially nothing is affected - and therefore when you perform a query on a new column, for example, it complains that it doesn't exist because it was never added.
I try to include transactions in my application because writing in database is very slow and I saw here and here that transactions are a solution but they are still very confusing to me.
I have Schedule objects that contains an object LineStation, and I want to write them in database using transactions.
Here, the method addSchedules in my class ScheduleDAO, that writes all schedules in database. It contains only one transaction.
public void addSchedules(ArrayList<Schedule> schedulesList) {
SQLiteDatabase db = this.dh.getWritableDatabase();
db.beginTransactionNonExclusive();
for (Schedule schedule : schedulesList) {
ContentValues values = new ContentValues();
// insert linestation
LineStationDAO.getLineStationDAO().addLineStation(schedule.getLineStation());
values.put(/*...*/);
/* ... */
db.insert(DatabaseHandler.TABLE_SCHEDULE, null, values);
}
db.setTransactionSuccessful();
db.endTransaction();
db.close();
}
And this is, the method addLineStation in my class LineStationDAO that saves the object LineStation given. It's called by addSchedules and doesn't contain transaction because it is "nested" in the addSchedules transaction.
public void addLineStation(LineStation lineStation) {
SQLiteDatabase db = this.dh.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(/*...*/);
/* ... */
db.insert(DatabaseHandler.TABLE_LINE_STATION, null, values); // database is locked (code 5)
db.close();
}
The LineStation insert implies an SQLiteDatabaseLockedException (database is locked -code 5).
What I have done wrong, please? Thanks.
The problem is that a database transaction cannot exist across multiple database connections. In the addLineStation method, you are opening a second database connection, when you should be using the one created in addSchedules.
You need to pass the SQLiteDatabase db object down to the addLineStation method like this:
LineStationDAO.getLineStationDAO().addLineStation(db, schedule.getLineStation());
and change this:
public void addLineStation(SQLiteDatabase db, LineStation lineStation) {
ContentValues values = new ContentValues();
values.put(/*...*/);
/* ... */
db.insert(DatabaseHandler.TABLE_LINE_STATION, null, values); // database is no longer locked (code 5)
}
I am including a database in my Android application. I am following the guide here:
http://developer.android.com/training/basics/data-storage/databases.html
I have a "DatabaseHelper" class that extends SQLiteOpenHelper as suggested, and I have a Contract that holds the information about the table and implements 'BaseColumns'. Therein, I have a function for inserting a new row into the database:
public static void addUploadInfo(SQLiteOpenHelper dbHelper, UploadInfo info) {
// Gets the data repository in write mode
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = info.getContentValues();
// Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
UploadInfoTable.TABLE_NAME,
null,
values);
db.close(); // Closing database connection
info.setDatabaseId(newRowId);
}
I am using the database to store app related meta data about a file. When I create a new file, I run the above function, and a row is successfully added to the database (and the database is created successfully if needed). I can see it using:
public static List<UploadInfo> getUploadInfoList(SQLiteOpenHelper dbHelper) {
List<UploadInfo> infoList = new ArrayList<>();
SQLiteDatabase db = dbHelper.getReadableDatabase();
String allQuery = "SELECT * FROM " + UploadInfoTable.TABLE_NAME;
Cursor cursor = db.rawQuery(allQuery, null);
if (cursor.moveToFirst()) {
do {
infoList.add(new UploadInfo(cursor));
} while (cursor.moveToNext());
}
cursor.close();
db.close();
return infoList;
}
However, when the app first loads, I insert a few initial entries into the database using:
#Override
public void onResume() {
super.onResume();
DatabaseHelper dbHelper = new DatabaseHelper(getActivity());
for (UploadInfo info: uploadList) {
UploadInfoTable.addUploadInfo(dbHelper, info);
}
}
These entries are assigned a new row ID that appears to be the correct value (not -1 indicating an error). There are no errors in the log. However, they are not found when I next run getUploadInfoList.
I have also tried this alternate insert function:
public static void addUploadInfo(SQLiteOpenHelper dbHelper, UploadInfo info) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = info.getContentValues();
// Insert the new row, returning the primary key value of the new row
db.beginTransaction();
long newRowId = -1;
try {
newRowId = db.insertOrThrow(
UploadInfoTable.TABLE_NAME,
null,
values);
db.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
}
db.close(); // Closing database connection
info.setDatabaseId(newRowId);
}
but I see the same result. In no case is info null. I always log the contents of the ContentValues. I use final variables for column names, so don't think I have an error in column name. This would show up as a row value of -1 anyway, and it doesn't.
Why would one implementation of the code insert successfully and the other not?
I suspect your code in onResume() is not successfully detecting the upgrade. Rather put all your code having to do with the upgrade in onUpgrade() instead of trying to detect an upgrade in onResume(). That should resolve the issue.
My application has a SQLite database to contain a list of highways. It shows them all in a list. It first tries to query the database for the roads. If the query doesn't return any, it calls another method that downloads a list from a remote server, and populates the database. After that returns, it immediately queries the database again.
That's how it should work. How it actually works is, the first query always returns nothing. It goes straight to downloading a fresh list, it inserts the list into the database, and queries again. The second query always returns the correct result. The strange thing is, I can repeat the operation without even exiting the application. Using the adb shell, I can read the SQLite3 database on the emulator. The data shown in the database is exactly as expected. But the application is behaving as though the data isn't there? Is there some behaviour I'm not aware of? Here's the code.
RoadsDataSource.java
public class RoadsDataSource {
private DataStorage data;
private static Context context;
public RoadsDataSource() {
this.data = new DataStorage(RoadsDataSource.context);
}
private List<Road> getRoads(Integer state) {
List<Road> roads = loadRoadsFromDb(state);
if (roads.isEmpty()) {
Request api = new Request(RoadsDataSource.context);
Roads apiRoads = api.fetchRoads(state);
this.data.storeRoads(apiRoads);
roads = loadRoadsFromDb(state);
}
return roads;
}
private List<Road> loadRoadsFromDb(Integer state) {
SQLiteQueryBuilder query = new SQLiteQueryBuilder();
query.setTables(Queries.ROAD_STATE_MATCHES);
Cursor results = query.query(
this.data.getWritableDatabase(),
new String[] {Tables.ROADS + "." + Tables.Roads.ID, Tables.ROADS + "." + Tables.Roads.TYPE, Tables.ROADS + "." + Tables.Roads.NUMBER},
Queries.ROADS_BY_STATE,
new String[] {state.toString()}, null, null, null
);
List<Road> roads = new ArrayList<Road>();
results.moveToFirst();
while (!results.isAfterLast()) {
roads.add(new Road(results.getInt(0), results.getString(1), results.getInt(2)));
results.moveToNext();
}
results.close();
System.out.println(roads.size());
return roads;
}
}
DataStorage.java
public class DataStorage extends SQLiteOpenHelper {
public void storeRoads(Roads roads) {
SQLiteDatabase db = this.getWritableDatabase();
for (Road road : roads.getRoads()) {
ContentValues roadRow = new ContentValues();
roadRow.put(Tables.Roads.ID, road.getId());
roadRow.put(Tables.Roads.TYPE, road.getType());
roadRow.put(Tables.Roads.NUMBER, road.getNumber());
try {
db.insertOrThrow(Tables.ROADS, null, roadRow);
} catch (SQLException e) {
}
ContentValues linkRow = new ContentValues();
linkRow.put(Tables.StatesRoads.STATE_ID, roads.getState());
linkRow.put(Tables.StatesRoads.ROAD_ID, road.getId());
try {
db.insertOrThrow(Tables.STATES_ROADS, null, linkRow);
} catch (SQLException e) {
}
}
}
}
Mo Kargas is right. Your db helper should look more like this http://code.google.com/p/android-notes/source/browse/trunk/src/com/bitsetters/android/notes/DBHelper.java?r=10
This may fix your issue though Replace
SQLiteDatabase db = this.getWritableDatabase();
with this
SQLiteDatabase db;
try {
db = dbHelper.getWritableDatabase();
} catch (SQLiteException e) {
db = dbHelper.getReadableDatabase();
}
I've had huge issues in the past with not closing the database handle - generally I do all db operations inside an SQLiteOpenHelper subclass, keeping a reference to the db, opening and closing it atomically.
I am trying to learn about SQLite databases, but I really hate dealing with any back-end stuff, with a passion. I'm already hitting walls with a seemingly simple problem.
Here is the code that I think matters from the DatabaseHelper class
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "Library";
public static final String TABLE_NAME = "books";
public static final String TITLE = "title";
public static final String AUTHOR = "author";
public static final String ISBN = "isbn";
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, title TEXT, author TEXT, isbn TEXT)");
}
public boolean insertBook(String title, String author, String isdn) {
try {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(TITLE, title);
cv.put(AUTHOR, author);
cv.put(ISBN, isdn);
db.insert(TABLE_NAME, null, cv);
db.close();
return true;
} catch (Exception exp) {
exp.printStackTrace();
return false;
}
}
}
And this is the code in my main activity
dbHelper = new DatabaseHelper(this);
dbHelper.insertBook("Harry Potter", "JK", "1000");
dbHelper.insertBook("Hamlet", "Shakespeare", "500");
Eclipse is telling me that there is an error in the insertBook() method. It says that there is no such table books: .... I have no idea what I am doing wrong here. What makes it more frustrating is that only a couple of minutes before it was working perfectly, then (I think) I dropped the table and it just create it again for whatever reason, even though this code has not changed since I first created it (I think...).
There is an older version of the database on your device, which does have the (empty) database in place, but not the books table. If that's an option for you, just uninstall and reinstall the app.
Later, when you'd like to add a new table to the database during production on end-user devices, but keep existing data, the designated hook to add new tables, alter the schema or upgrade your data is the onUpgrade method of your SQLiteOpenHelper.
I have written a ORM framework for that. https://github.com/ahmetalpbalkan/orman
You can easily write Android applications using SQLite with that. It uses your Java classes (Book, in this case) as database tables (entities).
It even creates your table automatically and you just say book1.insert(), done.
You must uninstall the applcation and then reinstall it. It should work after that.
try this one
for example for insert:
public boolean insertBook(String title, String author, String isdn) {
try {
SQLiteDatabase db = getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(TITLE, title);
cv.put(AUTHOR, author);
cv.put(ISBN, isdn);
***try
{
db.insert(TABLE_NAME, null, cv);
}
catch ( SQLiteException e)
{
onCreate(db);
db.insert(TABLE_NAME, null, cv);
}***
db.close();
return true;
} catch (Exception exp) {
exp.printStackTrace();
return false;
}
}
Nothing about this made any sense, as the table existed from the very beginning of the DB, so we tried different things:
uninstalling app
claning cache / data
changing database version number
forcing to copy the database even if it already existed
If for some reason, you dropped the table, you might want to delete the database to force the application to recreate it correctly. Use adb shell and find the database in /data/data/[package_name]/databases/. You can just delete the file.
I had this problem, but cleaning the project did not fixed it.
It turned out I passed DATABASE_NAME instead of TABLE_NAME.
Make sure that onCreate method called. I am also facing similar type of problem when creating multiple table. If you create a separate class make sure you first clear all the storage data of your app and then again run it ..It will work fine for me.
Also had the issue where I already had the app built with an existing DB, but wanted to add another table.
Besides having the onUpgrade method, I also had to increment the private static final Integer DB_VERSION, which is used when instantiating the DatabaseHelper, like so:
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DB_VERSION);
}
Also from the official documentation, my onUpgrade method looks like this:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// on upgrade drop older tables
db.execSQL("DROP TABLE IF EXISTS " + TABLE_1);
db.execSQL("DROP TABLE IF EXISTS " + TABLE_2);
// create new tables
onCreate(db);
}
Then, after building the app, the new version of the database worked.
Note:
External Database You Access Time must add for the path in Sqliteopenhelper class
public final static String DATABASE_PATH = "/data/data/com.example.shortcuts/databases/";
com.example.shortcuts=Your Package name
db.execSQL(
"CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, isbn TEXT)"
);
remove space after the table name, it should be like this.
db.execSQL(
"CREATE TABLE " + TABLE_NAME + "(id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, author TEXT, isbn TEXT)"
);