一篇技术好文之Android数据库 SQite全解析

2,105 阅读6分钟
原文链接: www.jianshu.com
登录 注册写文章 首页下载APP

一篇技术好文之Android数据库 SQite全解析

aserbao 关注 赞赏支持

一篇技术好文之Android数据库 SQite全解析

0.556 字数 737阅读 672 数据库SQLite.jpg

这篇文章是数据库系列篇文章的第一篇,主要讲Android Sqlite数据库存储,后面陆续出GreenDao,LitePal, Realm,wcdb的文章,一如既往,如果遇到任何关于Android中SQLite的问题,都可以直接在我的文章底部留言,或者直接在我的公众号aserbao留言,文章会持续更新,希望这篇文章能为大家提供到帮助!如果觉得文章对你有用,就帮忙点个赞,若觉得文章写得不好之处望指出,必将加以修正!


qrcode_for_gh_2886db5aaf83_258.jpg

这篇文章主要讲SQlite数据库存储,从数据库建表到数据库的增删改查操作,再到数据库升级操作,最后是文章总结及参考链接

SQlite

1. 创建数据库

Android中使用SQlite,需要自己创建库,建表,添加数据!好在Android中提供了SQLiteOpenHelper类来帮助创建使用数据库,我们只需要继承这个类就可以实现数据库的创建和对数据的操作了!Android 中创建数据库需要通过如下几部:

  1. 创建一个类继承SQLiteOpenHelper,需要实现其三个方法:
    • 构造方法:需要给SQLiteOpenHelper传递四个参数:(上下文,数据库名,游标工厂(通常为null),当前数据库版本号);
    • onCreate(): 表的创建,初始化操作
    • onUpgrade(): 表升级
    • 还有:onDowngrade(): 表降级,onOpen:每次打开数据库,onBeforeDelete:
public class ThingManagerDBOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "mysql.db";
    private static final int VERSION = 1;
    public ThingManagerDBOpenHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS "
                + ThingDBController.TABLE_NAME
                + String.format(
                "("
                        + "%s INTEGER PRIMARY KEY AUTOINCREMENT, "
                        + "%s VARCHAR, "
                        + "%s INTEGER"
                        +")"
                , ThingManagerDBModel.ID
                , ThingManagerDBModel.MESSAGE
                , ThingManagerDBModel.TIME
        )) ;
    }


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

说下SQlite的数据类型 :

作用
NULL: 这个值为空值
VARCHAR(n): 长度不固定且其最大长度为 n 的字串,n不能超过 4000。
CHAR(n): 长度固定为n的字串,n不能超过 254。
INTEGER: 值被标识为整数,依据值的大小可以依次被存储为1,2,3,4,5,6,7,8.
REAL: 所有值都是浮动的数值,被存储为8字节的IEEE浮动标记序号.
TEXT: 值为文本字符串,使用数据库编码存储(TUTF-8, UTF-16BE or UTF-16-LE).
BLOB: 值是BLOB数据块,以输入的数据格式进行存储。如何输入就如何存储,不改 变格式。
DATA: 包含了 年份、月份、日期。
TIME: 包含了 小时、分钟、秒。

2. 创建一个数据库存储对象

public final class ThingManagerDBModel {
    public static final String TABLE_NAME = "mysql_thing";
    public static final String ID = "_id";
    public static final String MESSAGE = "message";
    public static final String TIME = "time";

    int id;
    String message;
    long time;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public long getTime() {
        return time;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public ContentValues toContentValues(){
        ContentValues cv = new ContentValues();
        cv.put(MESSAGE, message);
        cv.put(TIME, time);
        return cv;
    }
}

3. 创建一个数据库控制器实现增删改查

1. 增

  1. 使用Android APi来实现增加数据
 public boolean insertApi(int id, String message, long time){
        ThingManagerDBModel dbModel = new ThingManagerDBModel();
        if(id >= 0) {
            dbModel.id = id;
        }
        dbModel.message = message;
        dbModel.time = time;
        final boolean success = db.insert(TABLE_NAME, null, dbModel.toContentValues()) != -1;
        dbClose();
        return success;
    }
  1. 使用Sql语句实现增加数据
 public void insertRaw(String message, long time){
        String sql = " insert into " + TABLE_NAME + "(message,time) values(?,?)";
        Object[] args = {message,time};
        db.execSQL(sql,args);
        dbClose();
    }

2. 删

  1. 使用Api
  public boolean deleteApi(String whereClause,String[] whereArgs){
        boolean sucess = db.delete(TABLE_NAME, whereClause,whereArgs) != -1;
        dbClose();
        return sucess;
    }
  1. 使用Sql语句
 public void deleteRaw(String message){
        String sql = "delete from "+TABLE_NAME + " where message = ?";
        Object[] args = {message};
        db.execSQL(sql,args);
        dbClose();
    }

3. 改

  1. 使用api
public boolean updateApi(int id,String message,long time){
        ThingManagerDBModel dbModel = new ThingManagerDBModel();
        if(id >= 0) {
            dbModel.id = id;
        }
        dbModel.message = message;
        dbModel.time = time;
        boolean success = db.update(TABLE_NAME, dbModel.toContentValues(), "_id = ?", new String[]{String.valueOf(id)}) != -1;
        dbClose();
        return success;
    }
  1. 使用Sql条件修改
public void updateRaw(int id,String message,long time){
        String sql = "update "+TABLE_NAME + " set message = ?,time = ? where _id = ?";
        Object[] args = {message,time,id};
        db.execSQL(sql,args);
        dbClose();
    }

4. 查

  1. 使用Api
/**
     * @param cloums 要查询的字段
     * @param selection 查询条件
     * @param selectionArgs 填充查询条件的值
     * @return
     */
    public List<Thing> queryApi(String[] cloums,String selection,String[]  selectionArgs){
        try {
            Cursor c = db.query(TABLE_NAME, cloums, selection, selectionArgs, null, null, null);
            ArrayList<Thing> arrayList = new ArrayList<>();
            if (!c.moveToLast()) {
                return arrayList;
            }
            do {
                Thing model = new Thing();
                model.setId(c.getInt(c.getColumnIndexOrThrow(ThingManagerDBModel.ID)));
                model.setTime(c.getLong(c.getColumnIndexOrThrow(ThingManagerDBModel.TIME)));
                model.setMessage(c.getString(c.getColumnIndexOrThrow(ThingManagerDBModel.MESSAGE)));
                arrayList.add(model);
            } while (c.moveToPrevious());
            c.close();
            return arrayList;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            dbClose();
        }

        return new ArrayList<>();
    }

  1. 使用sql条件
 public List<Thing> queryRawById(String  id){
    //cursor获取的一定是在这里申明的查询条件值
        String sql = "select _id,message,time from "+TABLE_NAME + " where _id = ?";
        Cursor cursor = db.rawQuery(sql, new String[]{id});
        ArrayList<Thing> arrayList = new ArrayList<>();
        if (!cursor.moveToLast()) {
            return arrayList;
        }
        do {
            Thing model = new Thing();
            model.setId(cursor.getInt(cursor.getColumnIndex(ThingManagerDBModel.ID)));
            model.setTime(cursor.getLong(cursor.getColumnIndex(ThingManagerDBModel.TIME)));
           model.setMessage(cursor.getString(cursor.getColumnIndexOrThrow(ThingManagerDBModel.MESSAGE)));
            arrayList.add(model);
        } while (cursor.moveToPrevious());
        cursor.close();
        dbClose();
        return arrayList;
    }

4. 数据库的升级

在开发的过程中,我们避免不了需要修改数据库结构,添加字段或者删除字段!我们通过增加传递给SQLiteOpenHelper的版本值,得到onUpgrade方法的回调来实现对数据库的操作来进行升级。这里需要注意的是,如果用户安装版本1之后直接跳装版本3,这时候我们就需要在onUpgrade方法中通过oldVersion来进行每一个版本的逐渐升级,方法如下:


public class ThingManagerDBOpenHelper extends SQLiteOpenHelper {
    public static final String DB_NAME = "mysql.db";
    private static final int VERSION = 3;
    ……
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion){
            case 1:
                upToDbVersion2(db);
            case 2:
                upToDbVersion3(db);
            default:
              break;
        }
    }
    public void upToDbVersion2(SQLiteDatabase db){
        db.execSQL("ALTER TABLE " + ThingDBController.TABLE_NAME + " ADD COLUMN add_user_name text");
    }

    public void upToDbVersion3(SQLiteDatabase db) {
        ContentValues values = new ContentValues();
        values.put("message", "版本升级后的数据");
        db.update(ThingDBController.TABLE_NAME, values, null, null);
    }
}

