Почему мой конструктор не вызывается?

Я пытаюсь добавить некоторые записи в таблицу SQLite, но LogCat сообщает мне, что таблица не существует. И DDMS показывает, что да, эта таблица не создается/не была создана.

Тем не менее я создаю таблицу в классе SQLiteOpenHelper:

public class SQLiteHandlerDeliveryItem extends SQLiteOpenHelper {
    . . .
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
                TABLE_DELIVERYITEMS + "("
                + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
                + COLUMN_UPCPLU + " TEXT," + COLUMN_VENDORITEMID + " TEXT,"
                + COLUMN_PACKSIZE + " INTEGER," + COLUMN_DESCRIPTION + " TEXT,"
                //+ COLUMN_COST + " REAL,"  + COLUMN_MARGIN + " REAL," + COLUMN_LISTPRICE + " REAL,"
                + COLUMN_COST + " REAL DEFAULT 0,"  + COLUMN_MARGIN + " REAL DEFAULT 0," + 

COLUMN_LISTPRICE + " REAL DEFAULT 0,"
                + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " TEXT"
                + ")";
        sqLiteDatabase.execSQL(CREATE_DELIVERYITEMS_TABLE);
    }

Я вызываю метод класса, который добавляет записи:

SQLiteHandlerDeliveryItem sqliteHandler = new SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);
sqliteHandler.addDeliveryItem(delItem);

Это должно вызывать конструктор SQLiteHandlerDeliveryItem (когда создается экземпляр sqliteHandler), но это не так! У меня есть точка останова в методе onCreate(), и, конечно же, она никогда не достигается.

Почему? А как заставить вызвать конструктор, чтобы таблица создавалась?

Странно [наиболее] то, что я также поставил точку останова в другом (рабочем) классе SQLiteOpenHelper, и она тоже не достигнута... что?!? Это сработало хотя бы один раз, так как таблица существует/была создана из этого кода.

Так что, очевидно, где-то в моем качелях есть дыра; что я не так понимаю или делаю не так?

ОБНОВИТЬ

Я слишком рано пометил ответ как ответ.

As to:

"1. В какой-то момент вы должны вызвать getWritableDatabase() или getReadableDatabase()."

Я делаю вызов getWritableDatabase() в каждом методе, создающем или считывающем записи, например так:

public long addDeliveryItem(DeliveryItem delItem) {
    long IdAdded = 0;
    ContentValues values = new ContentValues();
    values.put(COLUMN_INVOICENUM, delItem.get_invoiceNumber());
    . . .
    values.put(COLUMN_QTY, delItem.get_quantity());

    SQLiteDatabase db = this.getWritableDatabase(); <= Rot Cheer

    if (db != null) {
        IdAdded = db.insert(TABLE_DELIVERYITEMS, null, values);
    }
    . . .

...и по поводу:

"2. В вашем конструкторе для SQLiteHandlerDeliveryItem вы должны вызвать super(...)".

Я делаю вызов super в классе, расширяющем SQLiteOpenHelper:

public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)     
{
    super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}

Итак... я до сих пор не знаю, в чем проблема/решение...

ОБНОВЛЕНИЕ 2

Итак, что я вижу при попытке вставить запись в эту таблицу в LogCat:

E/SQLiteLog﹕ (1) нет такой таблицы: deliveryitems

Тем не менее, мой код, который пытается добавить запись, создает соответствующий/соответствующий класс, который расширяет SQLiteOpenHelper следующим образом:

else if ("Delivery Items".equals(tableName)) {
    try {
        JSONArray jsonArr = new JSONArray(result);
        for (int i = 0; i < jsonArr.length(); i++) {
            JSONObject jsonObj = jsonArr.getJSONObject(i);
            String invNum = jsonObj.getString("invoiceNumber");
        . . .                        
        // Prepare for writing to db
        DeliveryItem delItem = new DeliveryItem();
        delItem.set_invoiceNumber(invNum);
        . . .                        
        SQLiteHandlerDeliveryItem sqliteHandler = new  
            SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);
        sqliteHandler.addDeliveryItem(delItem);
    }
}

... и этот класс имеет код для создания таблицы:

public class SQLiteHandlerDeliveryItem extends SQLiteOpenHelper {
        . . .
public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)   
{
    super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
    String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
            TABLE_DELIVERYITEMS + "("
            + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
            + COLUMN_UPCPLU + " TEXT," + COLUMN_VENDORITEMID + " TEXT,"
            + COLUMN_PACKSIZE + " INTEGER," + COLUMN_DESCRIPTION + " TEXT,"
            + COLUMN_COST + " INTEGER,"  + COLUMN_MARGIN + " INTEGER," + COLUMN_LISTPRICE + " INTEGER,"
            + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " TEXT"
            + ")";
    sqLiteDatabase.execSQL(CREATE_DELIVERYITEMS_TABLE);
}

Итак... что я делаю неправильно или не делаю правильно?

ОБНОВЛЕНИЕ 3

Правда таблица не создается. Сообщение об ошибке в LogCat указывает на то, что это так (это «нет такой таблицы: deliverytitems»).

Мой конструктор выглядит так:

@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
            TABLE_DELIVERYITEMS + "("
            + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
            + COLUMN_UPCPLU + " TEXT," + COLUMN_VENDORITEMID + " TEXT,"
            + COLUMN_PACKSIZE + " INTEGER," + COLUMN_DESCRIPTION + " TEXT,"
            + COLUMN_COST + " INTEGER,"  + COLUMN_MARGIN + " INTEGER," + COLUMN_LISTPRICE + " INTEGER,"
            + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " TEXT"
            + ")";
    db.execSQL(CREATE_DELIVERYITEMS_TABLE);
}

Этот код действительно не вводится. Итак, через какой обруч я должен прыгнуть, чтобы вызвать конструктор?

Я думаю, что это произойдет, когда я создам экземпляр класса:

SQLiteHandlerDeliveryItem sqliteHandler = new 
    SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);

Даже когда он вызывается, как конструктор узнает, что такое arg (SQLiteDatabase db) - откуда он получает это значение?

У меня есть база данных с одной таблицей. Он просто отказывается добавлять эту вторую таблицу.

Как кто-то где-то рекомендовал, я добавляю отдельный класс, который расширяет SQLiteOpenHelper для каждой таблицы, которую я хочу добавить в базу данных.

Когда я дохожу до этой строки:

SQLiteHandlerDeliveryItem sqliteHandler = new 
    SQLiteHandlerDeliveryItem(SQLiteActivity.this, null);

... и нажмите F7, чтобы войти в него, я доберусь до конструктора класса:

public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)  
{
    super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}

... но не событие onCreate.

Так что «неудивительно», что у меня нет таблицы deliveryitems, так как код для создания указанной таблицы никогда не достигается; но ПОЧЕМУ он не достигается - что я должен сделать, чтобы он БЫЛ достигнут?


person B. Clay Shannon    schedule 12.04.2014    source источник
comment
Почему у вас есть отдельный SQLiteOpenHelper для каждой таблицы? SQLiteOpenHelper обрабатывает одну базу данных (то есть один файл .db в файловой системе Android), и одна база данных может содержать несколько таблиц.   -  person stackoverflowuser2010    schedule 14.04.2014
comment
Как я уже сказал, это была рекомендация, которую я где-то читал (не могу вспомнить, где)   -  person B. Clay Shannon    schedule 15.04.2014
comment
Даже в этом случае (при условии, что я перемещаю весь код создания БД в один класс), событие onCreate() этого исходного класса также не вызывается... кажется, должен быть способ сообщить приложению, что у вас есть новая таблица для обработки...   -  person B. Clay Shannon    schedule 15.04.2014
comment
Вам нужно полностью удалить приложение и повторить попытку.   -  person stackoverflowuser2010    schedule 15.04.2014


Ответы (3)


  1. В какой-то момент вы должны позвонить getWritableDatabase() или getReadableDatabase().

  2. В вашем конструкторе для SQLiteHandlerDeliveryItem вы должны вызвать super(...).

person stackoverflowuser2010    schedule 12.04.2014
comment
Я вызываю getWritableDatabase() в каждом методе, создающем или считывающем записи, например так: SQLiteDatabase db = this.getWritableDatabase(); ... но вы хотите сказать, что такой вызов нужен и в конструкторе? - person B. Clay Shannon; 14.04.2014

Во-первых, метод SQLiteOpenHelper.onCreate() не является конструктором. Я не уверен, но похоже, что вы смешиваете две концепции в вопросе.


Теперь попробуем решить проблему...

Похоже, ваша таблица не создается. Из онлайн-справки для SQLiteOpenHelper.onCreate():

public abstract void onCreate (SQLiteDatabase db)

Called when the database is created for the first time. This is where the creation of tables and the initial population of the tables should happen.

Таким образом, этот метод будет вызываться только один раз для типичного варианта использования — при первой установке приложения (сначала использует базу данных). В этот момент вызывается onCreate(), который создает фактические таблицы. Но это не вызывается каждый раз, когда вы запускаете приложение.

