Android 数据存储之SQLite数据库

转载请标明出处: http://blog.csdn.net/airsaid/article/details/52419418
本文出自:周游的博客

前言

Android中有许多的数据存储方式,如果我们有少量的数据需要存储,那么使用:SharedPreferences、文件存储就可以了。但是如果有大量数据需要进行读写,那么就需要使用到数据库了。Android中内置了SQLite数据库,而SQLite数据库是一个真正轻量级的数据库,它并没有后台进程,整个数据库就对应于一个文件。Android也给我们提供了大量的API,使用起来很方便。

SQLiteDatabase

Android提供了SQLiteDatabase对象来管理数据库,SQLiteDatabase有提供方法来创建,删除,执行SQL命令,并执行常见的数据库管理任务。

SQLiteDatabase提供了如下静态方法来打开一个文件对应的数据库:

  • SQLiteDatabase openDatabase(String path, SQLiteDatabase.CursorFactory factory, int flags):
    打开path文件所代表的SQLite数据库。
  • SQLiteDatabase openOrCreateDatabase(File file, SQLiteDatabase.CursorFactory factory):
    打开或创建(如果不存在)file文件所代表的SQLite数据库。
  • SQLiteDatabase openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory):
    打开或创建(如果不存在)path文件所代表的SQLite数据库。

当我们获取到SQLiteDatabase对象之后,就可以调用SQLiteDatabase如下的方法来对数据库进行操作了:

JAVA方法 释义
execSQL(String sql) 执行SQL语句
execSQL(String sql, Object[] bindArgs) 执行带占位符的SQL语句
insert(String table, String nullColumnHack, ContentValues values) 向指定表中插入特定数据
delete(String table, String whereClause, String[] whereArgs) 删除指定表中的特定数据
update(String table, ContentValues values, String whereClause, String[] whereArgs) 更新指定表中的数据
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy,String having, String orderBy) 查询执行表中的数据
query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy,String having, String orderBy, String limit) 查询执行表中的数据,limit参数控制最多查询几条数据
query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs,String groupBy, String having, String orderBy, String limit) 查询执行表中的数据,distinct 参数表示是否去除重复的值
rawQuery(String sql, String[] selectionArgs) 执行带占位符的SQL语句查询
beginTransaction() 开始事务
endTransaction() 结束事务

Android提供了上面的方法,来帮助开发者更“简便”的对数据库进行增删改查。但是其实这些方法完全可以通过SQL语句来完成。而用记上面复杂的参数的时间,就可以来掌握SQL语句了。

上面的方法,都是返回了一个Cursor接口,该接口提供对由数据库查询返回的结果集的随机读写访问,而Cursor同样提供了如下方法来移动查询结果的记录指针:

JAVA方法 释义
move(int offset) 从当前位置向前或向后移动记录指针,如果移动成功则返回true
moveToFirst() 移动记录指针到第一行
moveToLast() 移动记录指针到最后一行
moveToNext() 移动记录指针到下一行
moveToPrevious() 移动记录指针到上一行
moveToPosition(int position) 移动记录指针到指定行
isFirst() 是否指向第一条
isLast() 是否指向最后一条
isBeforeFirst() 是否指向第一条之前
isAfterLast() 是否指向最后一条之后
isNull(int columnIndex) 指定列是否为空(列基数为0)
isClosed() 游标是否已关闭
getCount() 总数据项数
getPosition() 返回当前游标所指向的行数
getColumnIndex(String columnName) 返回某列名对应的列索引值
getString(int columnIndex) 返回当前行指定列的值

一旦通过以上方法移动到指定行后,就可以调用Cursor的getXXX()方法来获取指定列的数据了。

创建数据库和表

  • 创建数据库:
// 判断数据库文件存放的文件夹是否存在,不存在则创建
String mPath = Environment.getExternalStorageDirectory() + "/db/";
File file = new File(mPath);
if(!file.exists() && !file.isDirectory()){
    // 创建目录
    file.mkdirs();
}
// 使用静态方法打开数据库(不存在则创建),第二个参数用于返回Course工厂,null则表示使用默认工厂。
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(mPath + "blog.db", null);
  • 创建表:
// 执行创建表SQL语句,创建了一个名为blog_info的表
String createSql = "create table blog_info(blog_id integer primary key, blog_name varchar(255), blog_link varchar(255))";
database.execSQL(createSql);

注意:由于数据库创建在SD卡中,所以需要加上写入SD卡权限

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

操作数据库

在创建好数据库和表之后就可以对表中的数据进行管理和操作了。我们可以直接通过execSQL()方法执行SQL语句来操作,也可以使用Android给我们直接提供的增删改查方法。

