数据存储技术

安卓提供了三种方式用于简单的数据持久化功能:文件储存,SharedPreference存储,数据库储存。

文件储存

用于保存一些简单的文本数据或二进制数据。
使用到的方法:Context类中提供了openFileOutput(String str,int a)方法 和 openFileInput(String str)方法

openFileOutput()方法:

文件默认的存储到data/data/<package name>/ files/目录下。
拥有两个参数 第一个是文件名 第二个是文件的操作方式字段。

文件的操作方式:
MODE_PRIVATE当指定同样文件名时会覆盖原文件中的内容
MODE_APPEND当该文件已存在时就往文件中追加内容,不会创建新文件

文件存储使用:java流

FileOutputStream out = openFileOutput("save", MODE_PRIVATE);
//方法得到的是一个FileOutputStream对象
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
//用OutputStreamWriter输出流读写器包装输出流,再用缓冲流包装输出流。
writer.write(text);//调用读写器方法写入数据text
openFileIntput()方法:
FileInputStream in = openFileInput("save");
//方法得到的是一个FileInputStream对象
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
//用InputStreamReader输入流读写器包装输出流,再用缓冲流包装输入流。
String len = "";
StringBuilder content=new StringBuilder();
while ((len = reader.readLine()) != null) {//调用读写器方法按行读取数据
content.append(len) };//每行均黏贴入缓冲域
content.toString();//缓冲域的内容转化为String

——————————————————————————————————————————

SharedPreferences储存:

sharedPreferences是采用键值对的方式存储数据的,它的储存方式比文件储存简单易用。

getSharedPreferences()方法:

默认的储存路径是:data/data/<package name>/shared_prefs/下
此方法接受两个参数:

第一个参数是文件名,如果文件不存在则会创建一个。
第二个参数是指定操作模式:
MODE_PRIVATE 表示只有当前应用程序可以对sharedPreferences文件读写。
MODE_MULTI_PROCESS 用于会有多个进程中对同一个sharedPreferences文件读取,6.0中废弃。