Мой совет проверить, не в этом ли проблема — удалить приложение и повторить попытку. Поставьте точку останова в этом методе, чтобы убедиться, что он действительно запускается.

Иногда может случиться так, что у нас есть база данных в приложении, затем мы обновляем что-то, чтобы добавить таблицу, и забываем удалить. Другой правильный способ сделать это — использовать метод onUpgrade() для добавления новой таблицы.

Дайте мне знать, как это работает, или если я неправильно понял проблему.

person Richard Le Mesurier    schedule 14.04.2014
comment
Под удалением приложения вы имеете в виду эмулятор? Если да, то я не знаю, как это делается...? - person B. Clay Shannon; 14.04.2014
comment
да, если вы разрабатываете на эмуляторе. Самый простой способ удалить из командной строки, предполагая, что эмулятор подключен к adb: adb uninstall my.app.package.in.manifest (используя реальное имя пакета приложения, как указано в манифесте) - person Richard Le Mesurier; 14.04.2014
comment
удаляя, вы удаляете БД. Когда база данных потребуется в следующий раз, когда вы ее запустите, система не найдет базу данных, и поэтому она вызовет onCreate() для вас. - person Richard Le Mesurier; 14.04.2014
comment
Будет ли он вызывать ВСЕ onCreate()? У меня есть класс, расширяющий SQLiteOpenHelper для каждой таблицы; должен ли я иметь только одну, в которой ВСЕ таблицы создаются в onCreate ()? - person B. Clay Shannon; 14.04.2014
comment
Разве это удаление не удалит больше, чем просто базу данных? Могу ли я просто зайти в Android Debug Monitor и вручную удалить оттуда файл db? Казалось бы, проще...? - person B. Clay Shannon; 14.04.2014
comment
хм, интересный вопрос. Насколько я понимаю, вызов getWriteableDatabase() запускает обратные вызовы onCreate() или onUpgrade(). Таким образом, разделение вашей реализации между различными помощниками может быть причиной проблемы. Перечитав документацию, на которую я ссылался, я думаю, что лучше всего объединить ваши различные вспомогательные классы в один или наследовать их все от общей базы, которая создает все таблицы. - person Richard Le Mesurier; 14.04.2014

Как я и надеялся, это оказалось простым решением: просто увеличьте значение версии вашей базы данных. Я прочитал это на стр. 262 книги О'Рейли «Программирование Android»:

DATABASE_VERSION ...Если версия базы данных на компьютере меньше DATABASE_VERSION, система запускает метод onUpgrade для обновления базы данных до текущего уровня.

Таким образом, все, что вам нужно сделать, это увеличить это число:

    private static final int DATABASE_VERSION = 2; 
    // I changed it from "1" to "2", but you could change it to anything you want, I reckon (42, or 1776, or whatever).

Увеличение значения версии приводит к запуску onUpgrade, который удаляет старую версию базы данных, а затем вызывает onCreate:

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_DELIVERYITEMS);
    onCreate(db);
}

Затем событие onCreate() делает именно это — добавляет DDL для создания таблицы.

Вот соответствующий код в классе, который расширяет SQLiteOpenHelper в контексте:

public class SQLiteHandlerDeliveryItem extends SQLiteOpenHelper {

private static final int DATABASE_VERSION = 2; 
private static final String DATABASE_NAME = "HHS.db";
private static final String TABLE_DELIVERYITEMS = "deliveryitems";

private static final String COLUMN_ID = "_id";
. . .
private static final String COLUMN_QTY = "quantity";

public SQLiteHandlerDeliveryItem(Context context, SQLiteDatabase.CursorFactory factory)  
{
    super(context, DATABASE_NAME, factory, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
    String CREATE_DELIVERYITEMS_TABLE = "CREATE TABLE " +
            TABLE_DELIVERYITEMS + "("
            + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_INVOICENUM + " TEXT,"
. . .
            + COLUMN_DEPTNUM + " INTEGER," + COLUMN_SUBDEPT + " TEXT," + COLUMN_QTY + " 
TEXT"
            + ")";
    db.execSQL(CREATE_DELIVERYITEMS_TABLE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE_DELIVERYITEMS);
    onCreate(db);
}
. . .

Поэтому мне кажется, что наличие нескольких классов, расширяющих SQLiteOpenHelper, - это хорошо, поскольку вы можете таким образом разделить свой код, а не иметь одну гигантскую/огромную пару спагетти onUpgrade/onCreate.

person B. Clay Shannon    schedule 15.04.2014