Search code examples
androidandroid-sqlitesqliteopenhelper

How to create a cursor from a custom database helper


I'm going through Griffith's Head First: Android Development book and a certain code is throwing a Cannot resolve Symbol error.

I am building a cursor from the custom database helper as per the instruction of the book. This is the code that is throwing the error.

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);

        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);

        try{
            SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
            SQLiteDatabase db = starbuzzDatabaseHelper.getReadableDatabase();

It says Cannot resolve symbol "StarbuzzDatabaseHelper" even though StarbuzzDatabaseHelper does exist in my project. This is code from the book. Not mine. I'm assuming that its an error from the book. I tried this:

SQLiteOpenHelper starbuzzDatabaseHelper = new SQLiteOpenHelper(this); and it still didn't work.

This is my full code:

public class DrinkActivity extends AppCompatActivity {

    public static final String EXTRA_DRINKNO = "drinkNo";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);

        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);

        try{
            SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
            SQLiteDatabase db = starbuzzDatabaseHelper.getReadableDatabase();
            Cursor cursor = db.query(
                    "DRINK",
                    new String[]{"NAME", "DESCRIPTION", "IMAGE_RESOURCE_ID"},
                    "_id = ?",
                    new String[]{Integer.toString(drinkNo)},
                    null, null, null);
            if(cursor.moveToFirst()){
                String nameText = cursor.getString(0);
                String descriptionText = cursor.getString(1);
                int photoId = cursor.getInt(2);

                TextView name = findViewById(R.id.name);
                name.setText(nameText);

                TextView description = findViewById(R.id.description);
                description.setText(descriptionText);

                ImageView photo = findViewById(R.id.photo);
                photo.setImageResource(photoId);
                photo.setContentDescription(nameText);
            }
            cursor.close();
            db.close();
        }catch(SQLiteException e){
            Toast.makeText(this, "Database Unavailable", Toast.LENGTH_SHORT).show();
        }
    }
}

and this is the database helper class:

public class StarbuzzDatabaseHelper extends SQLiteOpenHelper {

    public static final String DB_NAME = "starbuzz";
    public static final int DB_VERSION = 2;

    public StarbuzzDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, DB_NAME, factory, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        updateMyDatabase(db, 0, DB_VERSION);

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        updateMyDatabase(db, oldVersion, newVersion);
    }

    private void updateMyDatabase(SQLiteDatabase db, int oldVersion, int newVersion){
        if(oldVersion <1){

            db.execSQL("CREATE TABLE DRINK ("
                    + "_id INTEGER PRIMARY KEY AUTOINCREMENT, "
                    + "NAME TEXT, "
                    + "DESCRIPTION TEXT, "
                    + "IMAGE_RESOURCE_ID INTEGER);");

            insertDrink(db, "Latte", "Espresso and Steamed Milk", R.drawable.latte);
            insertDrink(db, "Capuccino", "Espresso, hot milk and steamed milk foam", R.drawable.cappuccino);
            insertDrink(db, "filter", "Our best drink coffee", R.drawable.filter);
        }
        else if(oldVersion < 2){
            db.execSQL("ALTER TABLE DRINK ADD COLUMN FAVORITE NUMERIC");
        }
    }

    private static void insertDrink(SQLiteDatabase db, String name, String description, int resourceID){
        ContentValues drinkValues = new ContentValues();
        drinkValues.put("NAME", name);
        drinkValues.put("DESCRIPTION", description);
        drinkValues.put("IMAGE_RESOURCE_D", resourceID);
        db.insert("DRINK", null, drinkValues);
    }
}

