Android 数据存储笔记

Stella981
• 阅读 666

我最近做项目用到了这个SQLite本地数据库,所以写篇博客总结一下,希望也能对小白有所帮助

  • 什么时候用SQLite(有固定规则的,大量的数据要保存时)

SharedPreferences是一种轻型的数据存储方式,在保存数据的时候其实存储的是key-value对,类似于Map。存储位置:/data/data/应用包名/shared_prefs/文件名.xml。通常用来存储一些简单的配置信息。实际开发中,SharedPreferences共享参数经常存储的数据有App的个性化配置信息、用户使用App的行为信息、临时需要保存的片段信息等。

简单且孤立的数据可保存在SharedPreferences。若是复杂且相互间有关的数据,则要保存在数据库中。(有固定规则的,大量的数据保存在数据库中)

文本形式的数据可保存在SharedPreferences。若是二进制数据,则要保存在文件中。(没有固定规则的,大量的数据保存在文件中)

haredPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其只能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。

SQLite是一个小巧的嵌入式数据库,使用方便、开发简单。它只是一个嵌入式的数据库引擎。在Android里,提供了SQLiteDatabase类,该类的一个对象就表明一个数据库,其实在底层就是一个文件。默认存储位置:/data/data//databases

  • SQLiteDatabase数据库管理类(直接对数据库进行操作)

 SQLiteDatabase是SQLite的数据库管理类,开发者可以在活动页面代码或任何能取到Context的地方获取数据库实例。然后通过SQLiteDatabase提供的一些API来对数据库进行操作:

// 创建名叫test.db的数据库。数据库如果不存在就创建它,如果存在就打开它
SQLiteDatabase db = openOrCreateDatabase(getFilesDir() + "/test.db", Context.MODE_PRIVATE, null);
// 删除名叫test.db数据库
// deleteDatabase(getFilesDir() + "/test.db");

调用该类API要用到SQL语句,SQLite的多数SQL语法与Oracle一样,可以到菜鸟网上查阅:https://www.runoob.com/sqlite/sqlite-tutorial.html

SQLiteDatabase中常用的API:

1. 管理类,用于数据库层面的操作。

  • openDatabase:打开指定路径的数据库。
  • isOpen:判断数据库是否已打开。
  • close:关闭数据库。
  • getVersion:获取数据库的版本号。
  • setVersion:设置数据库的版本号。

2. 事务类,用于事务层面的操作。

  • beginTransaction:开始事务。
  • setTransactionSuccessful:设置事务的成功标志。
  • endTransaction:结束事务。执行本方法时,系统会判断是否已执行setTransactionSuccessful,如果之前已设置就提交,如果没有设置就回滚。

3. 数据处理类,用于数据表层面的操作。

  • execSQL:执行拼接好的SQL控制语句。一般用于建表、删表、变更表结构。
  • delete:删除符合条件的记录。
  • update:更新符合条件的记录。
  • insert:插入一条记录。
  • query:执行查询操作,返回结果集的游标。
  • rawQuery:执行拼接好的SQL查询语句,返回结果集的游标。

但是直接通过SQLiteDatabase进行操作数据库非常不方便,必须小心不能重复地打开数据库,处理数据库的升级也很不方便。

因此Android提供了一个辅助工具—— SQLiteOpenHelper,我们可以通过SQLiteOpenHelper这个数据库帮助器来安全方便地打开、升级数据库。

  • SQLiteOpenHelper数据库帮助器(安全方便地打开、升级数据库)

使用方法:

新建一个继承自SQLiteOpenHelper的数据库操作类,提示重写onCreate和onUpgrade两个方法。

其中,onCreate方法只在第一次打开数据库时执行,在此可进行表结构创建的操作;

onUpgrade方法在数据库版本升高时执行,因此可以在onUpgrade函数内部根据新旧版本号进行表结构变更处理。

例如:

//数据库帮助器SQLiteOpenHelper
public class MySqliteHelper extends SQLiteOpenHelper {