问题

查询问题

Cursor只会拥有你查询的数据,如果在查询条件里面没有申请,Cursor里面就不会包含这个值,就会出现如下异常: 错误写法:

 String sql = "select message,time from "+TABLE_NAME + " where _id = ?";//条件里面没有申请查询_id的值
 ……something……
 model.setId(cursor.getInt(cursor.getColumnIndex(ThingManagerDBModel.ID)));//在这里提取会报异常

正确写法:

 String sql = "select  _id,message,time from "+TABLE_NAME + " where _id = ?";//条件里面没有申请查询_id的值
 ……something……
 model.setId(cursor.getInt(cursor.getColumnIndex(ThingManagerDBModel.ID)));//这里进行id值的提取

异常:

  java.lang.IllegalStateException: Couldn't read row 0, col -1 from CursorWindow.  Make sure the Cursor is initialized correctly before accessing data from it.
        at android.database.CursorWindow.nativeGetLong(Native Method)
        at android.database.CursorWindow.getLong(CursorWindow.java:513)
        at android.database.CursorWindow.getInt(CursorWindow.java:580)
        at android.database.AbstractWindowedCursor.getInt(AbstractWindowedCursor.java:69)
        at com.aserbao.aserbaosandroid.functions.database.mySql.beans.ThingDBController.queryRawById(ThingDBController.java:109)
        at com.aserbao.aserbaosandroid.functions.database.mySql.MySqlActivity.queryData(MySqlActivity.java:70)
        at com.aserbao.aserbaosandroid.functions.database.base.DataBaseBaseActivity.onViewClicked(DataBaseBaseActivity.java:85)
        at com.aserbao.aserbaosandroid.functions.database.base.DataBaseBaseActivity_ViewBinding$2.doClick(DataBaseBaseActivity_ViewBinding.java:48)
        at butterknife.internal.DebouncingOnClickListener.onClick(DebouncingOnClickListener.java:22)
        at android.view.View.performClick(View.java:6291)
        at android.view.View$PerformClick.run(View.java:24931)
        at android.os.Handler.handleCallback(Handler.java:808)
        at android.os.Handler.dispatchMessage(Handler.java:101)
        at android.os.Looper.loop(Looper.java:166)
        at android.app.ActivityThread.main(ActivityThread.java:7425)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:245)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:921)

参考链接

  1. Android 开发中使用 SQLite 数据库
  2. Android数据库Sqlite的基本用法及升级策略
6人点赞 技术好文   aserbao 拥有147钻 (约15.57元) 关注 "小礼物走一走,来简书关注我" 赞赏 广告

推荐阅读更多精彩内容

广告 aserbao关注 拥有147钻 (约15.57元) Android 相机开发,音视频入门教程全解 阅读 74 Android P之后关于网络的小知识点 阅读 38 年薪200W要交多少税 阅读 210

精彩继续

孩子放学回家撞见家里有陌生女人,为不打扰父亲,在寒风中等三小时不进门 阅读 12671 张嘉译为何变得这么油腻了?总喜欢跟小姑凉“谈恋爱”…… 阅读 11460 广告 评论0 赞6 6赞7赞 赞赏