Solution

  • You have defined the constructor for the starbuzzDatabaseHelper class to require 4 parameters as per :-

    public StarbuzzDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, DB_NAME, factory, DB_VERSION);
    }
    

    The attempt to instantiate an instance you are using :-

    SQLiteOpenHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
    

    Whilst you could/should be using :-

    StarbuzzDatabaseHelper starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(
        this,StarbuzzDatabaseHelper.DBNAME,
        your_factory,
        StarbuzzDatabaseHelper.DB_VERSION
    );
    
    • i.e. passing 4 parameters (see below)
    • Instantiating (creating an instance) your Database Helper rather than an instance limited by being an SQLiteOpenHelper instance.
    • providing a method that can be resolved.

    I would suggest that you alter the constructor to be :-

    public StarbuzzDatabaseHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
    

    Thus requiring the 1 parameter (the context)

    and then use the following in your activity (activities) :-

    StarbuzzDatabaseHelper starbuzzDatabaseHelper; //<<<< Class Scope for DBHelper
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drink);
    
        int drinkNo = (Integer)getIntent().getExtras().get(EXTRA_DRINKNO);
    
        //<<<< try/catch can be confusing as exceptions may not stop when you very likely should.
        starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
        SQLiteDatabase db = starbuzzDatabaseHelper.getReadableDatabase();
    
        ...... rest of your code
    

    Another issue

    You also have an issue in that the your insertDrink method will not insert drinks because of a typo.

    i.e. the columng when you create the table is IMAGE_RESOURCE_ID INTEGER, yet in the insert you call the column IMAGE_RESOURCE_D. I'd strongly suggest adopting the use of a single source (CONSTANTS) for table and column names and always using those wherever possible.

    I'd suggest the following changes to your StarbuzzDatabaseHelper class this should then remedy/simplify matters :-

    public class StarbuzzDatabaseHelper extends SQLiteOpenHelper {
    
        public static final String DB_NAME = "starbuzz";
        public static final int DB_VERSION = 2;
        public static final String TB_DRINK = "DRINK";
        public static final String COL_DRINK_ID = BaseColumns._ID;
        public static final String COL_DRINK_NAME = "NAME";
        public static final String COL_DRINK_DESCRIPTION = "DESCRIPTION";
        public static final String COL_DRINK_IMAGERESOURCEID = "IMAGE_RESOURCE_ID";
        public static final String COL_DRINK_FAVOURITE = "FAVOURITE";
    
        public StarbuzzDatabaseHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            updateMyDatabase(db, 0, DB_VERSION);
    
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            updateMyDatabase(db, oldVersion, newVersion);
        }
    
        private void updateMyDatabase(SQLiteDatabase db, int oldVersion, int newVersion){
            if(oldVersion <1){
    
                db.execSQL("CREATE TABLE DRINK ("
                        + COL_DRINK_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
                        + COL_DRINK_NAME + " TEXT, "
                        + COL_DRINK_DESCRIPTION + " TEXT, "
                        + COL_DRINK_IMAGERESOURCEID + " INTEGER);");
    
                insertDrink(db, "Latte", "Espresso and Steamed Milk", 1);
                insertDrink(db, "Capuccino", "Espresso, hot milk and steamed milk foam", 2);
                insertDrink(db, "filter", "Our best drink coffee", 3);
            }
            else if(oldVersion < 2){
                db.execSQL("ALTER TABLE DRINK ADD COLUMN FAVORITE NUMERIC"); //<<< rather then null
            }
        }
    
        private static void insertDrink(SQLiteDatabase db, String name, String description, int resourceID){
            ContentValues drinkValues = new ContentValues();
            drinkValues.put("NAME", name);
            drinkValues.put("DESCRIPTION", description);
            //drinkValues.put("IMAGE_RESOURCE_D", resourceID); //<<< NO SUCH COLUMN
            drinkValues.put(COL_DRINK_IMAGERESOURCEID,resourceID); // NO SPELLING ISSUES this way
            db.insert("DRINK", null, drinkValues);
        }
    
        // Alternative insert publicly available so activities can add drinks
        public long insertDrink(String drink_name,
                                String drink_description,
                                int resource_id,
                                double drink_favourite) {
            SQLiteDatabase db = this.getWritableDatabase();
            ContentValues cv = new ContentValues();
            cv.put(COL_DRINK_NAME, drink_name);
            cv.put(COL_DRINK_DESCRIPTION, drink_description);
            cv.put(COL_DRINK_IMAGERESOURCEID,resource_id);
            cv.put(COL_DRINK_FAVOURITE,drink_favourite);
            return db.insert(TB_DRINK,null,cv);
        }
    
        // method to return a Cursor with a sinhle drink as per the id
        public Cursor getDrinkById(long id) {
            String whereclause = COL_DRINK_ID + "=?";
            String[] whereargs = new String[]{String.valueOf(id)};
            SQLiteDatabase db = this.getWritableDatabase();
            return db.query(TB_DRINK,
                    null,
                    whereclause,
                    whereargs,
                    null,
                    null,
                    null
            );
        }
    
        // method to return All drinks as a cursor sort according to drink name
        public Cursor getAllDrinks() {
            return this.getWritableDatabase().query(
                    TB_DRINK,
                    null,
                    null,
                    null,
                    null,
                    null,
                    COL_DRINK_NAME + " ASC"
            );
        }
    }
    

    The following code has been used to test the above and is also an example of how to utilise the class :-

    public class DrinkActivity extends AppCompatActivity {
    
        StarbuzzDatabaseHelper starbuzzDatabaseHelper;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main); //<<<< change to your layout
    
            starbuzzDatabaseHelper = new StarbuzzDatabaseHelper(this);
            Cursor csr = starbuzzDatabaseHelper.getDrinkById(1); //<<< using 1 for testing
            if (csr.moveToFirst()) {
                Log.d("CURSORINFO","Drink name is " + csr.getString(csr.getColumnIndex(StarbuzzDatabaseHelper.COL_DRINK_NAME)));
                //<<<< replace the line above with your code >>>>
            }
        }
    }
    

    Notes

    • as it is unlikely that your database has any data due to the insert I'd suggest delete the database. This can be done by clearing/delete the App's data or by Uninstalling the App.