SQLite本地数据库的应用
说明 概述SQLite是一个轻量级的关系型数据库,运算速度快,占用资源少,很适合在移动设备上使用, 不仅支持标准SQL语法,还遵循ACID(数据库事务)原则,无需账号,使用起来非常方便! SQLite是类似于MySQL和Postgresql的开源关系型数据库。不同于其他数据库的是, SQLite使用单个文件存储数据,使用SQLite库读取数据。 小结下特点: SQlite通过文件来保存数据库,一个文件就是一个数据库,数据库中又包含多个表格,表格里又有 多条记录,每个记录由多个字段构成,每个字段有对应的值,每个值我们可以指定类型,也可以不指定 类型(主键除外) 关于SQLite数据库支持存储的数据类型及相关的基本操作语句可以移步到android中的数据库操作或者SQLite在线文档。 Android标准库包含SQLite库以及配套的一些Java辅助类。 使用SQLite本地数据库Step 1 : 定义Schema我们以上一篇RecyclerView的基本用法为例,将每一个View对象中的内容存入数据库。 创建数据库前,首先要清楚存储什么样的数据。 我们要保存的是一条条Info信息 SQL中一个重要的概念是schema:一种DB结构的正式声明,用于表示database的组成结构。schema是从创建DB的SQL语句中生成的。我们会发现创建一个伴随类(companion class)是很有益的,这个类称为合约类(contract class),它用一种系统化并且自动生成文档的方式,显示指定了schema样式。 Contract Clsss是一些常量的容器。它定义了例如URIs,表名,列名等。这个contract类允许在同一个包下与其他类使用同样的常量。 它让我们只需要在一个地方修改列名,然后这个列名就可以自动传递给整个code。 组织contract类的一个好方法是在类的根层级定义一些全局变量,然后为每一个table来创建内部类。 首先,我们来创建定义schema的Java类。创建时,新建一个包为databas,在包下新建类命名为InfoDbSchema,这样,就可以将InfoDbSchema.java文件放入专门的database包中,实现数据库操作相关代码的组织和归类。 在InfoDbSchema类中,再定义一个描述数据表的InfoTable内部类: public class InfoDbScheme {
public static final class InfoTable{
public static final String NAME = "infos";
}
}
InfoTable内部类唯一的用途就是定义描述数据表元素的String常量。首先要定义的是数据库表名(InfoTable.NAME)。 接下来定义数据表字段: public class InfoDbScheme {
public static final class InfoTable{
public static final String NAME = "infos";
public static final class Col{
public static final String UUID = "uuid";
public static final String TITLE = "title";
public static final String DATE = "date";
}
}
}
有了这些数据表元素,就可以在Java代码中安全地引用了。例如, InfoTable.Cols.TITLE就是指Info记录的title字段。此外,这种定义方式还给修改字段名称或新增表元素带来了方便。 step 2 : 使用SQL Helper创建初始数据库定 义 完 数 据 库 schema , 就 可 以 创 建 数 据 库 了 。 openOrCreateDatabase(…) 和databaseList()方法是Android提供的Context底层方法,可以用来打开数据库文件并将其转换为SQLiteDatabase实例。 不过,实际开发时,建议总是遵循以下步骤。
public class InfoBaseHelper extends SQLiteOpenHelper {
private static final int VERSION = 1;
private static final String DATABASE_NAME = "infoBase.db";
public InfoBaseHelper(Context context) {
super(context,DATABASE_NAME,null,VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion) {
}
}
有了SQLiteOpenHelper类,打开SQLiteDatabase的繁杂工作都可以交给它处理。在InfoLab中用它创建infos数据库(InfoLab.java): public class InfoLab {
private static InfoLab sInfoLab;
private Context mAppContext;
private ArrayList<Info> mInfos;
private SQLiteDatabase mDateBase;
private InfoLab(Context appContext){
mAppContext = appContext.getApplicationContext();
mDateBase = new InfoBaseHelper(mAppContext).getWritableDatabase();
mInfos = new ArrayList<Info>();
/* for(int i = 0;i<100;i++){ Info info = new Info(); info.setmTtitle("Info #"+i); mInfos.add(info); }*/
}
...
}
这里调用getWritableDatabase()方法时, CrimeBaseHelper要做如下工作。
最后,再做个总结: onCreate(SQLiteDatabase)方法负责创建初始数据库; onUpgrade(SQLiteDatabase,int)方法负责与升级相关的工作。 我 们 在onCreate(…)方法中创建数据库表,这需要导入InfoDbSchema类的InfoTable内部类。(InfoBaseHelper.java) public void onCreate(SQLiteDatabase db) {
db.execSQL("create table " + InfoTable.NAME + "(" +
" _id integer primary key autoincrement," +
InfoTable.Col.UUID + "," +
InfoTable.Col.TITLE + "," +
InfoTable.Col.DATE +
")"
);
}
现在我们就在手机本地文件中创建了一个本地数据库,数据库名字叫做infoBase.db,在数据库中还创建了一个数据库表,表的名字叫做infos,在表中我们还创建了几个字段,uuid、title还有date。 我们可以在手机目录/data/data/[your package name]下查看(前提是手机要root),你就可以看到下图这样的文件。 当然现在info表里我们还没有添加数据。 step 3 : 写入数据库要使用SQLiteDatabase,数据库中首先要有数据。数据库写入操作有:向infos表中插入新记录以及在Info变更时更新原始记录。 我们修改InfoLab类,不用List来存储数据,改用mDateBase来存储数据,首先要删除掉InfoLab类中的 public class InfoLab {
private static InfoLab sInfoLab;
private Context mAppContext;
// private ArrayList<Info> mInfos;
private SQLiteDatabase mDateBase;
private InfoLab(Context appContext){
mAppContext = appContext.getApplicationContext();
mDateBase = new InfoBaseHelper(mAppContext).getWritableDatabase();
// mInfos = new ArrayList<Info>();
/* for(int i = 0;i<100;i++){ Info info = new Info(); info.setmTtitle("Info #"+i); mInfos.add(info); }*/
}
public static InfoLab get(Context c){
if(sInfoLab==null){
sInfoLab = new InfoLab(c.getApplicationContext());
}
return sInfoLab;
}
public ArrayList<Info> getInfos(){
// return mInfos;
return new ArrayList<>();
}
public Info getInfo(UUID uuid){
// for(Info i:mInfos){
// if(i.getmId().equals(uuid)){
// return i;
// }
// }
return null;
}
public void addInfo(Info info){
// mInfos.add(info);
}
public void updateInfo(Info info){
}
}
在InfoListFragment.java类中增加添加数据的按钮,修改info_list_activity.xml的代码如下: <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".InfoListActivity">
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/info_recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content"/>
<LinearLayout android:id="@+id/empty_crime_list" android:layout_width="wrap_content" android:layout_height="123dp" android:orientation="vertical" android:layout_gravity="center">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:padding="16dp" android:text="没有Info记录可以显示"/>
<Button android:id="@+id/add_crime_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:padding="16dp" android:text="@string/new_crime"/>
</LinearLayout>
</LinearLayout>
修改InfoListActivity.java代码如下: public class InfoListActivity extends AppCompatActivity {
...
private LinearLayout mLinearLayout;
private Button addButton;
...
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.info_list_activity);
mLinearLayout = (LinearLayout)this.findViewById(R.id.empty_crime_list);
addButton = (Button)this.findViewById(R.id.add_crime_button);
mInfoRecyclerView = (RecyclerView)this.findViewById(R.id.info_recycler_view);
mInfoRecyclerView.setLayoutManager(new LinearLayoutManager(this));
updateUI();
}
...
private void updateUI() {
InfoLab infoLab = InfoLab.get(this);
List<Info> infos = infoLab.getInfos();
if(mAdapter==null){
mAdapter = new InfoAdapter(infos);
mInfoRecyclerView.setAdapter(mAdapter);
}
else{
mAdapter.notifyDataSetChanged();
}
if(infos.size()>0){
mLinearLayout.setVisibility(View.GONE);
}
else{
mLinearLayout.setVisibility(View.VISIBLE);
addButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("hehe","hehe");
Info info = new Info();
InfoLab.get(InfoListActivity.this).addInfo(info);
Intent intent = new Intent(InfoListActivity.this,InfoDetailActivity.class);
intent.putExtra(EXTRA_INFO_ID,info.getmId());
startActivity(intent);
}
});
}
mInfoRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
}
}
现在我们点击按钮,就会加载InfoDetailActivity.java页面。 接下来开始往数据库中写入数据: 使用 ContentValues 负责处理数据库写入和更新操作的辅助类是ContentValues。它是个键值存储类,类似于Java的HashMap和前面用过的Bundle。不同的是, ContentValues只能用于处理SQLite数据。 step 4 : 创建ContentValues( InfoLab.java )public class InfoLab {
...
public static ContentValues getContentValues(Info info){
ContentValues values = new ContentValues();
values.put(InfoTable.Col.UUID,info.getmId().toString());
values.put(InfoTable.Col.TITLE,info.getmTtitle());
values.put(InfoTable.Col.DATE,info.getmDate().toString());
return values;
}
}
step 5 : 插入和更新记录( InfoLab.java )public void addInfo(Info info){
// mInfos.add(info);
ContentValues values = getContentValues(info);
mDateBase.insert(InfoTable.NAME,values);
}
public void updateInfo(Info info){
String uuidString = info.getmId().toString();
ContentValues values = getContentValues(info);
mDateBase.update(InfoTable.NAME,values,InfoTable.Col.UUID + " = ?",new String[] { uuidString });
}
step 6 : Info数据刷新( InfoDetailActivity.java )public class InfoDetailActivity extends AppCompatActivity {
...
public void onCreate(Bundle savedInstanceState) {
...
}
@Override
protected void onResume() {
super.onResume();
InfoLab.get(this).updateInfo(mInfo);
}
}
这样,点击按钮,你就可以往里面插入数据了,因为还没有完成会导致闪退,但是数据库中已经成功的添加了一条数据,打开数据库目录可以看到: step 7 : 读取数据库读取SQLite数据库中数据需要用到query(…)方法。这个方法有好几个重载版本。我们要用的版本如下: public Cursor query(
String table,String[] columns,String where,String[] whereArgs,String groupBy,String having,String orderBy,String limit)
参数table是要查询的数据表。参数columns指定要依次获取哪些字段的值。参数where和whereArgs的作用与update(…)方法中的一样。 private Cursor queryCrimes(String whereClause,String[] whereArgs) {
Cursor cursor = mDatabase.query(
InfoTable.NAME,// Columns - null selects all columns
whereClause,whereArgs,// groupBy
null,// having
null // orderBy
);
return cursor;
}
step 8 : 使用 CursorWrapperCursor是个神奇的表数据处理工具,其任务就是封装数据表中的原始字段值。 创建InfoCursorWrapper类(InfoCursorWrapper.java) public class InfoCursorWrapper extends CursorWrapper {
/** * Creates a cursor wrapper. * * @param cursor The underlying cursor to wrap. */
public InfoCursorWrapper(Cursor cursor) {
super(cursor);
}
...
}
新增getCrime()方法(InfoCursorWrapper.java) public class InfoCursorWrapper extends CursorWrapper {
/** * Creates a cursor wrapper. * * @param cursor The underlying cursor to wrap. */
public InfoCursorWrapper(Cursor cursor) {
super(cursor);
}
public Info getInfo() {
String uuidString = getString(getColumnIndex(InfoTable.Col.UUID));
String title = getString(getColumnIndex(InfoTable.Col.TITLE));
long date = getLong(getColumnIndex(InfoTable.Col.DATE));
Info info = new Info(UUID.fromString(uuidString));
info.setmTtitle(title);
info.setmDate(new Date(date));
return info;
}
}
step 9 : 使用cursor封装方法(InfoLab.java)private InfoCursorWrapper queryInfo(String whereClaues,String[] whereArgs){
Cursor cursor = mDateBase.query(
InfoTable.NAME,// Columns - null selects all columns
whereClaues,// groupBy
null,// having
null // orderBy
);
return new InfoCursorWrapper(cursor);
}
step 10 :返回info列表(InfoLab.java)public ArrayList<Info> getInfos(){
// return mInfos;
//return new ArrayList<>();
ArrayList<Info> infos = new ArrayList<>();
InfoCursorWrapper cursor = queryInfo(null,null);
try {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
infos.add(cursor.getInfo());
cursor.moveToNext();
}
} finally {
cursor.close();
}
return infos;
}
step11 :重写getInfo(UUID)方法(InfoLab.java)public Info getInfo(UUID uuid){
// for(Info i:mInfos){
// if(i.getmId().equals(uuid)){
// return i;
// }
// }
// return null;
InfoCursorWrapper cursor = queryInfo(
InfoTable.Col.UUID + " = ?",new String[] { uuid.toString() }
);
try {
if (cursor.getCount() == 0) {
return null;
}
cursor.moveToFirst();
return cursor.getInfo();
} finally {
cursor.close();
}
}
上述代码的作用如下。
数据库查询没有问题了。 InfoDetailActivity现在能够看见InfoLab中的所有Info了。 InfoLab.getInfo(UUID) 方 法 也 能 正 常 工 作 了 。 InfoDetailActivity 终于可以显示真正的Info对象了。 step 12 : 刷新模型层数据添加 private class InfoAdapter extends RecyclerView.Adapter<InfoHolder> {
...
@Override
public int getItemCount() {
return mInfos.size();
}
public void setInfos(List<Info> infos){
mInfos = infos;
}
}
然后在updateUI()方法中调用 private void updateUI() {
InfoLab infoLab = InfoLab.get(this);
List<Info> infos = infoLab.getInfos();
if(mAdapter==null){
mAdapter = new InfoAdapter(infos);
mInfoRecyclerView.setAdapter(mAdapter);
}
else{
mAdapter.setInfos(infos);
mAdapter.notifyDataSetChanged();
}
...
}
现在,可以验证我们的成果了。运行应用,新增一项info记录,然后按回退键, 源码在这里。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |