资讯专栏INFORMATION COLUMN

Android数据库操作(上)

Tony / 1030人阅读

摘要:所谓的,其实就是数据库的基本操作,代表,代表,代表,则代表。数据库好帮手上面已经给出了创建表并定义字段的方法,在别的地方它可以在终端或者脚本中运行,在世界里自然要在类中大展宏图。

一 前言

经常听到已经工作的程序员说每天的工作很无聊,总是一些CURD操作,没什么技术含量,对,今天我们就要勇敢的探索一下这所谓的“无聊工作”--CURD,并想办法让它有趣起来。所谓的CURD,其实就是数据库的基本操作,C代表create,U代表update,R代表read,D则代表delete。写过后台脚本的朋友对这个应该很熟悉,你就可以随便翻翻啦,而不太了解数据库操作的朋友,我在这里抛砖引玉,讲一下怎么将内存中的数据保存到本地数据库中,您看完后可以更加深入的去寻找一些资料学习,千万要记得不管别人说什么无聊或者没有技术含量,我们还是要一步一个脚印,坚持敲下去!

二 创建伴随类

前面说了我们要把这简单的操作讲的有趣些,那我就玩点套路,不直接讲API,先来看一个类:

package com.aristark.note.database;

public class NoteDbScheme {
    public static final class NoteTable{
        public static final String name = "notes";
        
        public static final class Cols{
            public static final String UUID = "uuid";
            public static final String TITLE = "title";
            public static final String CONTENT = "content";
            public static final String DATE = "date";
            public static final String TAG = "tag";
            
        }
    }
}

类的名字叫NoteDbScheme,表明它是一个关于“计划”的类,而且和database少不了干系,再来回忆Note这个类:

public class Note {
    private UUID uuid;
    private String title;
    private String content;
    private Date date;
    private String tag;

    public Note(){
        uuid = UUID.randomUUID();
        date = new Date();
    }

    public UUID getUuid() {
        return uuid;
    }

    public Date getDate() {
        return date;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    public String getTag() {
        return tag;
    }
}

很容易看出来Note类里的私有字段和NoteDbScheme里常量的值是一致的,言下之意,我们可以从数据库里取值赋给Note类中的字段,也可以把Note类中字段的值存入数据库。再回忆一下之前我们是怎么用Note类的,我们在NoteLab创建了一个泛型为Note的ArrayList,然后通过NoteListActivity把它渲染到layout,也就是用户的界面上,这样看来,Note类隐隐约约好像作为一个沟通的桥梁(其实了解MVC模式的朋友应该很好理解)。

三 创建数据库

1.sql简介:了解sql知识点这里
了解sqlite知识点这里

懒得看的就直接看这段sql语句,强行接受我的解释就行了:

create table note(
    _int integer primary key autoincrement,
    uuid text,
    title text,
    content text,
    date text,
    tag text
)

这段SQL语句的意思是,创建一个名叫“note”的表,包含6个字段,以第一个字段为例,_int为字段名,integer为字段类型,primary key autoincrement表示它是主键并自增。请大家记住这个套路。

2.SQLiteOpenHelper---数据库好帮手
上面已经给出了创建表并定义字段的方法,在别的地方它可以在终端或者脚本中运行,在Android世界里自然要在类中大展宏图。如果你的英语还可以请你打开Android->sdk>doc,在官方文档中搜索SQLiteOpenHelper,你会发现你要做的这个类基本上已经帮你封装好了,你要做的就是继承它,并依照你的需求去敲敲打打就行。好,来写代码:

public class NoteBaseHelper extends SQLiteOpenHelper {
    @Override
    public void onCreate(SQLiteDatabase db) {
        
    }

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

    }
}

