Database unclosed cursor. Close it after insert method - android

Would you mind pointing out what's wrong with this peace of code. genresCursor contains "Application did not close the cursor or database object that was opened here" exception. How do I really close this cursor after inserting ?
Thanks.
UPD: It seems like there wasn't a problem at all. Even though it contains exception that's still possible to extract data. I must've been wrong in my real application and this exception led me to conclusion that's it had been the issue. Thanks everyone for participating.
public class DatabaseCursorActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
HashMap<Integer, String> _dummy = new HashMap<Integer, String>();
OpenDatabaseHelper helper = new OpenDatabaseHelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(OpenDatabaseHelper.GENRES_ID_KEY, 1);
values.put(OpenDatabaseHelper.GENRES_TITLE_KEY, "Test");
db.insert(OpenDatabaseHelper.GENRES_TABLE_NAME, null, values);
db.close();
helper.close();
db = helper.getReadableDatabase();
Cursor genresCursor = db.query(OpenDatabaseHelper.GENRES_TABLE_NAME, new String[]{OpenDatabaseHelper.GENRES_ID_KEY, OpenDatabaseHelper.GENRES_TITLE_KEY }, null, null, null, null, null);
int i = genresCursor.getColumnCount();
genresCursor.moveToFirst();
}
public class OpenDatabaseHelper extends SQLiteOpenHelper {
public static final String GENRES_TABLE_NAME = "genres";
public static final String GENRES_ID_KEY = "id";
public static final String GENRES_TITLE_KEY = "title";
public OpenDatabaseHelper(Context context) {
super(context, "ttt.db", null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + GENRES_TABLE_NAME + "( id integer primary key not null, title text);" );
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}

You are opening the database first and after inserting you closed the database and then opened again to read the value.
First thing you don't need to close and open it again.You can do all of your operation and can close it as you are still in same block of code.
After doing the operation with your genresCursor close it by by genresCursor.close() and then close your db.Hopefully it will be fine.

I do not understand why you insert this code:
db = helper.getReadableDatabase();
Cursor genresCursor = db.query(OpenDatabaseHelper.GENRES_TABLE_NAME, new String[]{OpenDatabaseHelper.GENRES_ID_KEY, OpenDatabaseHelper.GENRES_TITLE_KEY }, null, null, null, null, null);
int i = genresCursor.getColumnCount();
genresCursor.moveToFirst();
The problem is in this part of code. If you just want to close cursor then call genresCursor.close(). But from the architecture point of view I do not understand why you need this code.

The Best Practice to work with cursor and database is that you should open it and close it in the same block of code. When I use to insert rows into database I use to open it at start and then insert many column and close it just before returning.
In API before Honeycomb you wont get any log message and performance issue regarding unclosed cursor but API before Honeycomb has a Cursor not closed detector which will log message about cursor not being closed.
Edit:
Close Database within same block of code. Cursor can be closed as per the scenario but must be closed before Context Deletion.

Related

SQLite problem of no such column when trying to insert

I use Sqlite and I have the following DB , I am developing an application for women which it should contain a login and sign up . I had problems in the database like "no such column"
DataBase
public class DB_MJG extends SQLiteOpenHelper {
public static final String name ="dataB.db";
public static final int version =1;
//Les atts de la table FEMME
public static final String table_Femme ="Femme";
public static final String ID_F = "id";
public static final String NOM_F ="nom";
public static final String PRENOM_F="prenom";
public static final String PSEUDO="pseudo";
public static final String MDP="mdp";
public static final String GRP_F="grpSang";
public static final String AGE_F="age";
public static final String POIDS="poids";
public DB_MJG( Context context) {
super(context, name, null, version);
}
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_TABLE_FEMME = "CREATE TABLE " + table_Femme + "(
"+ID_F+" INTEGER PRIMARY KEY AUTOINCREMENT, "
+NOM_F+" TEXT, "+PRENOM_F+" TEXT " + ")";
db.execSQL(CREATE_TABLE_FEMME);
System.out.println("table femme crée");
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
db.execSQL("DROP TABLE IF EXISTS "+table_Femme);
db.execSQL("DROP TABLE IF EXISTS "+table_Enfant);
}
//insérer dans la table FEMME
public void insertFemme(Femme f)
{
SQLiteDatabase db = this.getWritableDatabase();
ContentValues vals = new ContentValues();
vals.put(NOM_F,f.getNom());
vals.put(PRENOM_F,f.getPrenom());
db.insert(table_Femme,null,vals);
db.close();
}
public ArrayList getFemme()
{
ArrayList<Femme> femmes = new ArrayList<>();
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = db.rawQuery("SELECT * FROM " +table_Femme, null);
while(c.moveToNext()){
Femme f = new Femme(c.getString(1),c.getString(2));
femmes.add(f);
}
return femmes;
}
}
Launcher Activity
public class MainActivity extends AppCompatActivity {
DB_MJG db = new DB_MJG(this);
SQLiteDatabase database ;
String s = "";
private Button log,sign;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
database = openOrCreateDatabase(db.name, Context.MODE_PRIVATE, null) ;
db.insertFemme(new Femme("sara","sara"));
ArrayList<Femme> femmes = db.getFemme();
TextView textView= (TextView) findViewById(R.id.textView13);
for(Femme f : femmes){
String ch = "Nom :" +f.getNom() + " Prenom : "
+f.getPrenom()+"\n";
s = s +ch;
}
textView.setText(s);
}
Error
E/SQLiteLog: (1) table Femme has no column named nom
E/SQLiteDatabase: Error inserting nom=sara prenom=sara
android.database.sqlite.SQLiteException: table Femme has no column named
nom (code 1 SQLITE_ERROR): , while compiling: INSERT INTO
Femme(nom,prenom) VALUES (?,?)
at
android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native
Method)
When I compile,it says no such column. I have defined the nom column. The database has the nom that I am trying to use for inserting but it doesn't work.
The primary issue you have is that the database helper's onCreate method is only called when the database is created, which is once in it's lifetime.
As such if any changes are made to the structure (schema) by amending the create table SQL, as used in the onCreate method, they will not be reflected unless the database is deleted or that the onCreate method is invoked some other way.
Often such changes, as least when developing the app, are applied by first doing one of the following :-
Deleting the App's data (this deletes the database, so onCreate is automatically called).
Uninstalling the App (and as a result deletes the App's data).
IF the onUpgrade method is written to drop the changed table(s) and recreate the tables (often/generally by calling the onCreate method)
After doing one of the above, rerunning the App will then result in the structure change being applied.
Note the above will result in the loss of existing data and IS NOT SUITABLE for an App that has been deployed.
In your case if using option 3, the onUpgrade method needs to include the creation of the table(s) after they have been dropped as it only drops the tables.
You code in the MainActivity is also overly complex in that it utilises opening the database without using the SQLiteOpenHelper to open the database but rather opens it using the SQLiteDatabase openOrCreate method (which doesn't result in the SQLiteOpenHelper's (DB_MJG is a subclass of SQLiteOpenHelper) onCreate method being called). However, by a quirk/luck, when you do start to use the instance of DB_MJG, namely db, as the instance was created before openOrCreate method, it actually goes on to call the onCreat method.
However, it would be much simpler, to just use one method to open the database.
As such I'd suggest implementing using just the DB_MJG DatabseHelper.
Fix
The essential fix, is to introduce the changed structure. So one of the 3, above should be taken.
if using 3. then amending the onUpgrade method to call the onCreate method and then increasing the version number would be required. That is the onUpgrade method could be :-
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
db.execSQL("DROP TABLE IF EXISTS "+table_Femme);
db.execSQL("DROP TABLE IF EXISTS "+table_Enfant);
onCreate(db); //<<<<<<<<<< ADDED so that onUpgrade creates the tables after they have been dropped
}
Additional/Recommended
Close Cursor
in DB_MJG.java the getFemme method leaves the Cursor open. This can result in a too many open databases or cursors excpetion so it is suggested that the line :-
c.close();
is added to the getFemme method, so it becomes :-
public ArrayList getFemme()
{
ArrayList<Femme> femmes = new ArrayList<>();
SQLiteDatabase db = this.getWritableDatabase();
Cursor c = db.rawQuery("SELECT * FROM " +table_Femme, null);
while(c.moveToNext()){
femmes.add(new Femme(c.getString(c.getColumnIndex(NOM_F)),c.getString(c.getColumnIndex(PRENOM_F))));
}
c.close(); //<<<<<<<<<< should always close a cursor when finished with it
return femmes;
}
Note the above also does away with the need for the intermediate Femme object f.
Use DB_MJG to open the database
There is no need to openOrCreate the database when using a subclass of SQliteOpenHelper as it will do this. So MainActivity.java could be :-
public class MainActivity extends AppCompatActivity {
DB_MJG db; // Only declare the DB_MJG object.
private Button log,sign;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = new DB_MJG(this);
db.insertFemme(new Femme("sara","sara"));
ArrayList<Femme> femmes = db.getFemme();
TextView textView= (TextView) findViewById(R.id.textView13);
StringBuilder sb = new StringBuilder(); //<<<<<<<<<< ADDED - Use StringBuilder in a loop
for(Femme f : femmes){
sb.append("Nom: ").append(f.getNom()).append(" Prenom: ").append(f.getPrenom());
}
textView.setText(sb.toString());
}
}
Note that the above should be changed at the same time or after the fix has been applied.
Instead of concatenating Strings in a loop a StringBuilder has been used. See -Why to use StringBuffer in Java instead of the string concatenation operator

How to get PRIMARY KEY ID of objects in DataBase -android sqlite 3

I want to get a primary key of a saved object in a table in database I wrote a class to handle my database I want to add a function to it for getting the Id (I tried to give id to objects manually it didn't go well so I prefer the primary key id)so how should this function look like?and also if u see a thing that needs changing in my code please let me know.
public class DataBaseHandler extends SQLiteOpenHelper {
private static int _ID =0;
private int ID =0;
private ArrayList<marker_model> markerList=new ArrayList<>();
public DataBaseHandler(Context context) {
super(context, Constans.TABLE_NAME, null, Constans.DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+Constans.TABLE_NAME+
" ("+Constans.MARKER_ID+" INTEGER PRIMARY KEY, "+
Constans.MARKER_TITLE+" TEXT, " +Constans.MARKER_DESCRIPTION+" TEXT ,"+Constans.My_MARKER_ID+" INT );");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS "+Constans.TABLE_NAME);
onCreate(db);
}
public void AddMarker(marker_model marker){
marker.set_Id(_ID);
SQLiteDatabase db=this.getWritableDatabase();
ContentValues values=new ContentValues();
values.put(Constans.MARKER_TITLE,marker.getTitle());
values.put(Constans.My_MARKER_ID,marker.get_Id());
values.put(Constans.MARKER_DESCRIPTION,marker.getDescription());
db.insert(Constans.TABLE_NAME,null,values);
db.close();
Log.d(TAG, "AddMarker: Successfully added to DB");
_ID++;
}
public ArrayList<marker_model> getMarkers(){
markerList.clear();
SQLiteDatabase db =getReadableDatabase();
Cursor cursor=db.query(Constans.TABLE_NAME
,new String[]{Constans.My_MARKER_ID,Constans.MARKER_TITLE,
Constans.MARKER_DESCRIPTION},null,null,null,null,null);
if (cursor.moveToFirst()){
do {
ID=0;
marker_model model=new marker_model();
model.set_Id(_ID);
model.setDescription(cursor.getString(cursor.getColumnIndex(Constans.MARKER_DESCRIPTION)));
model.setTitle(cursor.getString(cursor.getColumnIndex(Constans.MARKER_TITLE)));
markerList.add(model);
ID++;
}while(cursor.moveToNext());
}
cursor.close();
db.close();
return markerList;
}
public int getMarkerPrimaryId(Marker marker){
}
}
Assuming that you want to get the _id (the primary key) from the database and that marker is an instance of a marker_model object AND that
marker_model has methods getTitle and getDescription that return a string with the respective values, then something along the lines of the following would work.
public long getMarkerPrimaryId(Marker marker){
long rv = 0;
SQLiteDatabase db = getReadableDatabase();
String[] columns = new String[]{Constans.My_MARKER_ID};
String whereclause = Constans.MARKER_TITLE + "=?" +
Constans.MARKER_DESCRIPTION + "=?";
String[] whereargs = new String[]{
marker.getTitile,
marker.getDescription
}
Cursor cursor = db.query(Constans.TABLE_NAME,
columns,
whereclause,
whereargs,
null,null,null);
if (cursor.getCount() > 0) {
cursor.moveToFirst();
rv = cursor.getLong(cursor.getColumnIndex(Constans.My_MARKER_ID);
}
cursor.close;
db.close;
return rv;
}
However, if your issue is that getMarkers is not setting the Id member appropriately (i.e. to match the id in the database), then changing model.set_Id(_ID);
to
model.set_Id(cursor.getLong(cursor.getColumnIndex(Constans.My_MARKER_ID));
would suffice.
If your expectation is that an automatically generated incrementing _id is to be used the addMarker is a little flawed. Simply by removing the line values.put(Constans.My_MARKER_ID,marker.get_Id()); will result in _id being automatically generated (which how _id's tend to be used).
The following (BACKGROUND paragraph mostly) explains much about automatically generated unique identifiers (even though it is about AUTOINCREMENT you likely DO NOT want to code AUTOINCREMENT).
Id suggest that rather than :-
if (cursor.moveToFirst()){
do {
...
}while(cursor.moveToNext());
using :-
while (cursor.moveToNext() {
....
}
is simpler (a cursor, when created, will be positioned to before the first row (moveToPosition(-1) has the same effect) , moveToNext() will move to the first row the first time, if there are no rows the loop will not be entered (you may wish to consider this and the state of returned markerlist)).
Note! the above has been written without testing, so there may be the odd mistake.

Best practice to implement method getXXX from db using cursor for Android

For example code below. Do we need to close cursor? Do we better use try/catch/finally instead of using if()?
public int getCount() {
final Cursor countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
if (countCursor == null) {
return 0;
}
countCursor.moveToFirst();
final int count = countCursor.getInt(0);
return count;
}
The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
The answer I believe is primarily opinion-based. It depends I guess on coder's preference and the circumstances.
I have always preferred the if (cursor != null) or vice versa approach. Unless something truly spectacular has happened; which will be handled by throws Exception, I'd use if-else checks wherever I want the reader/reviewer to see which parts are really and truly exceptions and which are occurrences of different possible/valid scenarios.
This brings us to the current problem of Curosr and applying null checks.
AFAIK (since mostly a Cursor is related with a SQLiteDatabase) a ContentResolver.query() should never return a null Cursor if the query itself is valid unless in case of an invalid query which is a real exception and you should instead get an Exception.
So in my opinion the best approach would be using your example either
public int getCount() throws Exception {
Cursor countCursor;
try {
countCursor = contentResolver.query(
AnalyticContract.CONTENT_URI,
new String[] {"count(*) AS count"},
null,
null,
null);
countCursor.moveToFirst();
return countCursor.getInt(0);
}
finally {
cursor.close();
}
}
Or a variation where Exception is caught and handled within the method itself.
Now to answer your second question whether or not you should close() a Cursor: you should always close a Cursor. Whenever you don't have need for it. If you delve deeper into any of the Cursor.close() method-implementations. Since Curosr is an interface which in case of SQLite is implemented by SQLiteCursor you will notice that this method releases any and all allocations held by it.
I prefer to make a database helper class and through that database access becomes much much easier. Sample of a database helper Class -
public class DatabaseHelperClass extends SQLiteOpenHelper {
public DatabaseHelperClass(Context context)
{
super(context,"hakeem.db",null,1);
}
//Tables
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("Your SQL Query to create a table");
}
//Delete Tables
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table table_name if exists");
onCreate(db);
}
//Insertion of data into tables
long insertData(Various Parameters you like to pass for insertion)
{
SQLiteDatabase db=getWritableDatabase();
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return db.insert("signup_details",null,values);
}
//Delete record
public int deleteData(int id)
{
SQLiteDatabase sb=getWritableDatabase();
return sb.delete("hospital_details","id="+id,null);
}
//Update data in table
int updateData(Parameters you want to pass for update. Make sure you include a primary key)
{
ContentValues values=new ContentValues();
values.put("col_name1",value);
values.put("col_name2",value);
values.put("col_name3",value);
values.put("col_name4",value);
return getWritableDatabase().update("signup_details",values,"id="+id,null);
}
//Read data from tables
Cursor getSigninDetails() { return getWritableDatabase().rawQuery("select * from table_name",null); }
}
and to access results from the database-
private void getDataFromDatabase() {
Cursor cursor = db.getUserData();
if (cursor.moveToFirst()) {
do {
var_name1= cursor.getString(0);
var_name2= cursor.getString(1);
var_name3= cursor.getString(2);
var_name4= cursor.getString(3);
} while (cursor.moveToNext());
}
cursor.close();
}

Fetch data from existing sqlite database

I am having an existing sqlite database. I am developing an android app and I want to connect it with this existing sqlite DataBase.
Problem 1:
I have already included the sqlite database in my project via "DDMS push database function" as per my instructor's advise. Now I want to fetch the data from database, do I need to use SQLiteOpenHelper. If yes, how to use it and what will be coded in onCreate(SQLiteDatabase db) function and onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) function as we already have the Database with us, we don't really need to create it.
Problem 2:
What should be done to simply fetch the required data from the existing database.
Being a newbie, I am quite confused, can someone please explain these concepts and guide me to overcome this issue. Any help would be appreciated.
I have seen a tutorial also for this purpose as sugggested by #TronicZomB, but according to this tutorial (http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/), I must be having all the tables with primary key field as _id.
I have 7 tables namely destinations, events, tour, tour_cat, tour_dest, android_metadata and sqlite_sequence. Out of all, only tour_dest is not fulfilling the conditions of having a primary key named as _id. How to figure out this one?
Following is the screenshot of table which is lacking the primary key field necessary for binding id fields of database tables.
The onCreate and onUpgrade methods will be empty since you already have the database. There is a great tutorial on how to achieve this here.
You could then access the database like such (example):
public ArrayList<String> getValues(String table) {
ArrayList<String> values = new ArrayList<String>();
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT value FROM " + table, null);
if(cursor.moveToFirst()) {
do {
values.add(cursor.getString(cursor.getColumnIndex("value")));
}while(cursor.moveToNext());
}
cursor.close();
db.close();
return values;
}
Unless you are very comfortable with queries, databases, etc. I highly recommend you use http://satyan.github.io/sugar/ , it will also remove a lot of the boiler plate code required to do sqlite in Android
1. If DB already exists, onCreate will not invoke. onUpgrade will be invoked only if you will change DB version. onUpgrade you should to use if there some changes in your APP's database, and you have to make migration on new structure of data smoothly.
public class DbInit extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "name";
private static final int DATABASE_VERSION = 3;
private static final String DATABASE_CREATE = "create table connections . .. . ...
public DbInit(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (isChangeFromToVersion(1, 2, oldVersion, newVersion)) {
//Execute UPDATE here
}
}
private boolean isChangeFromToVersion(int from, int to, int oldVersion, int newVersion ) {
return (from == oldVersion && to == newVersion);
}
....
2. Simple example how to open connection to DB and get cursor object.
public class DAO {
private SQLiteDatabase database;
private DbInit dbHelper;
public ConnectionDAO(Context context) {
dbHelper = new DbInit(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public Connection getConnectionById(long id) {
Cursor cursor = null;
try {
open();
cursor = database.query(DbInit.TABLE_CONNECTIONS, allColumns, DbInit.COLUMN_ID + " = '" + id + "'", null, null, null, null);
if (!cursor.moveToFirst())
return null;
return cursorToConnection(cursor);
} finally {
if (cursor != null)
cursor.close();
close();
}
}
private Connection cursorToConnection(Cursor cursor) {
Connection connection = new Connection();
connection.setId(cursor.isNull(0) ? null : cursor.getInt(0));
connection.setName(cursor.isNull(1) ? null : cursor.getString(1));
.....
.....
return connection;
}

Android Database Update Record not working?

I have created a database. And opened it :
public SQLiteDatabase open() {
String path = "/data/data/com.develop.assetcapture/databases/Asset_Directory";
db = SQLiteDatabase.openOrCreateDatabase(path, null);
return db;
}
I'm writing a user registration and what to enable them to reset their passwords.
I have no problem inserting new records or querying the database. However on my resetPassword(username,password) method I get an error message:
android.database.sqlite.DatabaseObjectNotClosedException:
Application did not close the cursor or database object that was opened here
Please help, I've been stuck on this for too long now.
When you query the database you usually receive a Cursor object. Here is an example:
public Cursor querySomething() {
final String whereClause = ...
final String[] params = ...
final Cursor c = this.getReadableDatabase().query(
Table.TABLE_NAME,
Table.allColumnNames(),
whereClause,
params, // parameters for where clause
null, // group
null, // having
null); // order
return c;
}
Using the cursor c you access the rows in the answer. When you have finished processing the answer you have to close the cursor object:
c.close();

Categories

Resources