精华内容
参与话题
问答
  • android数据库

    2014-03-17 12:04:36
    Android数据库 2 1 关系型数据库SQLIte 2 2 扩展类 5 2.1 扩展SQLiteOpenHelper 5 2.2 Cursor类 6 2.3 其他使用类 6 2.4 数据类型 7 3 数据库操作 7 3.1 创建和打开数据库 7 3.2 创建 7 3.3 ...

     

    目录
    Android数据库 2
    1 关系型数据库SQLIte 2
    2 扩展类 5
    2.1 扩展SQLiteOpenHelper 5
    2.2 Cursor类 6
    2.3 其他使用类 6
    2.4 数据类型 7
    3 数据库操作 7
    3.1 创建和打开数据库 7
    3.2 创建 7
    3.3 销毁 7
    3.4 SQL使用 8
    3.4.1 新增 8
    3.4.2 删除 9
    3.4.3 查询 9
    3.4.4 修改—更新 9
    3.5 数据库操作的常见异常。 9
    4 数据库打包回滚 9
    5 官网地址 11
    5.1 Sqlite不支持的sql特性 11
    5.2 第一次开机流程 12
    5.3 升级流程 12
    5.4 方案2 13

    Android数据库

    1 关系型数据库SQLIte
      每个应用程序都要使用数据,Android应用程序也不例外,Android使用开源的、与操作系统无关的SQL数据库—
    SQLite。SQLite第一个Alpha版本诞生于2000年5月,它是一款轻量级数据库,它的设计目标是嵌入式的,占用
    资源非常的低,只需要几百K的内存就够了。SQLite已经被多种软件和产品使用,Mozilla FireFox就是使用
    SQLite来存储配置数据的,Android和iPhone都是使用SQLite来存储数据的。
    SQLite体系结构图如下:     
      编译器包括Tokenizer(词法分析器)、 Parser(语法分析器)、Code Generator(代码产生器)。他们协
    同处理文本形式的结构化查询语句。
      后端由B-tree,Pager,OS Interface组成。B-tree的职责是负责排序,维护多个数据库页之间错综复杂
    的关系,将页面组织成树状结构,页面就是树的叶子。Pager负责传输,根据B-tree的请求从磁盘读取页面或者
    写入页面。
      公共服务中有各种实用的功能比如:内存分配、字符串比较,Unicode转换等。
     SQLite数据库是D.Richard Hipp用C语言编写的开源嵌入式数据库,支持的数据库大小为2TB。它具有如下特征

    1、轻量级
    SQLite和C\S模式的数据库软件不同,它是进程内的数据库引擎,因此不存在数据库的客户端和服务器。使用
    SQLite一般只需要带上它的一个动态库,就可以享受它的全部功能。而且那个动态库的尺寸也相当小。
    2、独立性
    SQLite数据库的核心引擎本身不依赖第三方软件,使用它也不需要“安装”,所以在使用的时候能够省去不少麻
    烦。
    3、隔离性
    SQLite数据库中的所有信息(比如表、视图、触发器)都包含在一个文件内,方便管理和维护。
    4、跨平台
    SQLite数据库支持大部分操作系统,除了我们在电脑上使用的操作系统之外,很多手机操作系统同样可以运行,
    比如Android、Windows Mobile、Symbian、Palm等。
    5、多语言接口
    SQLite数据库支持很多语言编程接口,比如C\C++、Java、Python、dotNet、Ruby、Perl等,得到更多开发者
    的喜爱。
    6、安全性
    SQLite数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一
    数据库读取数据,但只有一个可以写入数据。在某个进程或线程向数据库执行写操作之前,必须获得独占锁定。
    在发出独占锁定后,其他的读或写操作将不会再发生。
    SQLite官方网站(http://www.sqlite.org),了解更多内容请前往。导出查看数据库文件
      在android中,为某个应用程序创建的数据库,只有它可以访问,其它应用程序是不能访问的,数据库位于
    Android设备/data/data/package_name/databases文件夹中。
      想要将数据库文件导出可以使用eclipse,如图所示:
     
      查看数据库,使用SQlite Database Browser,如图所示:
     
     
    2 扩展类
    2.1 扩展SQLiteOpenHelper
      Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须自己创建数据库,然后创建表、索引,填充数据。Android 提供了 SQLiteOpenHelper 帮助你创建一个数据库,你只要继承 SQLiteOpenHelper 类,就可以轻松的创建数据库。SQLiteOpenHelper 类根据开发应用程序的需要,封装了创建和更新数据库使用的逻辑。SQLiteOpenHelper 的子类,至少需要实现三个方法:
    构造函数,调用父类 SQLiteOpenHelper 的构造函数
      onCreate()方法;// TODO 创建数据库后,对数据库的操作
      onUpgrage()方法。// TODO 更改数据库版本的操作
      当你完成了对数据库的操作(例如你的 Activity 已经关闭),需要调用 SQLiteDatabase 的 Close() 方法来释放掉数据库连接。
      操作数据库的最佳实践是创建一个辅助类,例如联系人模块
      class ContactsDatabaseHelper extends SQLiteOpenHelper
    2.2 Cursor类
    Android使用Cursor类返回一个需要的值,Cursor作为一个指针从数据库查询返回结果集,使用Cursor允许Android更有效地管理它们需要的行和列,你使用ContentValues对象存储键/值对,它的put()方法允许你插入不同数据类型的键值。
    2.3 其他使用类
    SQLiteDatabase:
    是关于数据库操作,封装了管理数据库的各种方法,包括insert 、delete、 update 、query 、执行SQL命令等操作

    DatabaseHelper :
     帮助类用它的getWritableDatabase()和getReadableDatabase()来获得数据库(SQLiteDatabase 对象)。

    ContentProvider :
    负责组织应用程序的数据;向其他应用程序提供数据;

    ContentResolver :
    则负责获取ContentProvider提供的数据;修改/添加/删除更新数据等;
    UriMatcher:用于匹配Uri,使用步骤 1.首先把你需要匹配Uri路径全部给注册上。 2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行            匹配,如果匹配就返回匹配码,匹配码是调用 addURI()方法传入的第三个参数,假设匹配content://com.android.calendar/calendars路径,返回的匹配码            为1。

    ContentValues  ContentValues: 是一种存储的机制,能存储基本类型的数据

    DatabaseUtils  数据库操作帮助类

    2.4 数据类型
      SQLite 和其他数据库最大的不同就是对数据类型的支持,创建一个表时,可以在 CREATE TABLE 语句中指定某列的数据类型,但是你可以把任何数据类型放入任何列中。当某个值插入数据库时,SQLite 将检查它的类型。如果该类型与关联的列不匹配,则 SQLite 会尝试将该值转换成该列的类型。如果不能转换,则该值将作为其本身具有的类型存储。比如可以把一个字符串(String)放入 INTEGER 列。SQLite 称这为“弱类型”(manifest typing.)。
     
    3 数据库操作
    3.1 创建和打开数据库
      在Android中创建和打开一个数据库都可以使用openOrCreateDatabase方法来实现,因为它会自动去检测是否存在这个数据库,如果存在则打开,如果不存在则创建一个数据库:创建成功则返回一个SQLiteDatebase对象,否则抛出异常FileNotFoundException。
    3.2 创建
    可以看到这个方法:
    openOrCreateDatabase
     
    作为一个基础类,它提供了接口来创建或者打开数据库。
    而这里的path,毫无疑问应该是db的路径。如果路径指定的数据库不存在,那么就应该创建一个数据库。

    3.3 销毁
    deleteDatabase
     
    3.4 SQL使用
    数据库的使用,一个是增删查改的SQL。
    另外一个,则是表结构变更,视图、触发器、关联键以及索引等得创建、销毁和修改。

    我们关注常用的增删查改,可以看到它提供了如下接口,android的数据库的操作不能直接使用sql语句只能调用接口:
    3.4.1 新增
     

    3.4.2 删除
     
    3.4.3 查询
     
    3.4.4 修改—更新
     
     
    3.5 数据库操作的常见异常。
         我们常常把内存空间不足异常忘记,导致在插入数据到数据库时报错, 删除和更新一般不会出错。
    所以在执行数据库的时候注意系统资源不足情况的异常,如果需要捕获相应错误。
    4 数据库打包回滚

    在Android应用程序开发中,经常会遇到需要进行数据库操作的时候,有关数据库事务处理对Android应用程序的稳定性和效率提升非常重要。
    首先Android数据库操作,特别是进行写操作的时候非常慢,将所有操作打包成一个事务能大大提高处理速度。
    其次是保证数据的一致性,让一个事务中的所有操作都成功执行,或者失败,或者所有操作回滚。
    如果您喜欢使用其他平台(如PHP + MySQL),代码通常在一个功能强大的服务器上运行,一般不会被意外中止,但在Android平台上,您将会因为一次又一次的意外中止感到惊呀。Android系统会杀死apps/threads/activities等中断数据库的使用,电池电量会耗尽或被移除等。所以,使用数据库事物至关重要。
    实现Android数据库事务非常简单,只需要使用SQLiteDatabase类的三个方法即可。
    ◆beginTransaction();
    ◆setTransactionSuccessful();
    ◆endTransaction();
    当调用endTransaction()时,所有从beginTransaction()开始的操作都会被提交。
    一个简单的数据库事务操作如下所示:
    [html] view plaincopyprint?
    1. mDatabase.beginTransaction();   
    2. try{   
    3.   //在这里执行多个数据库操作   
    4.   //执行过程中可能会抛出异常   
    5.   mDatabase.setTransactionSuccessful();   
    6.   //在setTransactionSuccessful和endTransaction之间不进行任何数据库操作   
    7.   }catch(Exception e){   
    8.     //当数据库操作出现错误时,需要捕获异常,结束事务   
    9.     mDatabase.endTransaction();   
    10.     throw e;   
    11.   }   
    12.   //当所有操作执行完成后结束一个事务   
    13.   mDatabase.endTransaction(); 

    以上是关于Android数据库事务操作的一个简单示例,在我们平时的Android开发中,将所有操作打包成一个事务为一个事务并保证事务的一致性,可以大幅提高Android数据库操作的效率。
    5 官网地址
    http://www.sqlite.org/docs.html
    5.1 Sqlite不支持的sql特性
     
     
    很重要的一点是,sqlite支持alter语句新增列和修改表名,但是不支持删除列及修改列名。
    这意味着,如果你新增一列,参数有误或者设计不需要了,你无法通过alter语句来达到目的。
    当然,可以通过其他方法达到目标:
    ① 创建一个同数据结构的临时表
    ② 将目标表数据导入临时表
    ③ 删除基表,再从临时表中导出数据到新建表单中
    操作看起来不复杂,但是如果涉及大数据量操作以及键值关联的时候,就会比较麻烦。
    如果我们按照android给我们设计的思路,我们要考虑两个流程:
    一个是手机第一次开机或者说恢复出厂设置后的流程。
    一个是手机OTA升级的流程,或者是替换APK重启进程的流程。
    5.2 第一次开机流程
    要在onCreate方法中,
     
    在这个地方,要把变更的表结构加进去。

    5.3 升级流程
    对于TelephonyProvider对应的DB来说,我们要修改DATABASE_VERSION的值。我们只要让它的值比当前的值大就可以。
     
    比如可以按照惯例,设置成:
    private static final int DATABASE_VERSION = 9 << 16;

    这样TeleonyProvider依附与Phone进程被实例化后,再被调用查询语句被查询时,就会修改数据库的表结构。由于开启了事物,在事物结束后会重建Reference,因此,查询结果也能保证是正确的。
    5.4 方案2
    方案2,就是自己去写alter table 的语句,完了之后调用db.close()来清除Reference,这样也能保证后面Cursor查询的正确性。
     


    编译器包括Tokenizer(词法分析器)、 Parser(语法分析器)、Code Generator(代码产生器)。他们协
    同处理文本形式的结构化查询语句。
      后端由B-tree,Pager,OS Interface组成。B-tree的职责是负责排序,维护多个数据库页之间错综复杂
    的关系,将页面组织成树状结构,页面就是树的叶子。Pager负责传输,根据B-tree的请求从磁盘读取页面或者
    写入页面。
      公共服务中有各种实用的功能比如:内存分配、字符串比较,Unicode转换等。
     SQLite数据库是D.Richard Hipp用C语言编写的开源嵌入式数据库,支持的数据库大小为2TB。它具有如下特征

    1、轻量级
    SQLite和C\S模式的数据库软件不同,它是进程内的数据库引擎,因此不存在数据库的客户端和服务器。使用
    SQLite一般只需要带上它的一个动态库,就可以享受它的全部功能。而且那个动态库的尺寸也相当小。
    2、独立性
    SQLite数据库的核心引擎本身不依赖第三方软件,使用它也不需要“安装”,所以在使用的时候能够省去不少麻
    烦。
    3、隔离性
    SQLite数据库中的所有信息(比如表、视图、触发器)都包含在一个文件内,方便管理和维护。
    4、跨平台
    SQLite数据库支持大部分操作系统,除了我们在电脑上使用的操作系统之外,很多手机操作系统同样可以运行,
    比如Android、Windows Mobile、Symbian、Palm等。
    5、多语言接口
    SQLite数据库支持很多语言编程接口,比如C\C++、Java、Python、dotNet、Ruby、Perl等,得到更多开发者
    的喜爱。
    6、安全性
    SQLite数据库通过数据库级上的独占性和共享锁来实现独立事务处理。这意味着多个进程可以在同一时间从同一
    数据库读取数据,但只有一个可以写入数据。在某个进程或线程向数据库执行写操作之前,必须获得独占锁定。
    在发出独占锁定后,其他的读或写操作将不会再发生。
     
    SQLite官方网站(http://www.sqlite.org),了解更多内容请前往。导出查看数据库文件
      在android中,为某个应用程序创建的数据库,只有它可以访问,其它应用程序是不能访问的,数据库位于Android设备/data/data/package_name/databases文件夹中。
      想要将数据库文件导出可以使用eclipse,如图所示:

    1 数据库生命周期
      共享数据库 : static 块优先于Application,即共享数据库中先创建数据库在启动应用总入库Application。
      一般数据库 : 在实例化的时候被创建
      综上两点我们在做优化的时候应该考虑这两种情况,进行首次启动时间优化的考量。

    2 数据库操作中使用的的关键类
      DatabaseHelper
      SQLiteDatabase
      ContentValues
      Cursor
      ContentResolver
      UriMatcher
      ContentProvider
      DatabaseUtils


    3 应用的共享数据库一般应该加上权限防止被攻击。
      例如时钟应用当中:
      在配置文件中自定义读写权限,
      <permission android:name="com.huawei.alarm.provider.readPermission" />
      <permission android:name="com.huawei.alarm.provider.writePermission" />
      然后再Provider中声明它,
      <provider
       android:name=".AlarmProvider"
       android:authorities="com.android.deskclock"
       android:exported="true"
       android:readPermission="com.huawei.alarm.provider.readPermission"
       android:writePermission="com.huawei.alarm.provider.writePermission" />

    4 数据库操作的常见异常。
      我们常常把内存空间不足异常忘记,导致在插入数据到数据库时报错, 删除和更新一般不会出错。


    5 SQL语句
      直接使用SQL语句进行数据库的操作。
     

    搜一下:Checking for alarms...
    之后sending alarm 的log,这个里面有能看出是不是向应用发送intent


    闹钟在处理开机广播的过程中过时问题,应该怎么处理掉比较科学。


    #DTS2014021700292 w81003501 20140216 begin?
    ro.config.hw_audio_plus=true?
    #DTS2014021700292 w81003501 20140216 end

     


    #DTS2014021700292 w81003501 20140216 begin?
    ro.config.hw_audio_plus=true?
    #DTS2014021700292 w81003501 20140216 end

     


     

    展开全文
  • 上一篇文章中我们学习了一些Android数据库相关的基础知识,和几个颇为有用的SQLite命令,都是直接在命令行操作的。但是我们都知道,数据库是要和程序结合在一起使用的,单独对一个数据库去进行増删改查操作并没有...

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/38556989


    上一篇文章中我们学习了一些Android数据库相关的基础知识,和几个颇为有用的SQLite命令,都是直接在命令行操作的。但是我们都知道,数据库是要和程序结合在一起使用的,单独对一个数据库去进行増删改查操作并没有什么意义,因此今天我们就来学习一下如何在Android程序当中去操作SQLite数据库,还没看过前一篇文章的朋友可以先去参考 Android数据库高手秘籍(一)——SQLite命令 。


    操作数据库的第一步当然是创建表了,传统创建表的方法相信大多数人都知道,那么今天我除了会展示传统的建表方法之外,还会讲解LitePal这个框架的基本用法,并使用它来完成同样的建表操作,让大家体会到使用框架来操作数据库的魅力。


    那么先来简单介绍一下吧,LitePal是一款开源的Android数据库框架,它采用了对象关系映射(ORM)的模式,并将我们平时开发时最常用到的一些数据库功能进行了封装,使得不用编写一行SQL语句就可以完成各种建表、増删改查的操作。并且LitePal很“轻”,jar包只有100k不到,而且近乎零配置,这一点和Hibernate这类的框架有很大区别。目前LitePal的源码已经托管到了GitHub上,地址是 https://github.com/LitePalFramework/LitePal 。


    OK,简单介绍完了LitePal,我们还是先来看一下,在传统的Android开发中,需要怎么去创建表。


    传统的建表方式


    其实为了方便我们对数据库表进行管理,Android本身就提供了一个帮助类:SQLiteOpenHelper。这个类集创建和升级数据库于一身,并且自动管理了数据库版本,算是一个非常好用的工具。


    那我们现在就来试试SQLiteOpenHelper的用法吧。首先你要知道SQLiteOpenHelper是一个抽象类,这意味着如果我们想要使用它的话,就需要创建一个自己的帮助类去继承它。SQLiteOpenHelper中有两个抽象方法,分别是onCreate()和onUpgrade(),我们必须在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑。本篇文章只需要把注意力放在创建数据库这里就行了,升级数据库我们会在下一篇文章中去讨论。


    新建一个MySQLiteHelper类并让它继承SQLiteOpenHelper,这样一个最基本的数据库帮助类的代码如下所示:

    public class MySQLiteHelper extends SQLiteOpenHelper {
    
    	public MySQLiteHelper(Context context, String name, CursorFactory factory,
    			int version) {
    		super(context, name, factory, version);
    	}
    
    	@Override
    	public void onCreate(SQLiteDatabase db) {
    	}
    
    	@Override
    	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    	}
    
    }
    其中,当数据库创建的时候会调用onCreate()方法,在这里去执行建表操作就可以了。比如说我们想新建一张news表,其中有title,content,publishdate,commentcount这几列,分别代表着新闻标题、新闻内容、发布时间和评论数,那么代码就可以这样写:
    public class MySQLiteHelper extends SQLiteOpenHelper {
    	
    	public static final String CREATE_NEWS = "create table news ("
    			+ "id integer primary key autoincrement, "
    			+ "title text, "
    			+ "content text, "
    			+ "publishdate integer,"
    			+ "commentcount integer)";
    
    	public MySQLiteHelper(Context context, String name, CursorFactory factory,
    			int version) {
    		super(context, name, factory, version);
    	}
    
    	@Override
    	public void onCreate(SQLiteDatabase db) {
    		db.execSQL(CREATE_NEWS);
    	}
        ...
    }

    可以看到,我们把建表语句定义成了一个常量,然后在onCreate()方法中去执行了这条建表语句,news表也就创建成功了。这条建表语句虽然简单,但是里面还是包含了一些小的细节,我来解释一下。首先,根据数据库的范式要求,任何一张表都应该是有主键的,所以这里我们添加了一个自增长的id列,并把它设为主键。然后title列和content列都是字符串类型的,commentcount列是整型的,这都很好理解,但是publishdate列该怎么设计呢?由于SQLite中并不支持存储日期这种数据类型,因此我们需要将日期先转换成UTC时间(自1970年1月1号零点)的毫秒数,然后再存储到数据库中,因此publishdate列也应该是整型的。


    现在,我们只需要获取到SQLiteDatabase的实例,数据库表就会自动创建了,如下所示:

    SQLiteOpenHelper dbHelper = new MySQLiteHelper(this, "demo.db", null, 1);
    SQLiteDatabase db = dbHelper.getWritableDatabase();

    感觉很简单很方便是吗?那你就太容易满足了,下面我们就来学习一下LitePal的基本用法,看一看使用这个框架是如何实现同样的功能的。


    LitePal的基本用法


    虽说LitePal宣称是近乎零配置,但也只是“近乎”而已,它还是需要进行一些简单配置才可以使用的,那么我们第一步就先快速学习一下LitePal的配置方法。


    快速配置


    1. 引入Jar包或源码

    首先我们需要将LitePal的jar包引入到项目当中,可以点击这里查看LitePal的最新版本,选择你需要的下载即可。下载好了jar包之后,把它复制到项目的libs目录中就算是引入成功了,如下图所示:




    如果你不想用jar包的话,也可以把LitePal的源码下载下来,然后作为一个library库导入到Eclipse当中,再让我们的项目去引用这个library库就可以了。


    2. 配置litepal.xml

    接着在项目的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>用于设定所有的映射模型,我们稍后就会用到。


    3. 配置LitePalApplication

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

    <manifest>
        <application
            android:name="org.litepal.LitePalApplication"
            ...
        >
        ...
        </application>
    </manifest>
    当然,有些程序可能会有自己的Application,并在这里配置过了。比如说有一个MyApplication,如下所示:
    <manifest>
        <application
            android:name="com.example.MyApplication"
            ...
        >
        ...
        </application>
    </manifest>
    没有关系,这时只需要修改一下MyApplication的继承结构,让它不要直接继承Application类,而是继承LitePalApplication类,就可以使用一切都能正常工作了,代码如下所示:
    public class MyApplication extends LitePalApplication {
        ...
    }

    但是,有些程序可能会遇到一些更加极端的情况,比如说MyApplication需要继承另外一个AnotherApplication,并且这个AnotherApplication还是在jar包当中的,不能修改它的代码。这种情况应该算是比较少见了,但是如果你遇到了的话也不用急,仍然是有解释方案的。你可以把LitePal的源码下载下来,然后把src目录下的所有代码直接拷贝到你项目的src目录下面,接着打开LitePalApplication类,将它的继承结构改成继承自AnotherApplication,再让MyApplication继承自LitePalApplication,这样所有的Application就都可以在一起正常工作了。


    仅仅三步,我们就将所有的配置工作全部完成了,并且这是一件一本万利的事情,自此以后,你就可以开心地体验LitePal提供的各种便利了,就让我们从建表开始吧。


    开始建表


    前面在介绍的时候已经说了,LitePal采取的是对象关系映射(ORM)的模式,那么什么是对象关系映射呢?简单点说,我们使用的编程语言是面向对象语言,而我们使用的数据库则是关系型数据库,那么将面向对象的语言和面向关系的数据库之间建立一种映射关系,这就是对象关系映射了。


    但是我们为什么要使用对象关系映射模式呢?这主要是因为大多数的程序员都很擅长面向对象编程,但其中只有少部分的人才比较精通关系型数据库。而且数据库的SQL语言晦涩难懂,就算你很精通它,恐怕也不喜欢经常在代码中去写它吧?而对象关系映射模式则很好地解决了这个问题,它允许我们使用面向对象的方式来操作数据库,从而可以从晦涩难懂的SQL语言中解脱出来。


    那么接下来我们就看一看LitePal中是如何建表的吧。根据对象关系映射模式的理念,每一张表都应该对应一个模型(Model),也就是说,如果我们想要建一张news表,就应该有一个对应的News模型类。新建一个News类,如下所示:

    package com.example.databasetest.model;
    
    public class News {
    }
    然后,表中的每一列其实就是对应了模型类中的一个字段,比如news表中有id、title、content、publishdate、commentcount这几个列,那么在News类中就也应该有这几个字段,代码如下所示:
    public class News {
    	
    	private int id;
    	
    	private String title;
    	
    	private String content;
    	
    	private Date publishDate;
    	
    	private int commentCount;
    	
    	// 自动生成get、set方法
    	...
    }

    其中id这个字段可写可不写,因为即使不写这个字段,LitePal也会在表中自动生成一个id列,毕竟每张表都一定要有主键的嘛。


    这里我要特别说明一下,LitePal的映射规则是非常轻量级的,不像一些其它的数据库框架,需要为每个模型类单独配置一个映射关系的XML,LitePal的所有映射都是自动完成的。根据LitePal的数据类型支持,可以进行对象关系映射的数据类型一共有8种,int、short、long、float、double、boolean、String和Date。只要是声明成这8种数据类型的字段都会被自动映射到数据库表中,并不需要进行任何额外的配置。


    现在模型类已经建好了,我们还差最后一步,就是将它配置到映射列表当中。编辑assets目录下的litepal.xml文件,在<list>标签中加入News模型类的声明:

    <?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>

    注意这里一定要填入News类的完整类名。


    OK,这样所有的工作就都已经完成了,现在只要你对数据库有任何的操作,news表就会被自动创建出来。比如说LitePal提供了一个便捷的方法来获取到SQLiteDatabase的实例,如下所示:

    SQLiteDatabase db = Connector.getDatabase();

    调用一下上述代码,news表就应该已经创建成功了。我们使用在上一篇文章中学到的SQLite命令来查看一下,打开demo.db数据库,输入.table命令,结果如下图所示:




    可以看到,news表已经存在了。另外两张android_metadata和table_schema表是自动生成的,我们不用理。接下来我们还可以再查询一下news表的建表语句,如下图所示:




    这就是LitePal根据News类中的字段自动帮我们生成的建表语句,由此也说明,建表操作已经成功完成了。


    好了,到目前为止你已经算是对LitePal的用法有点入门了,那么本篇文章的内容就到这里,下篇文章当中我们将学习使用LitePal进行升级表的操作。感兴趣的朋友请继续阅读 Android数据库高手秘籍(三)——使用LitePal升级表 。


    关注我的技术公众号,每天都有优质技术文章推送。关注我的娱乐公众号,工作、学习累了的时候放松一下自己。

    微信扫一扫下方二维码即可关注:

            

    展开全文
  • Android数据库加密

    千次阅读 2019-09-11 18:28:39
    Android数据库加密 一、简介 SQLite是一个轻量的、跨平台的、开源的数据库引擎,它的读写效率、资源消耗总量、延迟时间和整体简单性上具有的优越性,使其成为移动平台数据库的最佳解决方案(如Android、iOS)。Android...

    Android数据库加密

    一、简介

    SQLite是一个轻量的、跨平台的、开源的数据库引擎,它的读写效率、资源消耗总量、延迟时间和整体简单性上具有的优越性,使其成为移动平台数据库的最佳解决方案(如Android、iOS)。Android系统内置了SQLite数据库,并且提供了一整套的API用于对数据库进行增删改查操作,具体就不详细说明了。

    然而,Android平台自带的SQLite有一个致命的缺陷:不支持加密。这就导致存储在SQLite中的数据可以被任何人用任何文本编辑器查看到。如果是普通的数据还好,但是当涉及到一些账号密码,或者聊天内容的时候,我们的应用就会面临严重的安全漏洞隐患。

    目前最好且唯一的方案就是SqlCipher对sqlite3整体加密,微信也用的它。开源,且支持很多平台。

    二、数据库加密原理

    目前主流的数据库都采用了各种安全措施,主要包括用户认证、访问控制、数据加密存储和数据库操作审计等措施。

    用户认证:用户或者程序向数据库提供自己的有效身份证明,数据库鉴别用户的身份是否合法,只有合法的用户才能存取数据库中的数据。用户认证是所有安全机制的前提,只有通过认证才能进行授权访问和审计。

    访问控制:数据库管理系统为不同的用户分配不同的权限,保证用户只能进行授权的访问。目前,一些大型数据库(如Oracle等)都采用了基于角色的访问控制机制,即为用户授予不同的角色,如db—owner,security administrator 等,不同的角色允许对数据库执行不同的操作。

    数据库加密:用户认证以及访问控制对访问数据库进行了控制,但攻击者可能会利用操作系统或数据库漏洞,或物理接触计算机,而直接接触数据库系统文件,从而可能绕过身份认证和存取控制而直接窃取或篡改数据库内容。对数据库中的数据进行加密是防范这类威胁的有效手段。

    数据库操作审计:监视和记录用户对数据库所做的各种操作的安全机制,它记录并存储用户的操作,用于事后分析,以检查导致数据库现状的原因以及提供追踪攻击者的线索。数据库的备份与恢复:当数据库发生不可恢复的故障时,可以将数据库恢复到先前的某个一致性的状态。

    三、解决方案*

    1.将数据加密后再写入数据库:

    我们可以对数据的数据库名,表名,列名就行md5,对存储的数据进行加密,例如进行aes加密(Android数据加密之Aes加密),查询的时候再对数据进行解密,这种方式不能说不好,但是使用起来可以想象一下其带来的麻烦程度。

    1)优点:

    a. 实现数据加密快速,只需添加两个方法
    一是:对明文数据进行加密返回密文数据
    二是:对密文数据进行解密返回明文数据

    b. 程序无需进行太大变动,仅在对数据进行添加,修改,删除,查询时。针对指定的表字段进行修改进行加密,解密的字段即可。

    2)不足:
    a. 由于对数据进行了加密。所以为了看到明文,必须密文进行解密。因此会增加处理器的消耗。因终端手机的处理能力有限,可能会出现处理数据缓慢的现象发生。

    b. 仅仅对数据进行了加密,还是可以看到数据表的sql语句,可能猜测到表的作用。另外,如果没有对一个表中的所有字段加密,则可以看没有加密的明文数据。

    这种方式使用简单,在入库/出库只需要将字段做对应的加解密操作即可,一定程度上解决了将数据赤裸裸暴露的问题,这种只是靠存取数据库时通过自己的一些算法加密解密,一定程度上会影响性能。

    这种方式并不是彻底的加密,因为数据库的表结构等信息还是能被查看到。另外写入数据库的内容加密后,搜索也是个问题。

    2. 对数据库文件加密

    将整个数据库整个文件加密,这种方式基本上能解决数据库的信息安全问题。目前已有的SQLite加密基本都是通过这种方式实现的。

    目前流行的是一款开源的SQLite加密工具 SQLCipher ,微信也在使用。 SQLCipher是完全开源的,其代码托管在github上。SQLCipher使用256-bit AES加密,由于其基于免费版的SQLite,主要的加密接口和SQLite是相同的,也增加了一些自己的接口。它有一个缺点就是使用该库之后会导致Apk会变大6M左右。下面就是具体介绍SQLCipher的使用方法。

    在这里插入图片描述

    SQLCipher使用

    SQLCipher是完全开源的软件,提供256-bit AES加密。

    SQLCipher是一个在SQLite基础之上进行扩展的开源数据库,SQLCipher具有占地面积小、性能因此它非常适合嵌入式应用的数据库保护,非常适合于移动开发。

    整体来说sqlcipher还是比较好用的,封装好了跟正常操作数据库的方式一样,只是在getWritableDatabase()时要多传个password参数。

    github地址

    导入SQLCipher加密库

    implementation 'net.zetetic:android-database-sqlcipher:4.2.0'
    

    替换原生的包

    android.database.Cursor 为 net.sqlcipher.Cursor
    android.database.sqlite.SQLiteDatabase 为 net.sqlcipher.database.SQLiteDatabase
    android.database.SQLiteOpenHelper 为 net.sqlcipher.database.SQLiteOpenHelper
    

    加载SQLCipher所需要的SO库

    SQLiteDatabase.loadLibs(this); 
    

    获取读写对象时候附带密码

    需要传入一个password,这个password就是用于加密的秘钥

    SQLiteOpenHelper.getWritableDatabase("密码"):
    SQLiteOpenHelper.getReadableDatabase("密码")
    

    DBCipherHelper

    /**
     * Created by : xiaoyehai
     * Create date : 2019/9/12 6:05
     * description :
     * <p>
     * SQLiteOpenHelper要引用sqlcipher包下的
     */
    public class DBCipherHelper extends SQLiteOpenHelper {
    
        private static final String DB_NAME = "test_cipher_db";//数据库名字
        public static final String DB_PWD = "whoislcj";//数据库密码
        public static String TABLE_NAME = "person";// 表名
        public static String FIELD_ID = "_id";// 列名
        public static String FIELD_NAME = "name";// 列名
        private static final int DB_VERSION = 1;   // 数据库版本
    
    
        public DBCipherHelper(Context context) {
            super(context, DB_NAME, null, DB_VERSION);
    
            //不可忽略的 进行so库加载
            SQLiteDatabase.loadLibs(context);
        }
    
    
        /**
         * 创建数据库
         *
         * @param db
         */
        @Override
        public void onCreate(SQLiteDatabase db) {
            //创建表
            String sql = "CREATE TABLE " + TABLE_NAME + "(" + FIELD_ID + " integer primary key autoincrement , " + FIELD_NAME + " text not null);";
            db.execSQL(sql);
    
        }
    
        /**
         * 数据库升级
         *
         * @param db
         * @param oldVersion
         * @param newVersion
         */
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    
        }
    }
    
    

    创建一个DBCipherManager数据库管理

    在这里插入图片描述

    /**
     * Created by : xiaoyehai
     * Create date : 2019/9/12 6:10
     * description :
     */
    public class DBCipherManager {
    
        private static final String TAG = "DBCipherManager";
        // 静态引用
        private volatile static DBCipherManager mInstance;
    
        // DatabaseHelper
        private DBCipherHelper dbHelper;
    
        private DBCipherManager(Context context) {
            dbHelper = new DBCipherHelper(context);
        }
    
        /**
         * 获取单例引用
         *
         * @return
         */
        public static DBCipherManager getInstance(Context context) {
            DBCipherManager inst = mInstance;
            if (inst == null) {
                synchronized (DBCipherManager.class) {
                    inst = mInstance;
                    if (inst == null) {
                        inst = new DBCipherManager(context);
                        mInstance = inst;
                    }
                }
            }
            return inst;
        }
    
        /**
         * 插入数据:未开启事务
         */
        public void insertData(List<PersonBean> datas) {
            //获取写数据库
            SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
            for (int i = 0; i < datas.size(); i++) {
                //生成要修改或者插入的键值
                ContentValues cv = new ContentValues();
                cv.put(DBCipherHelper.FIELD_NAME, datas.get(i).getName());
                // insert 操作
                db.insert(DBCipherHelper.TABLE_NAME, null, cv);
            }
    
            //关闭数据库
            db.close();
        }
    
        /**
         * 插入数据:开启事务批量插入
         */
        public void insertDataByTransaction(List<PersonBean> datas) {
            //获取写数据库
            SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
            db.beginTransaction();  //手动设置开始事务
            try {
                //批量处理操作
                for (int i = 0; i < datas.size(); i++) {
                    //生成要修改或者插入的键值
                    ContentValues cv = new ContentValues();
                    cv.put(DBCipherHelper.FIELD_NAME, datas.get(i).getName());
                    // insert 操作
                    db.insert(DBCipherHelper.TABLE_NAME, null, cv);
                    Log.e(TAG, "insertDatasByTransaction");
                }
                db.setTransactionSuccessful(); //设置事务处理成功,不设置会自动回滚不提交
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                db.endTransaction(); //处理完成
                //关闭数据库
                db.close();
            }
        }
    
        /**
         * 删除数据
         */
        public void deleteData(String name) {
            //生成条件语句
            StringBuffer whereBuffer = new StringBuffer();
            whereBuffer.append(DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
            //获取写数据库
            SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
            // delete 操作
            db.delete(DBCipherHelper.TABLE_NAME, whereBuffer.toString(), null);
            //关闭数据库
            db.close();
        }
    
        /**
         * 删除所有数据
         */
        public void deleteAllDatas() {
            SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
            String sql = "delete from " + DBCipherHelper.TABLE_NAME;
            db.execSQL(sql);
            db.close();
        }
    
        /**
         * 更新数据
         */
        public void updateData(String name, PersonBean personBean) {
            //生成条件语句
            StringBuffer whereBuffer = new StringBuffer();
            whereBuffer.append(DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
            //生成要修改或者插入的键值
            ContentValues cv = new ContentValues();
            cv.put(DBCipherHelper.FIELD_NAME, personBean.getName());
            //获取写数据库
            SQLiteDatabase db = dbHelper.getWritableDatabase(DBCipherHelper.DB_PWD);
            // update 操作
            db.update(DBCipherHelper.TABLE_NAME, cv, whereBuffer.toString(), null);
            //关闭数据库
            db.close();
        }
    
        /**
         * 指定条件查询数据
         */
        public List<PersonBean> queryDatas(String name) {
            List<PersonBean> list = new ArrayList<>();
            //生成条件语句
            StringBuffer whereBuffer = new StringBuffer();
            whereBuffer.append(DBCipherHelper.FIELD_NAME).append(" = ").append("'").append(name).append("'");
            //指定要查询的是哪几列数据
            String[] columns = {DBCipherHelper.FIELD_NAME};
            //获取可读数据库
            SQLiteDatabase db = dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
            //查询数据库
            Cursor cursor = null;
            try {
                cursor = db.query(DBCipherHelper.TABLE_NAME, columns, whereBuffer.toString(), null, null, null, null);
                while (cursor.moveToNext()) {
                    int count = cursor.getColumnCount();
                    String columName = cursor.getColumnName(0);//获取表结构列名
                    String tname = cursor.getString(0); //获取表结构列数据
                    Log.e(TAG, "count = " + count + " columName = " + columName + "  name =  " + tname);
                    PersonBean personBean = new PersonBean(tname);
                    list.add(personBean);
                }
                if (cursor != null) {
                    cursor.close();
                }
            } catch (SQLException e) {
                Log.e(TAG, "queryDatas" + e.toString());
            }
            //关闭数据库
            db.close();
            return list;
        }
    
        /**
         * 查询全部数据
         */
        public List<PersonBean> queryAllDatas() {
            List<PersonBean> list = new ArrayList<>();
            //指定要查询的是哪几列数据
            String[] columns = {DBCipherHelper.FIELD_NAME};
            //获取可读数据库
            SQLiteDatabase db = dbHelper.getReadableDatabase(DBCipherHelper.DB_PWD);
            //查询数据库
            Cursor cursor = null;
            try {
                cursor = db.query(DBCipherHelper.TABLE_NAME, columns, null, null, null, null, null);//获取数据游标
                while (cursor.moveToNext()) {
                    int count = cursor.getColumnCount();
                    String columeName = cursor.getColumnName(0);//获取表结构列名
                    String tname = cursor.getString(0); //获取表结构列数据
                    Log.e(TAG, "count = " + count + " columName = " + columeName + "  name =  " + tname);
                    PersonBean personBean = new PersonBean(tname);
                    list.add(personBean);
                }
                //关闭游标防止内存泄漏
                if (cursor != null) {
                    cursor.close();
                }
            } catch (SQLException e) {
                Log.e(TAG, "queryDatas" + e.toString());
            }
            //关闭数据库
            db.close();
            return list;
        }
    
    
    }
    
    

    注意:SQLiteDatabase.loadLibs(context);这个千万别忘记调用

    使用

      private void queryData() {
            List<PersonBean> list = mDbCipherManager.queryAllDatas(); //查询全部
            //List<PersonBean> list = mDbCipherManager.queryDatas("赵丽颖2"); //根据条件查询
            MyAdapter myAdapter = new MyAdapter(this, list, R.layout.item_list);
            mListView.setAdapter(myAdapter);
    
        }
    
        private void updateData() {
            PersonBean personBean = new PersonBean("赵丽颖更新", 100);
            mDbCipherManager.updateData("赵丽颖2",personBean);
        }
    
        private void deleteData() {
            //mDbCipherManager.deleteData("赵丽颖2"); //根据name删除
            mDbCipherManager.deleteAllDatas(); //删除所有
        }
    
        private void addData() {
            List<PersonBean> list = new ArrayList<>();
            for (int i = 1; i <= 10; i++) {
                PersonBean personBean = new PersonBean("赵丽颖" + i);
                list.add(personBean);
            }
    
            mDbCipherManager.insertData(list);
    
        }
    
    

    Android数据存储之SQLCipher数据库加密

    展开全文
  • Android 数据库对比

    千次阅读 2019-01-09 11:57:40
    Android 数据库对比 转载于:https://blog.csdn.net/u010134293/article/details/58590092 一、常见数据库介绍 GreenDao 是为Android设计的对象关系映射(ORM)工具。它提供了对象到关系型数据库SQLite的相应...

    Android 数据库对比

    转载于:https://blog.csdn.net/u010134293/article/details/58590092

    一、常见数据库介绍

    • GreenDao 是为Android设计的对象关系映射(ORM)工具。它提供了对象到关系型数据库SQLite的相应接口。为了在Android工程中使用greenDao,需要创建另一个“生成器”工程,它的任务是在你的工程域里生成具体的代码。因此相比与其它ORM框架具有出众性能。

    • LitePal 是对象关系映射(ORM)模型。它使开发者使用SQLite数据库变得非常容易。 你可以不用写一句SQL语句就可以完成大部分数据库操作,包括创建表,更新表,约束操作,聚合功能等等。

    • Afinal 是一个android的sqlite orm 和 ioc 框架。同时封装了android中的http框架,使其更加简单易用。FinalDB模块,android中的orm框架,一行代码就可以进行增删改查。支持一对多,多对一等查询。

    • ORMLite (Object Relational Mapping Lite)提供了一些轻量级持久化Java对象到SQL数据库,同时也避免了复杂性和更多的标准的ORM包的开销功能。它支持的SQL数据库使用JDBC的数量,还支持原生的Android操作系统数据库API调用sqlite。

    • SugarORM 是对象关系映射模式。不用写复杂的sql语句,而用简单的API即可完成创建和操纵数据;可以在原有的Bean上仅仅添加小的修改而复用Bean;简化而明了的数据库设计和创建过程,同时提供表的一对多的支持。

    • Realm 是用来替代sqlite的一种解决方案,它有一套自己的数据库存储引擎,比sqlite更轻量级,拥有更快的速度,并且具有很多现代数据库的特性,比如支持JSON,流式api,数据变更通知,自动数据同步,简单身份验证,访问控制,事件处理,最重要的是跨平台,目前已有Java,Objective C,Swift,React-Native,Xamarin这五种实现。

    • LiteOrm 是android上的一款数据库(ORM)框架库。速度快、体积小、性能高。开发者基本一行代码实现数据库的增删改查操作,以及实体关系的持久化和自动映射。设计原则:轻量、专注、性能优先、线程无关,专注数据及其关系存储和操作;无需工具辅助,不需要无参构造,不需要繁多注解,约定优于配置;使用极致简约,例如:db.save(u); db.query(U.class); db.deleteAll(U.class);。

    • DBFlow 综合了 ActiveAndroid, Schematic, Ollie,Sprinkles 等库的优点。同时不是基于反射,所以性能也是非常高,效率紧跟greenDAO其后。基于注解,使用apt技术,在编译过程中生成操作类,使用方式和ActiveAndroid高度相似,使用简单。无缝支持多个数据库,使用annotation processing提高速度,ModelContainer类库可以直接解析像JSON这样的数据,增加灵活性的丰富接口。

    • ActiveAndroid 是采用活动记录(Active Record)架构模式设计的适用于Android平台的轻量级ORM架构。

    二、数据库性能比较

    仔细找了一下发现 Android 平台上的数据库框架可真够多,但是有一个共同特点就是基于对象关系映射(ORM)模型的。实现的目标也都是不需要写 SQL 语句,通过对对象的操作保存和操作数据。要是从语法的简洁性来说都有自己的特点,总的来说不相上下,因此只能从数据的性能上来抉择了。下图对数据库执行性能进行了对比,测试数据来自http://www.jianshu.com/p/330bbd3b0e68

    database_option_time_compare

    从图中可以看出 Realm 的性能应该是最好的,同时它执行跨平台,且并不是采用 SQLite 的持久化引擎。

    三、Realm 数据库的使用

    Realm 官方文档:https://realm.io/docs/java/latest/

    1,环境配置

    1. 在项目的 build.gradle 加入如下代码:

      buildscript {
          repositories {
              jcenter()
          }
          dependencies {
              classpath "io.realm:realm-gradle-plugin:2.3.1"
          }
      }
      
    2. 在主工程 app 目录下的 build.gradle 的文件顶部加入如下代码:

      apply plugin: 'realm-android'
      
      •  

    2,自定义 Realm

    1. 创建数据库

      // 使用 RealmConfiguration 配置数据库
      // Realm 文件将创建在 Context.getFilesDir() 目录下,名字为 "myrealm.realm"   
      RealmConfiguration config = new RealmConfiguration.Builder()
        .name("myrealm.realm")
        .encryptionKey(getKey())
        .schemaVersion(1)
        .modules(new MySchemaModule())
        .migration(new MyMigration())
        .build();
      // 使用配置,获取一个 Realm 实例
      Realm realm = Realm.getInstance(config);
      
    2. 数据库版本升级

      // 当数据表结构改变时,删除数据库
      RealmConfiguration config = new RealmConfiguration.Builder()
          .deleteRealmIfMigrationNeeded()
          .build()
      
      // 更新数据
      RealmConfiguration config = new RealmConfiguration.Builder()
          .schemaVersion(2) // 数据表改变时,必须修改版本号
          .migration(new Migration()) // 设置在合并数据库时的修改
          .build()
      
      public class Migration implements RealmMigration {
          @Override
          public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
              // ...
          }
      }   
      

    3,创建实体表

    1. 创建一个实体类

      public class Dog extends RealmObject {
          private String name;
          private int age;
      
          @PrimaryKey
          private String id;
      
          // ... 设置和获取方法省略
      }
      
    2. 当包含多个时用 RealmList,如一个联系人包括多个邮件地址,如下:

      public class Contact extends RealmObject {
          public String name;
          public RealmList<Email> emails;
      }
      
      public class Email extends RealmObject {
          public String address;
          public boolean active;
      }
      
    3. 支持的数据类型: 
      boolean, byte, short, int, long, float, double, String, Date and byte[]

    4. 注解说明

      • @PrimaryKey

        1. 设置主键,字段类型必须是String、 integer、byte、short、 int、long 以及它们的封装类Byte, Short, Integer, and Long
        2. 使用了该注解之后可以使用copyToRealmOrUpdate()方法,通过主键查询它的对象,如果查询到了,则更新它,否则新建一个对象来代替。
        3. 使用了该注解将默认设置 @index 注解
        4. 使用了该注解之后,创建和更新数据将会慢一点,查询数据会快一点。
      • @Required 
        数据不能为空

      • @Ignore 
        忽略,即该字段不被存储到本地

      • @Index 
        为这个字段添加一个搜索引擎,这将使插入数据变慢、数据增大,但是查询会变快。建议在需要优化读取性能的情况下使用。

    4,添加

    1. 通过 Realm 新建一个对象,并进行存储

      realm.beginTransaction();
      User user = realm.createObject(User.class); // 通过 Realm 新建一个对象
      user.setName("John");
      user.setEmail("john@corporation.com");
      realm.commitTransaction();
      
    2. 复制一个对象到Realm数据库(与第一种方式的不同之处在于 User 对象是 new 出来的)

      User user = new User("John");
      user.setEmail("john@corporation.com");
      
      // Copy the object to Realm. Any further changes must happen on realmUser
      realm.beginTransaction();
      realm.copyToRealm(user);
      realm.commitTransaction();
      
    3. 使用事务块

      final User user = new User("John");
      user.setEmail("john@corporation.com");
      
      mRealm.executeTransaction(new Realm.Transaction() {
                  @Override
                  public void execute(Realm realm) {
      
                  realm.copyToRealm(user);
      
                  }
              });
      

    注:Realm 的增删改查操作都必须是在事务中进行。

    5,删除

        final RealmResults<Dog> dogs = mRealm.where(Dog.class).findAll();
    
        mRealm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm realm) {
    
                Dog dog=dogs.get(5);
                dog.deleteFromRealm();
                //删除第一个数据
                dogs.deleteFirstFromRealm();
                //删除最后一个数据
                dogs.deleteLastFromRealm();
                //删除位置为1的数据
                dogs.deleteFromRealm(1);
                //删除所有数据
                dogs.deleteAllFromRealm();
            }
        });
    

    或使用 beginTransaction() 和 commitTransaction() 的方式进行操作也是一样的。

    6,修改

        Dog dog = mRealm.where(Dog.class).equalTo("id", id).findFirst();
        mRealm.beginTransaction();
        dog.setName(newName);
        mRealm.commitTransaction();
    

    7,查询

    1. 全部查询,查询结果为 RealmResults,可以使用 mRealm.copyFromRealm(dogs) 方法将它转为 List

      public List<Dog> queryAllDog() {
          Realm  mRealm = Realm.getDefaultInstance();
          RealmResults<Dog> dogs = mRealm.where(Dog.class).findAll();
          return mRealm.copyFromRealm(dogs);
      }
      
    2. 条件查询

      public Dog queryDogById(String id) {
          Realm  mRealm = Realm.getDefaultInstance();
          Dog dog = mRealm.where(Dog.class).equalTo("id", id).findFirst();
          return dog;
      }
      
    3. 支持的条件查询语句

      • between(), greaterThan(), lessThan(), greaterThanOrEqualTo() & lessThanOrEqualTo()
      • equalTo() & notEqualTo()
      • contains(), beginsWith() & endsWith()
      • isNull() & isNotNull()
      • isEmpty() & isNotEmpty()
    4. 其它对查询结果的操作

      • sort()
      • sum()
      • min()
      • max()
      • average()

    8,异步操作

    大多数情况下,Realm的增删改查操作足够快,可以在UI线程中执行操作。但是如果遇到较复杂的增删改查,或增删改查操作的数据较多时,就可以子线程进行操作。

    RealmAsyncTask asyncTask = mRealm.executeTransactionAsync(new Realm.Transaction() {
        @Override
        public void execute(Realm realm) {
            // ... do something
        }
    }, new Realm.Transaction.OnSuccess() {
        @Override
        public void onSuccess() {
    
        }
    }, new Realm.Transaction.OnError() {
        @Override
        public void onError(Throwable error) {
    
        }
    });
    
    • 在窗体销毁时,记得要取消异步任务:
    protected void onDestroy() {
        if (asyncTask!=null && !asyncTask.isCancelled()){
            asyncTask.cancel();
        }
    }
    

    9,数据变更通知

    private RealmChangeListener callback = new RealmChangeListener() {
        @Override
        public void onChange(RealmResults<User> results) {
            // called once the query complete and on every update
        }
    };
    
    public void onStart() {
        RealmResults<User> result = realm.where(User.class).findAllAsync();
        result.addChangeListener(callback);
    }
    

    记得取消监听,在 Fragment 或 Activity 退出时,避免内存泄露

    public void onStop () {
        result.removeChangeListener(callback); // 移除指定的监听
        // 或
        result.removeChangeListeners(); // 移除注册的所有监听
    }
    

    这里列举了 Realm 使用的大部分方法,还有部分待后续补充。

     

    展开全文
  • Android数据库 之 SQLite数据库 Android数据库  一、关系型数据库SQLIte  每个应用程序都要使用数据,Android应用程序也不例外,Android使用开源的、与操作系统无关的SQL数据库—SQLite。SQLite第一...
  • android数据库 Android自带数据库SQLite 操作 一步一步 图示教程 附源代码 http://download.csdn.net/detail/wyx100/8670843
  • Android数据库升级

    千次阅读 2019-03-18 23:40:44
    今天跟着大神的blog学习下Android数据库升级的原理。 下面看一个软件的升级的例子 软件v1.0 安装v1.0,假设1.0版本只有一个account表,这时继续走SQLiteOpenHelper的onCreate,不走onUpgrade 软件v2.0 有两种...
  • Android 数据库查询

    2016-08-26 22:17:32
    1.Android 数据库 放在assets文件夹下,但是读取数据库时注意数据的地址path是: data/data/包名/files/数据库名;
  • Android数据库高手秘籍(零)——前言

    万次阅读 多人点赞 2014-09-04 09:07:19
    但是,能够很好地掌握数据库各方面的使用技巧,对于一个Android程序员来仍然是至关重要的,因此,这里我准备写一个《Android数据库高手秘籍》的专栏,带你一步步成为一个Android数据库高手,这是本专栏的第零篇文章
  • Android数据库升级总结

    千次阅读 2016-03-12 15:13:00
    Android数据库升级
  • 更改android 数据库存放位置
  • Android 数据库学习总结

    千次阅读 2018-06-09 16:28:08
    android 数据库学习总结 android 为了让我们能方便的使用数据库,给我们提供了一个SQLiteOpenHelper帮助类 它是一个抽象类,所以我们需要自定义一个类来继承这个类来实现我们创建数据库的逻辑 实现逻辑 ...
  • android数据库事务

    2011-05-21 22:32:00
    android数据库事务
  • Android数据库sqlite3及settings命令设置

    千次阅读 2018-06-09 09:54:46
    Android数据库sqlite3及settings命令设置 1. Android数据库sqlite3命令设置 2. Android数据库settings命令设置 Android数据库sqlite3及settings命令设置 1. Android数据库sqlite3命令设置 cd /data/...
  • Android数据库高手秘籍(五)——LitePal的存储操作

    万次阅读 多人点赞 2014-10-16 09:22:30
    还没有看过前一篇文章的朋友建议先去参考 Android数据库高手秘籍(四)——使用LitePal建立表关联 。 LitePal提供的CRUD操作的API还是颇为丰富的,一篇文章肯定是介绍不全的,因此这里我们仍然是分几篇文章进行讲解,...
  • Android数据库批量操作

    千次阅读 2017-10-09 11:24:40
    Android 数据库批量操作为了方便数据库的批量更新、删除、插入操作,android系统引入了ContentProviderOperation类。使用这个类具有以下优点:1.数据完整性,即所有的操作都在一个事务中执行。2.由于批量操作在一...
  • Android 数据库加密 SQLCipher使用方法

    万次阅读 2018-04-10 20:32:57
    Android 数据库加密 SQLCipher使用方法 CSDN Github 最近在做数据库加密,遇到了些问题,特此记录 greendao 支持数据库加密 网址http://greenrobot.org/greendao/documentation/database-encryption/ ...
  • Android 数据库DbFlow的使用

    千次阅读 2018-01-10 10:45:35
    Android 数据库DbFlow的使用 为什么要使用DbFlow DBFlow的设计吸取了其他很多ORM框架中好的特征,并将之做得更好。它很灵活,让你能更专注于App中真正需要关注的地方。不要让一个ORM库限制了你的思维,而是让...
  • Android数据库操作_表格显示

    千次阅读 2019-07-09 08:49:56
    Android数据库操作_表格显示 显示表格布局 完成后效果如下: 首先需要一个主布局文件main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width=...
  • Android数据库事件的回滚

    千次阅读 2016-04-01 22:58:51
    android数据库事务操作  浏览(3369)|评论(0) 交流分类:移动编程|笔记分类: 未分类  在android应用程序开发中,在使用到数据库的时候,事务处理是非常重要的。 首先android数据库操作(特别是写操作)是...
  • Android数据库并发操作解决思路

    千次阅读 2017-04-05 17:07:47
    一种Android数据库的并发操作解决思路
  • Android数据库监听器+Demo

    千次阅读 2017-08-06 15:54:50
    Android数据库监听器+demo数据库监听 在Android开发中,常常会使用到改变数据库内容之后,马上执行相应的操作,比如:文件删除之后,下载完成之后立即显示某个视图上面的内容。很多人都是使用数据库存储,当数据库...
  • 上一篇文章中,简单介绍了一下android数据库的一些基本概念,那么从本节开始,就实战一下Android数据库的创建和升级。 上文中,也介绍了,SQLiteOpenHelper是一个抽象类,是我们用来创建和升级数据库的最佳实践。...

空空如也

1 2 3 4 5 ... 20
收藏数 40,354
精华内容 16,141
关键字:

android数据库