这里需要覆写的onCreate方法就是我们创建表的地方,当NoteBaseHelper被实例化时,它会自动检测数据库中有没有note这个表,如果没有就调用该方法。所以我们把刚刚的sql语句写进去:

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL("create table " +NoteDbScheme.NoteTable.name+"(" +
            "_id integer primary key autoincrement,"
            +NoteDbScheme.NoteTable.Cols.UUID+","
            +NoteDbScheme.NoteTable.Cols.TITLE+","
            +NoteDbScheme.NoteTable.Cols.CONTENT+","
            +NoteDbScheme.NoteTable.Cols.DATE+","
            +NoteDbScheme.NoteTable.Cols.TAG
            +")"
    );

}

这里写的和上面其实是完全一样的,只不过我们是用NoteDbScheme中的常量代替了字符串而已,这样做的好处是如果以后表中的字段需要变更的话我们只要在NoteDbScheme中变更就行了,而不需要动这里的代码。另一个需要覆写的onUpgrade在数据库需要更新的时候调用。我们再添加两个常量来标记数据库的名字和版本:

public class NoteBaseHelper extends SQLiteOpenHelper {
    public static final int VERSON = 1;
    public static final String DATABASE_NAME = "NoteBase";
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table " +NoteDbScheme.NoteTable.name+"(" +
                "_id integer primary key autoincrement,"
                +NoteDbScheme.NoteTable.Cols.UUID+","
                +NoteDbScheme.NoteTable.Cols.TITLE+","
                +NoteDbScheme.NoteTable.Cols.CONTENT+","
                +NoteDbScheme.NoteTable.Cols.DATE+","
                +NoteDbScheme.NoteTable.Cols.TAG
                +")"
        );

    }

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

    }
}

最后别忘了创建NoteBaseHelper的构造方法:

public NoteBaseHelper(Context context) {
    super(context,DATABASE_NAME,null,VERSON);
}

DATABASE_NAME和VERSON就是我们刚才定义过的。

3.创建数据库和表
前面我们分析了,我们用NoteLab类将数据渲染到前台用户界面,同样的我们也用它来与后台数据库交互,先来回顾一下NoteLab类:

public class NoteLab {
    private static NoteLab sNoteLab; //for the global use
    private ArrayList notes;

    private NoteLab(Context context){
        notes = new ArrayList();

        //generate 100 Note Objects
//        for (int i=0;i<100;i++){
//            Note note = new Note();
//            note.setTitle("this is title "+i);
//            note.setContent("this is content "+i+"balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
");
//            notes.add(note);
//        }
    }

    public static NoteLab getNoteLab(Context context){
        if (sNoteLab == null){
            sNoteLab = new NoteLab(context);
        }

        return sNoteLab;
    }

    public ArrayList getNotes() {
        return notes;
    }

    public void addNote(Note note){
        notes.add(note);
    }

    public Note getNote(UUID uuid){
        for (Note note : notes){
            if (note.getUuid().equals(uuid)){
                return note;
            }
        }

        return null;
    }
}

每个方法的用途都可以通过命名来体现,实在看不懂的翻看我前面两篇文章就行了。
public SQLiteDatabase getWritableDatabase ()

Added in API level 1
Create and/or open a database that will be used for reading and writing. The first time this is called, the database will be opened and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and/or onOpen(SQLiteDatabase) will be called.

Once opened successfully, the database is cached, so you can call this method every time you need to write to the database. (Make sure to call close() when you no longer need the database.) Errors such as bad permissions or a full disk may cause this method to fail, but future attempts may succeed if the problem is fixed.

Database upgrade may take a long time, you should not call this method from the application main thread, including from ContentProvider.onCreate().

Returns
a read/write database object valid until close() is called
Throws
SQLiteException if the database cannot be opened for writing

这是Google官方文档中关于SQLiteOpenHelper类的一个方法,用来创建或者打开一个数据库,好,我们就在NoteLab类的构造方法中来使用这个方法:

public class NoteLab {
    private static NoteLab sNoteLab; //for the global use
    private ArrayList notes;
    private Context context;
    private SQLiteDatabase database;

    private NoteLab(Context context){
        notes = new ArrayList();
        database =  new NoteBaseHelper(context).getWritableDatabase();


        //generate 100 Note Objects
//        for (int i=0;i<100;i++){
//            Note note = new Note();
//            note.setTitle("this is title "+i);
//            note.setContent("this is content "+i+"balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
balabalabalabalalabalabalabalabalala
");
//            notes.add(note);
//        }
    }
    ......
}

这样我们在创建NoteLab的同时也就创建了NoteBase数据库和note表。好,编译,运行,这时候打开Android Device Monitor(Android Studio问号左边的小机器人按钮)

在Android中,所有App的本地数据库都在data->data下,找到我们这个应用的包名之下的databases目录下,我们看到了NoteBase数据库,说明我们成功创建了note表!

四 插入数据

插入数据无非就是将一组键值对提交给操作数据的API,而这里的键值对就是以ContentValues来组装的(大家在这里一定要去官方文档搜到这个类好好读一下,不然可能会在这里卡住),我们现在在NoteLab类中添加如下方法:

   private ContentValues getValues(Note note){
        ContentValues values = new ContentValues();
        values.put(NoteDbScheme.NoteTable.Cols.UUID,note.getUuid().toString()![图片描述][5]);
        values.put(NoteDbScheme.NoteTable.Cols.TITLE,note.getTitle());
        values.put(NoteDbScheme.NoteTable.Cols.CONTENT,note.getContent());
        values.put(NoteDbScheme.NoteTable.Cols.DATE,note.getDate().toString());
        values.put(NoteDbScheme.NoteTable.Cols.TAG,note.getTag());
        return values;
    }

代码很容易读,put方法接受的就是key--value对,这里再一次用到了NoteDbScheme类,是不是觉得很奇妙很方便?现在我们改写之前的addNote方法:

    public void addNote(Note note){
//        notes.add(note);
        ContentValues values = getValues(note);
        database.insert(NoteDbScheme.NoteTable.name,null,values);
    }

这里着重掌握insert方法,第一个参数接受将要插入的表名,第三个参数就是我们需要组装的ContentValues,第二个参数是关于插入空记录的相关设置,大家可以去查看文档。好,编译,运行:
点击右上角加号,输入数据几组数据

很遗憾我现在并不会检验数据是否写进表,不管了继续写代码,去把数据库的记录读出来!

五 读取数据

写了这么多有点写不下去了,不知道你是不是也看不下去了,你心里肯定想这太冗杂了(谁让咱们使用java写android呢,随便写写就感觉好多的样子,但是换个角度想我们可以对外吹我们的代码量很多啊哈哈,虽然并没有什么卵用)。扯店犊子,大家回顾一下C语言中的指针和java语言中的引用(句柄?handle?指针?),或者是数组中的索引,我们会发现这些重要的抽象对于我们操作数据是很有用的,对了,在Android我们有Cursor(中文意思是游标,对,就是游标卡尺的那个游标),我们可以变相的把它理解为一个带有指针的数组,我们可以通过操作“指针”来获取其中的数组。每次我们通过一定的条件进行数据库查询时,返回的就是一个Cursor, 它可以依次指向查询结果记录的每一条。不小心又说多了,来,写代码,在NoteLab中创建如下方法:

private Cursor queryNote(String whereClause,String[] whereArgs){
    Cursor cursor = database.query(
            NoteDbScheme.NoteTable.name,
            null,
            whereClause,
            whereArgs,
            null,
            null,
            null
    );
    return cursor
}

我们需要关注的是query方法,第一个参数是要查询的表名,第三个参数和第四个参数对应SQL语句中的where(少年,去回顾一下sql语句吧)。

但Cursor还不不太利于我们的操作,来粉饰一波,在Cursor外面再裹上一层,因此就有了CursorWrapper,创建一个类让它继承CursorWrapper:

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

变更刚刚的queryNote方法:

private CursorWrapper queryNote(String whereClause, String[] whereArgs){

Cursor cursor = database.query(
        NoteDbScheme.NoteTable.name,
        null,
        whereClause,
        whereArgs,
        null,
        null,
        null
);
return new CursorWrapper(cursor);

}
之所以说CursorWrapper方便,是因为我们将查询返回的Cursor传入该类之后,可以很方便的把查询的值取出来,还是看代码:
......
......

六 后记

别看了,太晚了,写之前没料到会有这么长。明天继续写。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/12637.html

相关文章

  • Android Studio

    摘要:应用程序响应速度最糟糕的是应用程序无响应对话框。然而,不幸的是,并不能获取所要的结果,宽高值均为。提供侧滑操作的控件这是一款提供侧滑功能的,可以设置它的滑动方向左右上下。 写给 Android 开发者的混淆使用手册 点击打开链接 毫无疑问,混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。 首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源...

    dunizb 评论0 收藏0
  • 【Bugly干货分享】TRIM:提升磁盘性能,缓解Android卡顿

    摘要:长期使用手机必将产生大量的磁盘碎片,而磁盘碎片将会降低磁盘的读写性能,从而影响系统流畅度。相对于方案一,该方案总体耗时较长,但不会影响正常操作时的磁盘性能。主动调用后,可以发现卡的效率指标均恢复至接近原始值水平但仍未完全达到初始状态的水平。 Bugly 技术干货系列内容主要涉及移动开发方向,是由 Bugly 邀请腾讯内部各位技术大咖,通过日常工作经验的总结以及感悟撰写而成,内容均属原创...

    bovenson 评论0 收藏0
  • 程序员MM的自白:磨人小妖精之安卓碎片化

    摘要:想要做更全面的自动化机型测试,破解兼容问题,腾讯优测是很好的选择。在及之前的版本中,使用至版本使用的是版本开始则使用。据不完全统计,要适配市场上大部分的设备,如下所列出的一大长串权限是免不了的,而且肯定还有不少漏网之鱼。 文/腾讯优测 章婉霞 除了crash问题,Android平台的碎片化越来越受到移动开发的关注,且不谈支持Android系统的移动设备早已过万款,屏幕、品牌以及传感器等...

    Yuqi 评论0 收藏0
  • android必读文 - 收藏集 - 掘金

    摘要:如何签名掘金签名是阻碍开发者集成最大的绊脚石,这里主要针对签名的生成和使用进行讲解,高级开发者可忽略。但是当我们需要去做权限管理及其封装掘金前言出来很久了,都快发布了,尽管如此还是要整理一下这块。 给所有开发者的 React Native 详细入门指南(第一阶段) - Android - 掘金本文已授权微信公众号::鸿洋(hongyangAndroid)原创首发 本文为Marno原创,...

    Simon_Zhou 评论0 收藏0
  • MVVM_Android-CleanArchitecture

    摘要:业务层,业务层,是最为核心的一层。对于和的状态保存恢复也通过处理。对于的绑定操作和命令操作都是暴露的,也易于测试。需要注意的是标签的节点中要使用到根节点中标签里设置的的话需要这样设置抽象类中设置了和注解,只起到清晰提醒作用。 原文发表于:Rockos blog(rocko.xyz)] - MVVM_Android-CleanArchitecture 前言 Architecture is...

    icattlecoder 评论0 收藏0
  • MVVM_Android-CleanArchitecture

    摘要:业务层,业务层,是最为核心的一层。对于和的状态保存恢复也通过处理。对于的绑定操作和命令操作都是暴露的,也易于测试。需要注意的是标签的节点中要使用到根节点中标签里设置的的话需要这样设置抽象类中设置了和注解,只起到清晰提醒作用。 原文发表于:Rockos blog(rocko.xyz)] - MVVM_Android-CleanArchitecture 前言 Architecture is...

    gaosboy 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<