insert

  • insert(String table, String nullColumnHack, ContentValues values):
    • table:想插入数据的表名。
    • nullColumnHack:代表强行charunull值的数据列列名。当values参数为null或不想包含任何key-value对时该参数有效。
    • values:代表一行记录的数据。
      插入的数据参数为ContentValues,ContentValues有点类似与Map集合,它也有put(String key, XXX value)方法,其中key为数据列名,getXxx(String key)为取出对应列名的数据。

往刚刚上面创建的数据库blog_info表中插入一条数据:

ContentValues contentValues = new ContentValues();
contentValues.put("blog_name", "Airsaid");
contentValues.put("blog_link", "http://blog.csdn.net/airsaid");
// line为新添加的行号,该行号是一个内部值,与主键id无关,当发生错误时,返回-1。
long line = mDatabase.insert("blog_info", null, contentValues);

dalete

  • delete(String table, String whereClause, String[] whereArgs):
    • table:代表想删除数据的表名。
    • whereClause:满足该whereClause子句的记录将会被删除。
    • whereArgs:用于为whereClause子句传入参数。

删除和”Airsaid”相关的数据:

int num = mDatabase.delete("blog_info", "blog_name like ?", new String[]{"Airsaid"});
Toast.makeText(this, "一共删除了:" + num + "条数据", Toast.LENGTH_SHORT).show();

update

  • update(String table, ContentValues values, String whereClause, String[] whereArgs):
    • table:代表需要更新的表名。
    • values:代表想更新的数据。
    • whereClause:满足该WhereClause子句的数据将会被更新。
    • whereArgs: 用于为whereClause子句传入参数。

将所有主键大于5的blog_name改为周游:

ContentValues contentValues = new ContentValues();
contentValues.put("blog_name", "周游");
int num = mDatabase.update("blog_info", contentValues, "_id > ?", new String[]{"5"});

query

  • query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs,String groupBy, String having, String orderBy, String limit):
    • distinct:是否去除重复记录。
    • table:需要执行查询的表名。
    • columns:要查询出来的列名。
    • selection:查询条件子句。
    • selectionArgs:占位符传入参数。
    • groupBy:用与控制分组。
    • having:用于对分组进行排序。
    • orderBy:用于对记录进行排序。
    • limit:用于进行分页。

由于查询条件的不确定性,所以query()方法的参数真的是比较长。。感觉建议在写查询语句的时候,可以直接使用rawQuery()方法进行查询。

实例

下面写一个实例,用户分别输入博客名和博客链接,点插入后,将数据存储到数据库,并用列表展示出来:
* 布局:

<?xml version="1.0" encoding="utf-8"?>
<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="com.airsaid.sqlitedemo.MainActivity">

    <EditText
        android:id="@+id/edt_blog_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"/>

    <EditText
        android:id="@+id/edt_blog_link"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"/>

    <Button
        android:onClick="insert"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="插入数据"/>

    <ListView
        android:id="@+id/listView"
        android:layout_marginTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>
  • 代码:
public class MainActivity extends AppCompatActivity {

    private String mPath = Environment.getExternalStorageDirectory() + "/db/";
    private static final String DB_NAME = "blog.db";

    private EditText mEdtName;
    private EditText mEdtLink;
    private ListView mListView;
    private SQLiteDatabase mDatabase;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mEdtName = (EditText) findViewById(R.id.edt_blog_name);
        mEdtLink = (EditText) findViewById(R.id.edt_blog_link);
        mListView = (ListView) findViewById(R.id.listView);

        // 判断数据库文件存放的文件夹是否存在,不存在则创建
        File file = new File(mPath);
        if(!file.exists() && !file.isDirectory()){
            // 创建目录
            file.mkdirs();
        }
        // 使用静态方法打开数据库(不存在则创建),第二个参数用于返回Course工厂,null则表示使用默认工厂。
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mPath + DB_NAME, null);
        // 执行创建表SQL语句,创建了一个名为blog_info的表
        String createSql = "create table blog_info(_id integer primary key, blog_name varchar(255), blog_link varchar(255))";
        mDatabase.execSQL(createSql);
    }

    /**
     * 插入数据
     */
    public void insert(View v){
        // 获取用户输入信息
        String name = mEdtName.getText().toString();
        String link = mEdtLink.getText().toString();
        // 执行SQL语句插入数据
        mDatabase.execSQL("insert into blog_info values(null, ?, ?)", new String[]{name, link});
        // 填充数据到ListView
        Cursor cursor = mDatabase.rawQuery("select * from blog_info", null);
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.item_listview_blog, cursor
                , new String[]{"blog_name", "blog_link"}, new int[]{R.id.txt_name, R.id.txt_link}
                , CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        mListView.setAdapter(adapter);
    }
}
  • Item布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_gravity="center"
              android:orientation="horizontal">

    <TextView
        android:id="@+id/txt_name"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_weight="1"/>

    <TextView
        android:id="@+id/txt_link"
        android:layout_width="0dp"
        android:layout_height="50dp"
        android:layout_weight="1"/>

</LinearLayout>

运行结果:

事务

数据库操作,事务是少不了的,而SQLiteDatabase也提供了如下几个方法来操作事务:
* beginTransaction():开始事务。
* endTransaction():结束事务。
* inTransaction():判断当前上下文是否处于事务中。
当调用endTransaction()方法结束事务的时候,那么到底是提交事务还是进行回滚事务呢?这取决于SQLiteDatabase是否调用了:setTransactionSuccessful()方法来设置事务标志,如果在执行事务的时设置了,当事务成功则提交事务,否则程序将回滚事务。

SQLiteOpenHelper

其实在真实项目开发中,很少使用SQLiteDatabase的方法来打开数据库,而是通过继承Android给我们提供的SQLiteOpenHelper抽象类来创建。
SQLiteOpenHelper是一个辅助类,用于来帮助我们建立和管理数据库。我们只需要继承它,并实现两个抽象方法:
* onCreate(SQLiteDatabase db):在初次生成数据库的时候会被调用,用于生成表结构。
* onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本发生更新时会被调用,oldVersion为旧数据库版本号,newVersion为新版本数据库版本号。

方法

SQLiteOpenHelper有如下方法:

JAVA方法 释义
getWritableDatabase() 以写的方式打开数据库对应的SQLiteDatabase对象
getReadableDatabase() 以读写的方式打开数据库对应的SQLiteDatabase对象
getDatabaseName() 返回被打开SQLite数据库的名称
onConfigure(SQLiteDatabase db) 正在配置数据库连接时调用
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) 当数据库降级时调用
onOpen(SQLiteDatabase db) 当数据库已经打开时调用
setWriteAheadLoggingEnabled(boolean enabled) 启用或者禁用数据库中使用预写日记记录

实例

  • 继承SQLiteOpenHelper,在onCreate方法中创建数据库表:
public class MyDatabaseHelper extends SQLiteOpenHelper {

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建表结构
        db.execSQL("create table dict(_id integer primary key autoincrement, word, detail)");
    }

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

    }
}
  • 布局很简单,分别为单词输入框和释义输入框,输入完成后点击按钮保存到数据库,保存数据后,用户可以输入要查询的生词进行查询,点击查询将结果作为dialog展示出来。添加生词的方法如下:
public void add(View v) {
    // 添加生词
    String word = mEdtWord.getText().toString();
    String detail = mEdtDetail.getText().toString();
    if(TextUtils.isEmpty(word) || TextUtils.isEmpty(detail)){
        Toast.makeText(this, "输入的数据不能为空", Toast.LENGTH_SHORT).show();
        return;
    }

    SQLiteDatabase database = mDb.getReadableDatabase();
    database.execSQL("insert into dict values(null, ? , ?)", new String[]{word, detail});
    Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
}

主要是通过SQLiteOpenHelper的 getReadableDatabase()方法获取了SQLiteDatabase对象。这里需要注意的是:
getReadableDatabase()方法会先以读写方式打开数据库,如果数据库的磁盘空间已经满了,那么就会打开失败,然后再尝试以只读的方式打开数据库。
而通过getWritableDatabase()方法,如果磁盘空间已满,打开时就会出错。

  • 查询:
public void seek(View v) {
    // 查看生词
    String dict = mEdtInput.getText().toString();
    SQLiteDatabase database = mDb.getReadableDatabase();
    Cursor cursor = database.rawQuery("select * from dict where word like ? or detail like ?"
            , new String[]{"%" + dict + "%", "%" + dict + "%"});

    List<String> dicts = new ArrayList<>();
    while (cursor.moveToNext()){
        String word = cursor.getString(1);
        String detail = cursor.getString(2);
        dicts.add("生词:" + word + " 解释:" + detail);
    }
    showData(dicts);
}
  • 将结果展示:
private void showData(List<String> dicts) {
    if(dicts.size() < 1) {
        Toast.makeText(this, "没有找到到该生词数据", Toast.LENGTH_SHORT).show();
        return;
    }
    String[] strings = dicts.toArray(new String[dicts.size()]);
    for (String string : strings) {
        Log.e("test", string);
    }

    new AlertDialog.Builder(this)
            .setTitle("查询出来的单词结果")
            .setItems(dicts.toArray(new String[dicts.size()]), new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();
                }
            }).create().show();
}

运行结果:
这里写图片描述

参考文章:《疯狂Android讲义》

发布了55 篇原创文章 · 获赞 117 · 访问量 30万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 精致技术 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览