Android 数据存储笔记

我最近做项目用到了这个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/<PackageName>/databases

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

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

1// 创建名叫test.db的数据库。数据库如果不存在就创建它,如果存在就打开它 2SQLiteDatabase db = openOrCreateDatabase(getFilesDir() + "/test.db", Context.MODE_PRIVATE, null); 3// 删除名叫test.db数据库 4// 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函数内部根据新旧版本号进行表结构变更处理。

例如:

1//数据库帮助器SQLiteOpenHelper 2public class MySqliteHelper extends SQLiteOpenHelper { 3 4 public MySqliteHelper(Context context, 5 String name, 6 SQLiteDatabase.CursorFactory factory, 7 int version) { 8 super(context, name, factory, version); 9 } 10 public MySqliteHelper(Context context){ 11 super(context,Constant.DATABASE_NAME,null,Constant.DATABASE_VERSION); 12 } 13 14 @Override 15 public void onCreate(SQLiteDatabase db) { 16 // TODO 创建数据库后,对数据库的操作     // sql中Constant是用来存放一些关于数据库的常量的类     // 类型有:Integer、text文本、varchar(n)、real浮点型、blob二进制类型 String sql = "create table if not exists "+Constant.TABLE_NAME+"("+ 17 Constant.ID+" Integer primary key ,"+ 18 Constant.USER+" text,"+ 19 Constant.DATE+" text,"+ 20 Constant.TIME+" text,"; 21 db.execSQL(sql); 22 } 23 24 @Override 25 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 // TODO 更改数据库版本的操作,根据新旧版本号进行表结构变更处理,当打开数据库时传入版本号与当前不同会调用此方法    //在使用中只需要调用构造函数时把版本号参数version改大即可 27 28    db.execSQL("drop table if exists " + Constant.TABLE_NAME);    onCreate(db); 29 30 }   @Override   public void onOpen(SQLiteDatabase db) {     super.onOpen(db); // TODO 每次成功打开数据库后首先被执行   } }
  • 数据库操作的工具类(封装保证数据库安全的必要方法和操作数据库记录的方法)

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

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

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

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

例如:

1/** 2 * DbManger 操作我们数据库的工具类 我们一般写成单例模式 3 * 单例模式 : 在整个应用程序中 不管什么地方(类) 获得的都是同一个对象实例*/ 4public class DbManger { 5 private static final String TAG = "DbManger"; 6 private static MySqliteHelper helper; //建立一个数据库对象 7 //表名 8 private String table_name ="p_data"; 9    /**单例模式:不能让每一个类都能new一个,那样就不是同一个对象了,所以首先构造函数要私有化,以上下文Context作为参数 10 * @param ctx 本类的上下文对象 11 * @return 12 */ 13 private DbManger(Context ctx){ 14 //由于数据库只需要调用一次,所以在单例中建出来 15 helper= new MySqliteHelper(ctx); 16 } 17 18 //public static 为静态类型,要调用就要有一个静态的变量,为私有的 19 private static DbManger instance; 20 21 //既然该类是私有的 那么别的类就不能够调用 那么就要提供一个public static(公共的 共享的)的方法 22 //方法名为getInstance 参数为上下文 返回值类型为这个类的实例 23  //要加上一个synchronized(同步的)如果同时有好多线程 同时去调用getInstance()方法 就可能会出现一些创建(new)多个BlackDao的现象 24 public static synchronized DbManger getInstance(Context ctx){ 25 //如果为空 就创建一个, 如果不为空就还用原来的 这样整个应用程序中就只能获的一个实例 26 if(instance == null){ 27 instance = new DbManger(ctx); 28 } 29 return instance; 30 }//常用方法 增删改查 31 32 /** 33 * 添加数据 至数据库 34 * @param USER 用户名 DATE 日期 TIME 时间 35 */ 36 public void addData(String USER,String DATE,String TIME){ 37 //获得一个可写的数据库的一个引用 38 SQLiteDatabase db = helper.getWritableDatabase(); 39 40 ContentValues values= new ContentValues(); 41 values.put(Constant.USER, USER); 42 values.put(Constant.DATE, DATE); 43 values.put(Constant.TIME, TIME);// 参数一:表名,参数三,是插入的内容 44 // 参数二:只要能保存 values中是有内容的,第二个参数可以忽略 45 db.insert(table_name, null, values); 46 Log.d(TAG, "addData: 数据保存成功:"+USER+""+DATE+""+TIME); 47 } 48 49 /** 50 * 删除用户 51 * @param user 52 */ 53 public void deletebyuser(String user){ 54 SQLiteDatabase db = helper.getWritableDatabase(); 55 //表名 删除的条件 56 db.delete(table_name, "user = ?", new String[] {user}); 57 58 } 59 60 /** 61 * 删除某条记录 62 * @param id 63 */ 64 public void deletebyid(int id){ 65 SQLiteDatabase db = helper.getWritableDatabase(); 66 //表名 删除的条件 67 db.delete(table_name, "id = ?", new String[] {Integer.valueOf(id).toString()}); 68 } 69 70 /** 71 * //查找 每一个黑名单都有 号码和模式 先把号码和模式封装一个bean 72 * 获得所有的黑名单 73 * @param user 用户名 74 * @param pageIndex 页数 75 * @param pageSize 每页显示的行数 76 * @return 77 */ 78 //分页查询 修改 p_data是一个新定义的,用来存放一系列数据的类 79 public List<p_data> getAllData(String user,int pageIndex, int pageSize){//创建集合对象 80 List<p_data> data = new ArrayList<p_data>(); 81 SQLiteDatabase db = helper.getReadableDatabase(); //Cursor cursor = db.query(p_data, null, null, null, null, null, null);//查询全部数据//order by _id desc 根据_id倒叙排列 使新添加的数据在查询时显示上面。根据用户名查询 82 //Cursor cursor = db.rawQuery("select * from p_data where user = ?"+";", new String[]{user}); 83 //分页查询:pageSize每页显示的数目,pageIndex页数 84 Cursor cursor = db.rawQuery("select * from p_data where user = ? order by id desc limit "+pageSize +" offset "+((pageIndex-1)*pageSize)+";", new String[]{user});//返回的 cursor 默认是在第一行的上一行 85 //遍历 86 while(cursor.moveToNext()){// cursor.moveToNext() 向下移动一行,如果有内容,返回true 87 String TIME = cursor.getString(cursor.getColumnIndex("time")); 88 // 获得time 这列的值 89 String DATE = cursor.getString(cursor.getColumnIndex("date")); 90 // 获得date 这列的值//将查找到的数据封装到bean中 91 p_data bean = new p_data(user,DATE,TIME); 92 //封装的对象添加到集合中 93 data.add(bean); 94 } 95 96 //关闭cursor 97 cursor.close(); 98 //SystemClock.sleep(1000);// 休眠2秒,查找出的数据比较多、比较耗时的情况下使用 99 Log.d(TAG, "getAllData: 查询数据库数据"+data ); 100 return data; 101 } 102 103 104 /** 105 * 获得数据的数量 106 */ 107 public int getNumCount(String user){ 108 SQLiteDatabase db = helper.getReadableDatabase(); 109 Cursor cursor = db.query(table_name, new String[] {"count(*)"}, "user = ?",new String[] {user}, null, null, null, null); 110 111 cursor.moveToNext(); 112 int count = cursor.getInt(0);// 仅查了一列,count(*) 这一刻列 113 114 cursor.close(); 115 return count; 116 117 } 118}

可被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中):

1private DbManger dbManger; 2 3dbManger = DbManger.getInstance(view.getContext());//fragment 4dbManger = DbManger.getInstance(this);//activity

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

  • 补充:保存数据的类

    public class p_data {

    1private String USER; 2private String DATE; 3private String TIME; 4 5public Nbp_data( String USER, String DATE, String TIME) { 6 this.USER = USER; 7 this.DATE = DATE; 8 this.TIME = TIME; 9} 10 11public void setDATE(String DATE) { 12 this.DATE = DATE; 13} 14 15public void setUSER(String USER) { 16 this.USER = USER; 17}

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

  • 补充:保存常量的类

    public class Constant {

    1public static final String DATABASE_NAME = "info.db"; // 数据库名称 2public static final int DATABASE_VERSION = 1; //数据库版本 3public static final String TABLE_NAME = "p_data"; //数据库表名 4/** 5 * id、user、date以下是数据库表中的字段 6 */ 7public static final String ID = "id"; //id主键 8public static final String USER = "user"; //用户 9public static final String DATE = "date"; //日期 10public 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

点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )