Java安卓学习总结(十八)

第十四章 SQLite数据库

有关新建的三个数据库类

public class CrimeDbSchema
public class CrimeBaseHelper extends SQLiteOpenHelper
public class CrimeCursorWrapper extends CursorWrapper

public class CrimeDbSchema是用于定义数据库的scheme,包括表名、属性项名。

public class CrimeDbSchema {
    public static final class CrimeTable
    {
        public static final String NAME="crimes";
        public static final class Cols
        {
            public static final String UUID="uuid";
            public static final String TITLE="title";
            public static final String DATE="date";
            public static final String SOLVED="solved";

        }
    }
}

public class CrimeBaseHelper extends SQLiteOpenHelper对应于数据库操作中的DDL(数据定义语言),这里只用到了创建一个表,里面还定义了文件名称和版本。

public class CrimeBaseHelper extends SQLiteOpenHelper {
private static final int VERSION=1;
private static String DATABASE_NAME="crimeBase.db";
public CrimeBaseHelper(@Nullable Context context) {
super(context,DATABASE_NAME , null, VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table "+ CrimeTable.NAME+"("+"_id integer primary key autoincrement, "+CrimeTable.Cols.UUID+", "+CrimeTable.Cols.TITLE+", "+CrimeTable.Cols.DATE+", "+CrimeTable.Cols.SOLVED+")");
}

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

}
}

public class CrimeCursorWrapper extends CursorWrapper是一个游标封装类,public Crime getCrime()将从数据库中读到的一条记录封装变成一个crime。

public class CrimeCursorWrapper extends CursorWrapper {
    public CrimeCursorWrapper(Cursor cursor) {
        super(cursor);
    }

    public Crime getCrime()
    {
        String uuidString=getString(getColumnIndex(CrimeTable.Cols.UUID));
        String title=getString(getColumnIndex(CrimeTable.Cols.TITLE));
        long date=getLong(getColumnIndex(CrimeTable.Cols.DATE));
        int isSolved=getInt(getColumnIndex(CrimeTable.Cols.SOLVED));

        Crime crime=new Crime((UUID.fromString(uuidString)));
        crime.setTitle(title);
        crime.setDate(new Date(date));
        crime.setSolved(isSolved!=0);

        return crime;
    }
}

有关连接到数据库

之前的所有数据都是通过CrimeLab来进行增删改查的,现在也不例外,所以需要在CrimeLab中打开一个数据库(利用CrimeBaseHelper)。

private Context mContext;
private SQLiteDatabase mDatabase;
...
private CrimeLab(Context context)
{
mContext=context.getApplicationContext();
mDatabase=new CrimeBaseHelper(mContext).getWritableDatabase();
}

有关利用数据库进行增删改查(DML)

负责数据库写入(增)和更新(改)操作需要用到辅助类ContentValues,它和HashMap、Bundle是一个键值存储类。只不过ContentValues只能用于处理SQLite的数据。创建ContentValues就是将传入的crime打包成键值对的形式。

private static ContentValues getContentValues(Crime crime)
{
ContentValues values=new ContentValues();
values.put(CrimeDbSchema.CrimeTable.Cols.UUID,crime.getId().toString());
values.put(CrimeDbSchema.CrimeTable.Cols.TITLE,crime.getTitle());
values.put(CrimeDbSchema.CrimeTable.Cols.DATE,crime.getDate().getTime());
values.put(CrimeDbSchema.CrimeTable.Cols.SOLVED,crime.isSolved()?1:0);

return values;
}

改写CrimeLab.addCrime()方法:

public void addCrime(Crime c)
{
    ContentValues values=getContentValues(c);
    mDatabase.insert(CrimeDbSchema.CrimeTable.NAME,null,values);
}

insert第一个参数是表名,第二个是空值时的操作(没用到),第三个是插入的元组。

