正确的方法将特定的数据列表从SQLite检索到列表视图?

时间:2018-10-21 14:19:52

标签: android sqlite android-sqlite

有人可以帮我如何从sql到listview检索特定的列表数据吗?我尝试检索所有数据,它成功地工作,但是当我尝试使用Where子句检索特定的数据时,它崩溃了。有什么帮助吗?还是有其他方法可以检索特定的数据列表

这是我尝试检索sql数据的代码的一部分

ArrayList<String> results=new ArrayList<>();
    db=new DBHandler(this);
    ListView listView = (ListView) findViewById(R.id.list);


    lst.setText(callquiz);
    String YOUR_QUERY  = "SELECT * FROM user WHERE title= "+ callquiz;
//callquiz is a string variable i use to hold the data specifier for my table
    SQLiteDatabase dbfectch;
    dbfectch = db.getWritableDatabase();
    Cursor c = dbfectch.rawQuery(YOUR_QUERY,null);
    if (c != null ) {
        if  (c.moveToFirst()) {
            do {  results.add(c.getString(c.getColumnIndex(COL_ANS)));
            }while (c.moveToNext());
        }
    }
    ArrayAdapter adapter = new ArrayAdapter<String>(this,
            android.R.layout.simple_list_item_1, results);
    listView.setAdapter(adapter);

这是DBHandler类

public class DBHandler extends SQLiteOpenHelper {
public static final String DB_NAME = "AnswerTables.db";
public static final int DB_VERSION = 2;
public static final String TABLE = "user";
public static final String COL_TASK_TITLE = "title";
public static final String ID = "id";
public static final String COL_ANS="answer";
public DBHandler(Context context) {
    super(context, DB_NAME, null, DB_VERSION);
    SQLiteDatabase db=getWritableDatabase();

}

@Override
public void onCreate(SQLiteDatabase db) {
    String createtable="CREATE TABLE " + TABLE + " ( "+ ID +" INTEGER PRIMARY KEY AUTOINCREMENT, "+ COL_TASK_TITLE+ " TEXT NOT NULL,"+ COL_ANS+" TEXT NOT NULL);";
    db.execSQL(createtable);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL("DROP TABLE IF EXISTS " + TABLE);
    onCreate(db);
}
public boolean insertData(String name,String quiz){
    SQLiteDatabase db=this.getWritableDatabase();
    ContentValues contex=new ContentValues();
    contex.put(COL_TASK_TITLE,name);
    contex.put(COL_ANS,quiz);
    long result=db.insert(TABLE,null,contex);
    if(result==-1)
    {
        return false;
    }else{
        return true;
    }
 }
}

2 个答案:

答案 0 :(得分:0)

出于安全原因(我不确定),每当您执行Where或另一种与值匹配的语句时,匹配的值都应该像“?”一样,因此您的语句看起来像这样

String QUERY  = "SELECT * FROM user WHERE title=?";

其余代码如下:

//callquiz is a string variable i use to hold the data specifier for my table
SQLiteDatabase dbfectch;
dbfectch = db.getWritableDatabase();
Cursor c = dbfectch.rawQuery(QUERY, new String[]{callquiz});

如果查询未返回任何数据,请尝试将撇号放在“?”之间在声明中。

答案 1 :(得分:0)

主要问题

您的问题很可能是由于找不到列名。

这是因为您没有将callquiz字符串括在引号中,并且如果该值不是数字,则它将值视为列名。

可以使用:-

解决此问题
String YOUR_QUERY  = "SELECT * FROM user WHERE title= '"+ callquiz + "'";

使用查询便捷方法进行改进。

但是,防止此类错误的更好方法是改用SQLiteDatabase类的 query 便捷方法。这也可以防止SQL注入。

要利用这一点,您可以编写代码而不是:-

字符串YOUR_QUERY =“ SELECT * FROM user WHERE title =”“ + callquiz; // callquiz是一个字符串变量,我用来保存表的数据说明符     SQLiteDatabase dbfectch;     dbfectch = db.getWritableDatabase();     光标c = dbfectch.rawQuery(YOUR_QUERY,null);

是:-

