SimpleCursorAdapter не загружает внешнюю базу данных sqlite: ошибка _id

Ошибка говорит: column _id does not exists, но столбец находится в базе данных (установлен как первичный ключ), а этот находится во внешней папке SD. Я пытаюсь вернуть значения, содержащиеся в базе данных, при начальной загрузке действия, но кажется, что курсор ничего не возвращает.

public class ComponentsDbAdapter {

  public static final String COLUMN_ID = "_id";
  public static final String COLUMN_SUBSTRUCTURE = "substructure";
  public static final String COLUMN_TYPE = "type";
  public static final String COLUMN_ORDERNUM = "ordernum";
  public static final String COLUMN_INSTALLATION = "installation";
  private static final String TAG = "ComponentsDbAdapter";
  private DatabaseHelper mDbHelper;
  private SQLiteDatabase mDb;
  private static final String DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ "/DATABASE_BACKUP/IMPORTED/";
  private static final String DATABASE_NAME = "android.db";
  private static final String TABLE_NAME = "TAB_WORKSCPE";
  private static final int DATABASE_VERSION = 1; 
  private final Context mCtx;


  public ComponentsDbAdapter open() throws SQLException {
    mDbHelper = new DatabaseHelper(mCtx);
    mDb = mDbHelper.getWritableDatabase();
    return this;
  }

  private static class DatabaseHelper extends SQLiteOpenHelper {
    DatabaseHelper(Context context) {
        super(context, DATABASE_PATH+DATABASE_NAME, null, DATABASE_VERSION);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null); 
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
    }
  }

  public ComponentsDbAdapter(Context ctx) {
    this.mCtx = ctx;
  }

  public void close() {
    if (mDbHelper != null) {
        mDbHelper.close();
    }
 }

  public Cursor fetchComponentsByName(String inputText) throws SQLException {
    Log.w(TAG, inputText);
    Cursor mCursor = null;
    if (inputText == null  ||  inputText.length () == 0)  {
        mCursor = mDb.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null); 
} else {
    mCursor = mDb.query(true, TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, COLUMN_TYPE + " like '%" + inputText + "%'", null, null, null, null, null);
}
    if (mCursor != null) {
        mCursor.moveToFirst();
    }
    return mCursor; 
}

public Cursor fetchAllComponents() {
    Cursor mCursor = mDb.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
    if (mCursor != null) {
        mCursor.moveToFirst();
    }
    return mCursor;
  }
}





public class AndroidListViewCursorAdaptorActivity extends Activity {

private ComponentsDbAdapter dbHelper;
private SimpleCursorAdapter dataAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    dbHelper = new ComponentsDbAdapter(this);
    dbHelper.open();

    //Generate ListView from SQLite Database
    displayListView();
}

private void displayListView() {
    Cursor cursor = dbHelper.fetchAllComponents();

    // The desired columns to be bound
    String[] columns = new String[] {
        ComponentsDbAdapter.COLUMN_SUBSTRUCTURE,
        ComponentsDbAdapter.COLUMN_TYPE,
        ComponentsDbAdapter.COLUMN_ORDERNUM,
        ComponentsDbAdapter.COLUMN_INSTALLATION
    };

    // the XML defined views which the data will be bound to
    int[] to = new int[] {
        R.id.inst,
        R.id.subdt,
        R.id.type,
        R.id.ordernum,
    };

    // create the adapter using the cursor pointing to the desired data
    //as well as the layout information
    dataAdapter = new SimpleCursorAdapter(
        this, 
        R.layout.country_info,
        cursor,
        columns,
        to,
        0);

    ListView listView = (ListView) findViewById(R.id.listView1);
    // Assign adapter to ListView
    listView.setAdapter(dataAdapter);


    listView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> listView, View view,
            int position, long id) {
            // Get the cursor, positioned to the corresponding row in the result set
            Cursor cursor = (Cursor) listView.getItemAtPosition(position);

            // Get the state's capital from this row in the database.
            String compSubdt = cursor.getString(cursor.getColumnIndexOrThrow("subdt"));
            Toast.makeText(getApplicationContext(), compSubdt, Toast.LENGTH_SHORT).show();
        }
    });

    EditText myFilter = (EditText) findViewById(R.id.myFilter);
    myFilter.addTextChangedListener(new TextWatcher() {

        public void afterTextChanged(Editable s) {
        }

        public void beforeTextChanged(CharSequence s, int start,int count, int after) {
        }

        public void onTextChanged(CharSequence s, int start,int before, int count) {
            dataAdapter.getFilter().filter(s.toString());
        }
    });

    dataAdapter.setFilterQueryProvider(new FilterQueryProvider() {
        public Cursor runQuery(CharSequence constraint) {
            return dbHelper.fetchComponentsByName(constraint.toString());
        }
    });
 }
 }