public void updateCrime(Crime crime)
{
String uuidString =crime.getId().toString();
ContentValues values=getContentValues(crime);
mDatabase.update(CrimeDbSchema.CrimeTable.NAME,values, CrimeDbSchema.CrimeTable.Cols.UUID+"= ?",new String[]{uuidString});
}

update第一个参数是表名,第二个参数是修改的结果,第三个是where语句;第三个where语句中的“?”在第四个参数中补充。

见挑战练习。

读取数据库对应select,SQLite用的是query。查询结果储存在游标cursor里面,但是我们之前为了方便已经定义了游标的封装类CrimeCursorWrapper了,所以就使用它。

private CrimeCursorWrapper queryCrimes (String whereClause, String[] whereArgs)
{
    Cursor cursor=mDatabase.query(
            CrimeDbSchema.CrimeTable.NAME,
            null,
            whereClause,
            whereArgs,
            null,
            null,
            null
    );
    return new CrimeCursorWrapper(cursor);
}

上面这一段方法实际上就是将select的范围锁定在表名为CrimeDbSchema.CrimeTable.NAME的表里而已。

得到游标之后需要逐条读取每一条记录,这样才能获得所有结果。读取每一条记录并将其封装为Crime类的方法就是之前创建的CrimeCursorWrapper的getCrime()方法。接下来在说明如何用游标来读取数据。

读-获取所有记录

读取所有记录并存在一个list中以便于像前几章一样供Fragment读取。所以需要重写CrimeLab.getCrimes()方法。

public List<Crime>getCrimes()
{
List<Crime> crimes=new ArrayList<>();

CrimeCursorWrapper cursor=queryCrimes(null,null);

try
{
cursor.moveToFirst();
while(!cursor.isAfterLast())
{
crimes.add(cursor.getCrime());
cursor.moveToNext();
}
}
finally {
cursor.close();
}

return crimes;
}

读-获取某一个特定的记录

原先的CrimeLab.getCrime()方法是获取指定Id的记录。在前几张的挑战练习中要求改进查询方式,当时我就说效率提升不是很大,而且之后还需要不断修改,没想到用上数据库之后可以直接全删了。

public Crime getCrime(UUID id)
{
    CrimeCursorWrapper cursor=queryCrimes(CrimeDbSchema.CrimeTable.Cols.UUID+" =?",new String[]{id.toString()});
try
    {
        if (cursor.getCount()==0)
        {
            return null;
        }
        cursor.moveToFirst();
        return cursor.getCrime();
    }
    finally {
        cursor.close();
    }
}

有关刷新模型层数据

到此为止,新建一个crime之后RecyclerView不会立刻显示出新建的项目,甚至是你重新打开这个软件之前都不会显示。

造成这个问题的原因是RecyclerView的刷新所依据的Adapter.mCrimes是对创建其本身时对数据库的一次快照,保留的只是当时的数据,而我们对数据的增删改查全是直接对数据库进行操作,绕过了mCrimes。那么只需要每一次更新界面时,让mcrimes再重新获取一次数据库的快照就行了。

private void updateUI()
{
CrimeLab crimeLab=CrimeLab.get(getActivity());
List<Crime> crimes=crimeLab.getCrimes();
if (mAdapter==null)
{
mAdapter=new CrimeAdapter(crimes);
mCrimeRecyclerView.setAdapter(mAdapter);
}
else
{
mAdapter.setCrimes(crimes);
mAdapter.notifyItemChanged(CrimePosition);
}
updateSubtitle();
}

有关挑战练习 删除Crime记录

这个简单,直接在CrimeLab方法中增加有关deleteCrime的方法。

public void deleteCrime(Crime crime)
{
    String uuidString =crime.getId().toString();
    mDatabase.delete(CrimeDbSchema.CrimeTable.NAME, CrimeDbSchema.CrimeTable.Cols.UUID+"= ?",new String[]{uuidString});   
}

delete的第一个参数是要删除元组的表名,第二个参数是where语句,其中“?”由第三个参数补充。

发表评论