存入
SharedPreferences.Editor  editor = getSharedPreferences("save", MODE_PRIVATE).edit();
//拿到编辑器对象才能放入数据
editor.putString("name","Tom");
editor.apply();//提交放入的数据
取出
SharedPreferences pref= getSharedPreferences("save", MODE_PRIVATE);
String str =pref.getString(''name","");//第二个参数为拿不到所设置的默认值

——————————————————————————————————————————

数据库储存-SQLiteOpenHelper:

当我们需要储存大量复杂的关系型数据的时候,前两种方法就有点力不从心了,例如保存短息,联系人信息等,这个时候我们就可以使用安卓内置的数据库。
安卓系统内置了SQLLite数据库,它支持SQL语法,还遵循数据库的ACID事务,是一款轻量级的数据库。

创建数据库

1.创建数据库需要继承SQLiteOpenHelper,我们需要重写它的两个方法,onCreate()和onUpgrade().分别在这连个方法中创建和升级数据库。

2.SQLiteOpenHelper的构造方法,四个参数 第一个Context 第二个 数据库名 第三个 查询数据时返回一个自定义的Cursor,一般都传null , 第四个是数据库的版本号。

3.SQLiteOpenHelper中有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建和打开一个现有的数据库。当磁盘空间满的时候getReadableDatabase()会打开一个只读的数据库,getWritableDatabase()会出现异常。磁盘空间未满的时候都是创建或打开一个可读可写的数据库,并返回一个SQLiteDatabase对象,可以用其进行CRUD操作。

4.在SQLite数据库中数据类型 integer表示整形 real表示浮点型 text 表示文本类型 blob表示二进制类型,数据库文件会存放在data/data/<package name>/databases/下。


//MySQLiteOpenHelper实现类
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
     
    public static final String Create_contact = "create table person(_id integer primary key autoincrement, " +
            "name char(10), " +
            "salary char(20), " +
            "phone integer(20))";
 
    public MySQLiteOpenHelper(Context context, String name,CursorFactory factory, int version) {
        super(context, name, factory, version);
        mcontext = context;
    }
 
    public void onCreate(SQLiteDatabase db) {
        //当创建数据库的时候会调用此方法在此方法中创建表
        db.execSQL(Create_contact);
        Toast.makeText(mcontext, "数据库创建了", 0).show();
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
在数据库实现类中使用建表语言:

由于实现类继承了SQLiteOpenHelper,由此获得了SQLiteDatabase的操作对象与功能,并传入onCreate();与onUpgrade();在这两个方法中,就可以使用SQLiteDatabase的execSQL(String str)语句
执行字符串str中的建表语句。

升级数据库:

重写实现类 onUpgrade()方法。
并且修改实现类构造函数中的参数使其大于上一版本。

 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        switch (oldVersion) {//系统自动完成上一次new实现类对象的版本号传递
        case 1:
            db.execSQL(Create_sms);// 如果用户从1版本升级过来的话,就不会创建两张表而是再升级中添加一张表
         case 2:
            db.execSQL("alter table person add column category_id integer");// 如果用户从2版本升级过来的话,就会直接添加一个列字段
        default:
        }
Create/insert & Update/update & Delete/delete:增删改
//创建帮助类实例
MySQLiteOpenHelper  dbHelper=new MySQLiteOpenHelper(this,"SQL.db",null,0);
//获得操作数据库的功能对象
SQLiteDatabase db= dbHelper.getWritableDatabase();
//利用赋值容具类ContentValues完成对一行多列数据的转存
ContentValues values=new ContentValues();

//增:
values.put("column1","text1")
values.put("column2","text2")
...
//第二个参数nullColumnHack:当values参数为空或者里面没有内容的时候,我们insert是会失败的(底层数据库不允许插入一个空行)
//为了防止这种情况,在这里指定一个列名,到时候如果发现将要插入的行为空行时,就会将你指定的这个列名的值设为null,然后再向数据库中插入。
db.insert(String table(插入表名),columns, values);

//删: 删除指定列满足条件的数据   
//第二个参数(selection:查询条件)相当于SQL语句中where,?是一个占位符,由后一个参数str提供
db.delete(String table(删除表名),"column+关系式+?",string str);

//改:更改指定列满足条件的数据
//第三个参数(selection:查询条件)相当于SQL语句中where,?是一个占位符,由后一个参数str提供
//由values提供列名与修改内容
db.update(String table(更新表名),values,"column+关系式+?",string str);
Retrieve/select:查询

query(String table,String []Columns, String selection, String[]selectionArgs, String having, string orderBy);共7个参数 :
table:表名,不能为null
columns:要查询的列名,可以是多个,可以为null,表示查询所有列
selection:查询条件,比如id=? and name=? 可以为null
selectionArgs:对查询条件赋值,一个问号(占位符)对应一个值,按顺序 可以为null
groupBy: 指定需要按第二个参数来groupby分组的列,可以为null
having:语法having设置分组条件,可以为null
orderBy:指定查询结果排序方法,可以为null

调用query都会返回一个Cursor对象,对这个对象进行遍历即可得到查询后的所有数据

Cursor cursor = db.query("person", null, null, null, null, null, null);
  if(cursor.moveToFirst()) {
do{
 String colunm1 = cursor.getString(cursor.getColumnIndex("colunm1"));
 String colunm2 = cursor.getString(cursor.getColumnIndex("colunm2"));
.....
 }while(cursor.moveToNext());
}
cursor.close();
此外,也可使用db.execSQL(String str)执行SQL语言完成增删改,查询则使用db.rawQuery(String str);

——————————————————————————————————————————

数据库储存-LitePal:

快速配置
  1. 引入Jar包或源码
    LitePal的开源已经提交到 jcenter,故我们想要使用,只需要编辑app/build.gradle文件,在dependencies闭包中添加以下内容:
    compile 'org.litepal.android:core:1.4.1' //1.4.1是版本号,最新版本号在引用可见

  2. 配置litepal.xml
    接着在项目的main目录下创建assets目录并新建一个litepal.xml文件,并将以下代码拷贝进去:

<?xml version="1.0" encoding="utf-8"?>  
<litepal>  
    <dbname value="demo" ></dbname>  
    <version value="1" ></version>  
    <list>  
    </list>  
</litepal>

配置文件相当简单,<dbname>用于设定数据库的名字,<version>用于设定数据库的版本号,<list>用于设定所有的映射模型,我们稍后就会用到。

  1. 配置LitePalApplication
    由于操作数据库时需要用到Context,而我们显然不希望在每个接口中都去传一遍这个参数,那样操作数据库就显得太繁琐了。因此,LitePal使用了一个方法来简化掉Context这个参数,只需要在AndroidManifest.xml中配置一下LitePalApplication,所有的数据库操作就都不用再传Context了,如下所示:
<manifest>  
    <application  
        android:name="org.litepal.LitePalApplication"  
        ...  
    >  
    ...  
    </application>  
</manifest> 
创建数据库

配置映射:

    package com.example.databasetest.model;  
   
    public class News {  
    //定义字段,可以进行对象关系映射的数据类型一共有8种,int、short、long、float、double、boolean、String和   Date。
    //只要是声明成这8种数据类型的字段都会被自动映射到数据库表中,并不需要进行任何额外的配置。
    //自动生成get,set方法
    }  

完成映射:

<?xml version="1.0" encoding="utf-8"?>  
<litepal>  
    <dbname value="demo" ></dbname>  
  
    <version value="1" ></version>  
  
    <list>  
        <mapping class="com.example.databasetest.model.News"></mapping>//添加到关系类列表  
    </list>  
</litepal> 

成功创建:
在活动中运行:LitePal.getDatabase();方法则成功完成创建。

升级数据库

不需要去编写任何与升级相关的逻辑,也不需要关心程序是从哪个版本升级过来的,唯一要做的就是确定好最新的Model结构是什么样的,然后将litepal.xml中的版本号加1,所有的升级逻辑就都会自动完成了。LitePal确实将数据库表的升级操作变得极度简单,使很多程序员可以从维护数据库表升级的困扰中解脱出来。

Create/insert & Update/update & Delete/delete:增删改

进行CRUD操作时就不行了,LitePal要求所有的实体类都要继承自DataSupport这个类
【具体操作参见:】http://blog.csdn.net/guolin_blog/article/details/39345833

添加:每new出一个对应类的对象,则为添加一行数据,save();方法提交,save()返回为存储的结果。调用save();后则这个对象为已存储,此时再调用isSave():则为TRUE

修改:
1.先调用set方法设置新属性,然后使用 该对象.update(long id) //快捷修改,会默认传入类对象并省略values
2.如果是需要设置条件的复杂修改,则需要与ContentValues values联用,使用DataSupport.update();或DataSupport.updateAll();

删除:与修改类似,不过是deleteAll();与delete();方法。

Retrieve/select:查询

简单查询:

News news = DataSupport.find(News.class, 1); //查id为1的记录
News firstNews = DataSupport.findFirst(News.class); //查第一条
News lastNews = DataSupport.findLast(News.class);
List<News> newsList = DataSupport.findAll(News.class, 1, 3, 5, 7);  //后边传入任意id,无则为全部

连缀查询:原本query的多个参数被封装成各自的方法,在DataSupport.find(.class)前连缀使用

激进查询:

News news = DataSupport.find(News.class, 1, true);  //这会将和news表关联的所有表中的数据也一起查出来

该查询效率不高,最佳写法是在Model类中添加了一个get()方法,而这个方法的内部就是使用了一句连缀查询,查出了当前类对象关联的所有类对象。

此外,也可使用原生语句SQL执行SQL语言完成查询;
    Cursor cursor = DataSupport.findBySQL("select * from news where commentcount>?", "0");  

——————————————————————————————————————————
【github新版本】https://github.com/LitePalFramework/LitePal
【配置问题】http://blog.csdn.net/guolin_blog/article/details/38556989
【LitePal实现表内连接】 http://blog.csdn.net/guolin_blog/article/details/39207945
【LitePal实现查询】 http://blog.csdn.net/guolin_blog/article/details/40153833

——————————————————————————————————————————
【样本方法实例:】http://blog.csdn.net/itluochen/article/details/52605392
【数据库CRUD操作参数详解】http://blog.csdn.net/nomisshe/article/details/17797631

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容