person user2224135    schedule 29.03.2013    source источник
comment
Не вижу вашего CREATE TABLE .... Как насчет db.execSQL("CREATE TABLE ...") в тебе onCreate()?   -  person Trinimon    schedule 29.03.2013
comment
Также вы должны указать учебник, из которого вы скопировали код: AndroidSQLite на vogella .com   -  person dcow    schedule 29.03.2013
comment
Извините, я должен был упомянуть ссылку на учебник: mysamplecode.com/ 2012/07/ Я знаю, что пропустил CREATE TABLE, так как данные уже существуют. Нужен ли мне для этого еще один курсор?   -  person user2224135    schedule 29.03.2013
comment
РЕШЕНИЕ: приведенный выше код работает нормально, но проблема была в базе данных. Все мои столбцы в базе данных были в ВЕРХНЕМ РЕГИСТРЕ. Это было бы нормально, за исключением того факта, что идентификатор столбца всегда должен быть в НИЖНЕМ РЕГИСТРЕ. Следовательно: «общедоступная статическая окончательная строка COLUMN_ID = _id;» 'public static final String COLUMN_SUBSTRUCTURE = SUBSTRUCTURE;' 'public static final String COLUMN_TYPE = TYPE;' ...   -  person user2224135    schedule 05.04.2013


Ответы (3)


Из вашего кода не видно, что вы уже создали таблицу, поэтому столбцы не будут найдены.

Вы делаете это в методе onCreate, создавая запрос для создания таблицы. В вашем коде вы, кажется, делаете выбор, а не создаете.

private static final String TABLE_CREATE = "create table "
    + TABLE_NAME
    + "("
    + COLUMN_ID + " integer primary key autoincrement, "
    + COLUMN_TYPE + " text not null default '', "
    + COLUMN_ORDERNUM + " integer not null default 0, "
    + COLUMN_INSTALLATION + " integer not null default 0, "
    + COLUMN_SUBSTRUCTURE + " text not null default ''"
    + ");";

@Override
public void onCreate(SQLiteDatabase database) {
    database.execSQL(TABLE_CREATE);
}

Чтобы сохранить это во внешнем хранилище, вам необходимо переопределить getDatabasePath(...). Аналогичное решение здесь https://stackoverflow.com/a/8385537/935779.

@Override
public File getDatabasePath(String name) {
    // reference where you would like the file to be here.
    File result = new File(getExternalFilesDir(null), name);
    return result;
}

Я полагаю, вы захотите переопределить это в своем классе Application, так как он является членом класса ContextWrapper.

Метод getDatabaseFile(...) используется внутри openOrCreateDatabase(...), чтобы определить местоположение.

В качестве альтернативы вы можете просто переопределить openOrCreateDatabase(...) и указать там расположение файла.

person Kirk    schedule 29.03.2013
comment
Я не верю, что это создаст базу данных. Вы должны определить структуру таблицы с помощью оператора sqlite create table. В учебнике, на который вы ссылались выше, есть оператор создания, но в вашей версии кода его нет. - person Kirk; 29.03.2013
comment
Что я надеялся сделать, так это изменить учебник, чтобы при загрузке списка данные также загружались. Теперь код с CREATE TABLE работает нормально, если база данных не закрывается, и я бы создал ее в /DATA/DATA/... и добавил бы в нее некоторые данные через страницу редактирования (активность) + загрузка тоже нормально. Но как только я указываю путь к внешней базе данных, кажется, что она не может его прочитать. Если я не включаю android.permission.WRITE_EXTERNAL_STORAGE в манифест, он говорит, что у него нет разрешения на запись, поэтому я предполагаю, что он пытается подключиться к нему. - person user2224135; 29.03.2013
comment
Я считаю, что вам нужно изменить местоположение файла базы данных, переопределив метод. я обновил свой ответ - person Kirk; 29.03.2013
comment
Разве это не то же самое, что: DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ /DATABASE_BACKUP/IMPORTED/; - person user2224135; 01.04.2013
comment
Я добавил еще один метод, чтобы подтвердить, что он существует, но он все равно не откроет его. - person user2224135; 01.04.2013

Я не думаю, что вы можете изменить или даже указать местоположение базы данных, только имя. Не указывайте путь и не пытайтесь поместить его во внешнее хранилище — пусть Android сам определит путь.

person Martin    schedule 29.03.2013

Хорошо, это заняло у меня почти неделю и много стресса, но вот решение. Я начал проходить множество руководств и заработал в этом:

http://www.mysamplecode.com/2012/11/android-database-content-provider.html

Я извлек базу данных из виртуального устройства и вручную добавил дополнительные данные. Затем скопировал базу данных в нужную папку в папке моего устройства (просто чтобы убедиться, что согласованность/столбцы базы данных точно такие же). Затем изменил класс MyDatabaseHelper следующим образом:

public class MyDatabaseHelper extends SQLiteOpenHelper {

    private static final String DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ "/MYFOLDER/";
    private static final String DATABASE_NAME = "TheWorld.db";
    private static final int DATABASE_VERSION = 1;

    MyDatabaseHelper(Context context) {
        super(context, DATABASE_PATH+DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        CountriesDb.onCreate(db);
    }

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

Не забудьте добавить разрешения в свой манифест:

<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Сделанный! Если вы прочитаете сообщения выше, ответ основан на совете Кирка, поэтому чтение его рекомендуемой ссылки поможет. У меня все еще есть дополнительные тесты на тот случай, если моя структура базы данных раньше была неправильной.

person user2224135    schedule 03.04.2013