//String YOUR_QUERY  = "SELECT * FROM user WHERE title= "+ callquiz; //<<<<<<<<<< NOT NEEDED
//callquiz is a string variable i use to hold the data specifier for my table
SQLiteDatabase dbfectch;
dbfectch = db.getWritableDatabase();
Cursor c = dbfectch.query(DBHandler.TABLE, //<<<<<<<<<< The table to query
               null,                       //<<<<<<<<<< The columns null for all eqv to *
               DBHandler.COL_TASK_TITLE + "=?", //<<<<<<<<<< The WHERE clause ? is replaced on a 1 for 1 basis from the WHERE args (next paramter)
               new String[]{callquiz}, //<<<<<<<<<< The WHERE args that replace ?
               null, //<<<<<<<<<< GROUP BY clause, null = no clause
               null, //<<<<<<<<<< HAVING clause, null = no clause
               null //<<<<<<<<<< ORDER BY clause, null = no clause
            );
  • 请注意,提供了相应的关键字,不应将其包括在内
  • 上面的结果将导致查询按照修复程序进行,包括将字符串括在单引号中。

检查游标是否为空

关于正确性检查,从SQLiteDabase方法返回的游标的 null 是无用的,并且可能导致问题。这样的游标将永远不会为空,而是如果现在提取行,则游标将有0行。 moveTo ??????如果无法进行移动,则方法将返回false(另外,Cursor getCount 方法将返回0)。

使用while (cursor.moveToNext) {....}

的简单循环

使用while (cursor.MoveToNext) { do your stuff.... }遍历游标更为简单。因此,更正确的代码可能是:-

SQLiteDatabase dbfectch;
dbfectch = db.getWritableDatabase();
Cursor c = dbfectch.query(DBHandler.TABLE, //<<<<<<<<<< The table to query
               null,                       //<<<<<<<<<< The columns null for all eqv to *
               DBHandler.COL_TASK_TITLE + "=?", //<<<<<<<<<< The WHERE clause ? is replaced on a 1 for 1 basis from the WHERE args (next parameter)
               new String[]{callquiz}, //<<<<<<<<<< The WHERE args that replace ?
               null, //<<<<<<<<<< GROUP BY clause, null = no clause
               null, //<<<<<<<<<< HAVING clause, null = no clause
               null //<<<<<<<<<< ORDER BY clause, null = no clause
            );
while (c.moveToNext()) {
   results.add(c.getString(c.getColumnIndex(DBHandelr.COL_ANS)));
}

使用单个适配器

有时,列表可能会更改(例如,您可能有删除行的代码),您实际上并不想每次实例化一个新适配器,而是通过调用适配器的NotifyDatasetChanged方法来刷新列表。因此,仅跟随:-

ArrayAdapter adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, results);
listView.setAdapter(adapter);

相反,您可以使用:-

if (adapter == null) {
    adapter = new ArrayAdapter<String>(this,
        android.R.layout.simple_list_item_1, results);
        listview.setAdapter(adapter);
} else {
    adapter.notifyDatasetChanged();
}
  • 请注意,这并不适合重建结果ArrayList,它还假定适配器已声明为类变量(因为结果可能是为了更好地促进在调用notifyDatasetChanged方法之前重新构建适配器)。

ArrayList的限制

通常,您会希望用户与列表进行交互(例如,长按要删除的项目(不建议单击中间没有确认删除的中间对话框的情况下,不建议单击,长单击至少可以防止意外单击) ))。

使用 ArrayList 的问题在于,只有显示的字符串是直接可用的,并且可能不足以标识基础行。宁愿使用ArrayList更好,因为您通常会拥有一个这样的对象(在您的情况下, your_more_comprehensive_object 可能是一个 user 对象,其中包含相应 ID 列)。但是,如果不使用CustomAdapter或在对象的类中覆盖toString方法,则会受到限制。

对于ListView,更简单的解决方案是改为使用游标适配器。使用CursorAdapter(例如SimpleCursorAdapter)确实需要存在专门命名为 _id 的列。

使用SimpleCursorAdapter和侦听器的工作示例

因此,您可能希望考虑以下基于您的代码的工作示例(希望上面的说明中的注释将解释所有内容):-

DBHanlder.java