    public MySqliteHelper(Context context,
                          String name,
                          SQLiteDatabase.CursorFactory factory,
                          int version) {
        super(context, name, factory, version);
    }
    public MySqliteHelper(Context context){
        super(context,Constant.DATABASE_NAME,null,Constant.DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // TODO 创建数据库后,对数据库的操作     // sql中Constant是用来存放一些关于数据库的常量的类     // 类型有:Integer、text文本、varchar(n)、real浮点型、blob二进制类型        String sql = "create table if not exists "+Constant.TABLE_NAME+"("+
                Constant.ID+" Integer primary key ,"+
                Constant.USER+" text,"+
                Constant.DATE+" text,"+
                Constant.TIME+" text,";
        db.execSQL(sql);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
       // TODO 更改数据库版本的操作,根据新旧版本号进行表结构变更处理,当打开数据库时传入版本号与当前不同会调用此方法    //在使用中只需要调用构造函数时把版本号参数version改大即可

    db.execSQL("drop table if exists " + Constant.TABLE_NAME);    onCreate(db);

 }   @Override   public void onOpen(SQLiteDatabase db) {     super.onOpen(db); // TODO 每次成功打开数据库后首先被执行    } }
  • 数据库操作的工具类(封装保证数据库安全的必要方法和操作数据库记录的方法)

在这个类中,封装保证数据库安全的必要方法,包括获取单例对象、打开数据库连接、关闭数据库连接,并且封装对表记录进行增加、删除、修改、查询的操作方法。

获取单例对象:确保App运行时数据库只被打开一次,避免重复打开引起错误。

打开数据库连接:SQLite有锁机制,即读锁和写锁的处理;故而数据库连接也分两种,读连接可调用SQLiteOpenHelper的getReadableDatabase方法获得,写连接可调用getWritableDatabase获得。

关闭数据库连接:数据库操作完毕后,应当调用SQLiteDatabase对象的close方法关闭连接。

例如:

/**
 * DbManger 操作我们数据库的工具类  我们一般写成单例模式
 * 单例模式 :  在整个应用程序中  不管什么地方(类)  获得的都是同一个对象实例*/
public class DbManger {
    private static final String TAG = "DbManger";
    private static MySqliteHelper helper; //建立一个数据库对象
    //表名
    private String table_name ="p_data";
       /**单例模式:不能让每一个类都能new一个,那样就不是同一个对象了,所以首先构造函数要私有化,以上下文Context作为参数
     * @param ctx 本类的上下文对象
     * @return
     */
     private DbManger(Context ctx){
        //由于数据库只需要调用一次,所以在单例中建出来
        helper= new MySqliteHelper(ctx);
    }

    //public static 为静态类型,要调用就要有一个静态的变量,为私有的
    private static DbManger instance;

    //既然该类是私有的  那么别的类就不能够调用    那么就要提供一个public static(公共的  共享的)的方法
    //方法名为getInstance 参数为上下文    返回值类型为这个类的实例
   //要加上一个synchronized(同步的)如果同时有好多线程 同时去调用getInstance()方法  就可能会出现一些创建(new)多个BlackDao的现象
    public static synchronized DbManger getInstance(Context ctx){
        //如果为空 就创建一个, 如果不为空就还用原来的 这样整个应用程序中就只能获的一个实例
        if(instance == null){
            instance = new DbManger(ctx);
        }
        return  instance;
    }//常用方法  增删改查

    /**
     * 添加数据 至数据库
     * @param USER 用户名 DATE 日期 TIME 时间 
     */
    public void addData(String USER,String DATE,String TIME){
        //获得一个可写的数据库的一个引用
        SQLiteDatabase db = helper.getWritableDatabase();

        ContentValues values= new ContentValues();
        values.put(Constant.USER, USER); 
        values.put(Constant.DATE, DATE);
        values.put(Constant.TIME, TIME);// 参数一:表名,参数三,是插入的内容
        // 参数二:只要能保存 values中是有内容的,第二个参数可以忽略
        db.insert(table_name, null, values);
        Log.d(TAG, "addData: 数据保存成功:"+USER+""+DATE+""+TIME);
    }

    /**
     * 删除用户
     * @param user
     */
    public void deletebyuser(String user){
        SQLiteDatabase db = helper.getWritableDatabase();
        //表名  删除的条件
        db.delete(table_name, "user = ?", new String[] {user});

    }

    /**
     * 删除某条记录
     * @param id
     */
    public void deletebyid(int id){
        SQLiteDatabase db = helper.getWritableDatabase();
        //表名  删除的条件
        db.delete(table_name, "id = ?", new String[] {Integer.valueOf(id).toString()});
    }

    /**
     * //查找 每一个黑名单都有 号码和模式  先把号码和模式封装一个bean
     * 获得所有的黑名单
     * @param user 用户名
     * @param pageIndex 页数
     * @param pageSize 每页显示的行数
     * @return
     */
    //分页查询 修改 p_data是一个新定义的,用来存放一系列数据的类
    public List<p_data> getAllData(String user,int pageIndex, int pageSize){//创建集合对象
        List<p_data> data = new ArrayList<p_data>();
        SQLiteDatabase db = helper.getReadableDatabase();        //Cursor cursor = db.query(p_data, null, null, null, null, null, null);//查询全部数据//order by _id desc 根据_id倒叙排列 使新添加的数据在查询时显示上面。根据用户名查询
        //Cursor cursor = db.rawQuery("select * from p_data where user = ?"+";", new String[]{user});
        //分页查询:pageSize每页显示的数目,pageIndex页数
        Cursor cursor = db.rawQuery("select * from p_data where user = ? order by id desc limit "+pageSize +" offset "+((pageIndex-1)*pageSize)+";", new String[]{user});//返回的 cursor 默认是在第一行的上一行
        //遍历
        while(cursor.moveToNext()){// cursor.moveToNext() 向下移动一行,如果有内容,返回true
            String TIME = cursor.getString(cursor.getColumnIndex("time"));
            // 获得time 这列的值
            String DATE = cursor.getString(cursor.getColumnIndex("date"));
            // 获得date 这列的值//将查找到的数据封装到bean中
            p_data bean = new p_data(user,DATE,TIME);
            //封装的对象添加到集合中
            data.add(bean);
        }

        //关闭cursor
        cursor.close();
        //SystemClock.sleep(1000);// 休眠2秒,查找出的数据比较多、比较耗时的情况下使用
        Log.d(TAG, "getAllData: 查询数据库数据"+data );
        return data;
    }


    /**
     * 获得数据的数量
     */
    public int getNumCount(String user){
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor cursor = db.query(table_name, new String[] {"count(*)"}, "user = ?",new String[] {user}, null, null, null, null);

        cursor.moveToNext();
        int count = cursor.getInt(0);// 仅查了一列,count(*) 这一刻列

        cursor.close();
        return count;

    }
}

可被SQLite直接使用的数据结构是ContentValues类,类似于映射Map,提供put和get方法用来存取键值对。区别之处在于ContentValues的键只能是字符串”。ContentValues主要用于记录增加和更新操作,即SQLiteDatabase的insert和update方法。

对于查询操作来说,使用的是另一个游标类Cursor。调用SQLiteDatabase的query和rawQuery方法时,返回的都是Cursor对象,因此获取查询结果要根据游标的指示一条一条遍历结果集合,Cursor的常用方法:

1. 游标控制类方法,用于指定游标的状态。

  • close:关闭游标。
  • isClosed:判断游标是否关闭。
  • isFirst:判断游标是否在开头。
  • isLast:判断游标是否在末尾。

2. 游标移动类方法,把游标移动到指定位置。

  • moveToFirst:移动游标到开头。
  • moveToLast:移动游标到末尾。
  • moveToNext:移动游标到下一条记录。
  • moveToPrevious:移动游标到上一条记录。
  • move:往后移动游标若干条记录。
  • moveToPosition:移动游标到指定位置的记录。

3. 获取记录类方法,可获取记录的数量、类型以及取值。

  • getCount:获取结果记录的数量。

  • getInt:获取指定字段的整型值。

  • getFloat:获取指定字段的浮点数值。

  • getString:获取指定字段的字符串值。

  • getType:获取指定字段的字段类型。

  • 在Activity、fragment中使用SQLite

创建数据库(onCreateView或onCreate中):

private DbManger dbManger;

dbManger = DbManger.getInstance(view.getContext());//fragment
dbManger = DbManger.getInstance(this);//activity

然后通过dbManger调用所需的操作函数即可。

  • 补充:保存数据的类

    public class p_data {

    private String USER;
    private String DATE;
    private String TIME;
    
    public Nbp_data( String USER, String DATE, String TIME) {
        this.USER = USER;
        this.DATE = DATE;
        this.TIME = TIME;
    }
    
    public void setDATE(String DATE) {
        this.DATE = DATE;
    }
    
    public void setUSER(String USER) {
        this.USER = USER;
    }
    

    public String getUSER() { return USER; } public String getDATE() { return DATE; } public String getTIME() { return TIME; } }

  • 补充:保存常量的类

    public class Constant {

    public static final String DATABASE_NAME = "info.db";  // 数据库名称
    public static final int DATABASE_VERSION = 1;          //数据库版本
    public static final String TABLE_NAME = "p_data";     //数据库表名
    /**
     * id、user、date以下是数据库表中的字段
     */
    public static  final String ID = "id";                //id主键
    public static  final String USER = "user";            //用户
    public static final String DATE = "date";             //日期
    public static final String TIME = "time";             //时间
    

    }

参考:

Android Studio开发实战:从零基础到App上线  欧阳燊著 清华大学出版社

CSDN博主:「快乐的金豆」 Android 数据储存的方式之本地数据库储存  https://blog.csdn.net/lijinweii/java/article/details/73930260

CSDN博主:「小鹿动画学编程」 搭建Android本地数据库(SQLite)的详细讲解  https://blog.csdn.net/qq_36903042/java/article/details/79772268

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
2年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这