public class DBHandler extends SQLiteOpenHelper {
    public static final String DB_NAME = "AnswerTables.db";
    public static final int DB_VERSION = 2;
    public static final String TABLE = "user";
    public static final String COL_TASK_TITLE = "title";
    //public static final String ID = "id"; //<<<<<<<<<< replaced by line below
    public static final String ID = BaseColumns._ID; //<<<<<<<<<< make column the standard _id
    public static final String COL_ANS="answer";
    public DBHandler(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        SQLiteDatabase db=getWritableDatabase();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createtable="CREATE TABLE " + TABLE + " ( "+ ID +" INTEGER PRIMARY KEY, "+ COL_TASK_TITLE+ " TEXT NOT NULL,"+ COL_ANS+" TEXT NOT NULL);";
        db.execSQL(createtable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS " + TABLE);
        onCreate(db);
    }
    public boolean insertData(String name,String quiz){
        SQLiteDatabase db=this.getWritableDatabase();
        ContentValues contex=new ContentValues();
        contex.put(COL_TASK_TITLE,name);
        contex.put(COL_ANS,quiz);
        long result=db.insert(TABLE,null,contex);
        if(result==-1)
        {
            return false;
        }else{
            return true;
        }
    }

    //<<<<<<<<<< new method
    public Cursor getAllUsersWithTitle(String title) {
        String whereclause = COL_TASK_TITLE + "=?";
        String[] whereargs = new String[]{title};
        SQLiteDatabase db = this.getWritableDatabase();
        return db.query(TABLE,null,whereclause,whereargs,null,null,null);
    }

    //<<<<<<<<<< new method
    public boolean deleteUserById(long id) {
        String whereclause = ID + "=?";
        String[] whereargs = new String[]{String.valueOf(id)};
        SQLiteDatabase db = this.getWritableDatabase();
        return (db.delete(TABLE,whereclause,whereargs) > 0);
    }
}
  • 请注意,要合并以上内容,您需要执行以下一项操作:-
    • 删除/清除应用数据
    • 卸载应用
    • 随着数据库结构/架构的更改,增加分配给DB_version的值
  • 请注意,您很有可能不需要/想要自动添加,其开销请参见SQLite Autoincrement

MainActivity.java

public class MainActivity extends AppCompatActivity {

    Context context; //<<<<<<<<<< ADDED for Toast from handler
    ListView listView;
    DBHandler db;
    Cursor cursor;
    SimpleCursorAdapter sca;
    String callquiz;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this;
        listView = this.findViewById(R.id.list);

        db = new DBHandler(this);
        addSomeUsers(); //<<<<<<<<<< Add some testing data
        callquiz = "Fred";
        refreshOrInitialiseListView(); //<<<<<<<<<< handle the listview

        //<<<<<<<<<< EXTRA >>>>>>>>>>
        //<<<<<<<<<< Add A click Listener to toast the clicked item
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                Toast.makeText(context,
                        "You clicked on the Item with a title of " +
                                cursor.getString(cursor.getColumnIndex(DBHandler.COL_TASK_TITLE)) +
                                " and ans is " + cursor.getString(cursor.getColumnIndex(DBHandler.COL_ANS)) +
                                " whose ID is " + String.valueOf(l) //<<<<<<<<<< note 4th parameter passed is the id
                        ,
                        Toast.LENGTH_SHORT).show();
            }
        });
        listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
                db.deleteUserById(l);
                refreshOrInitialiseListView();
                return true; //<<<<<<<<<<<< indicate that the long click has been handled
            }
        });
    }

    //<<<<<<<<<< ADDED to clean up (not needed for the main activity but does not hurt)
    @Override
    protected void onDestroy() {
        cursor.close();
        db.close();
        super.onDestroy();
    }

    //<<<<<<<<<< ADDED to refresh the ListView is the activity is resumed e.g. return from invoked activity.
    @Override
    protected void onResume() {
        super.onResume();
        refreshOrInitialiseListView();
    }

    private void refreshOrInitialiseListView() {
        cursor = db.getAllUsersWithTitle(callquiz);
        if (sca == null) {
            sca = new SimpleCursorAdapter(
                    this,
                    android.R.layout.simple_list_item_2,
                    cursor,
                    new String[]{DBHandler.COL_TASK_TITLE,DBHandler.COL_ANS},
                    new int[]{android.R.id.text1,android.R.id.text2},
                    0
            );
            listView.setAdapter(sca);
        } else {
            sca.swapCursor(cursor);
        }
    }

    private void addSomeUsers() {
        if (DatabaseUtils.queryNumEntries(db.getWritableDatabase(),DBHandler.TABLE) < 1) {
            db.insertData("Fred","Freds Quiz");
            db.insertData("Fred","Fred's 2nd Quiz");
            db.insertData("Fred","Fred's 3rd quiz");
            db.insertData("Mary","Mary's first quiz");
            db.insertData("Mary","mary's 2nd quiz");
            db.insertData("Mary","Mary's 3rd quiz");
        }
    }
}

从头开始运行该应用程序将导致ListView中列出6行(项目)中的3行(显示标题和答案)。

  • 单击某个项目将导致该项目的详细信息被烘烤。
  • 长按一个项目将从表中删除该项目,并从列表中删除该项目。

注意:该工作示例旨在用作演示并具有局限性(例如,如果您删除所有项目/行,那么除非您删除数据库,否则不会显示任何内容)