精华内容
下载资源
问答
  • android数据库

    千次阅读 2011-06-27 17:45:00
    Android 数据存取之Databases 收藏 在Android平台上可以操作数据库,这是第一次接触Android时的惊艳之一。在Android平台上,绑定了SQLite数据库,这个数据库系统也是极具性格的,它的最大的应用场景是嵌入式系统,...

    转载   Android 数据存取之Databases 收藏

     

     

    在Android平台上可以操作数据库,这是第一次接触Android时的惊艳之一。在Android平台上,绑定了SQLite数据库,这个数据库系统也是极具性格的,它的最大的应用场景是嵌入式系统,进一步了解可以参看这里

    如果有JDBC的经验,那么在这里会容易的多。Android中操作数据库首先要通过一个类:android.database.sqlite.SQLiteOpenHelper。它封装了如何打开一个数据库,其中当然也包含如果数据库不存在就创建这样的逻辑。看一个例子:

    这里面,如下的语句需要解释:

    • super(context, DATABASE_NAME, null, DATABASE_VERSION)
      数据库连接的初始化,中间的那个null,是一个CursorFactory参数,没有仔细研究这个参数,暂时置空吧。
    • public void onCreate(SQLiteDatabase db)
      这里面的onCreate是指数据库onCreate 时,而不是DatabaseHelper的onCreate。也就是说,如果已经指定 database已经存在,那么在重新运行程序的时候,就不会执行这个方法了。要不然,岂不是每次重新启动程序都要重新创建一次数据库了!在这个方法中,完成了数据库的创建工作。也就是那个execSQL()方法。
    • public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
      在程序的开发维护过程中,数据库的结构可能会有变化,那么这个方法就有用处了。在DatabaseHelper这个对象一创建时,就已经把参数 DATABASE_VERSION传入,这样,如果Android发现此版本与现有版本不一致,就会调用这个onUpgrate方法。于是,可以在这里面实现一些数据的upgrade工作,比如说创建一个临时表,将数据由临时表中转到新的表结构中。需要注意的是,这里面的onUpgrade是在版本不一致时调用,也就是说不管当前需要的版本高于现有版本还是低于现有版本,都会出发这个方法,类似的这种情况,就需要对oldVersion和 newVersion进行判断之后再决定使用什么策略来更新数据。

    在Android中,数据库存放在 /data/data/PACKAGE_NAME/databases  目录下。

    接下来就可以使用这个Helper来操作数据库了,操作数据库也就无非是增、删、改、查。先看一个增的例子:

    • DatabaseHelper mOpenHelper = new DatabaseHelper(context);
      如果和JDBC例子的话,这一步貌似就像是获得了一个Statement,用它就可以操作数据库了。
    • ContentValues values = new ContentValues();
      Android在向数据库中插入数据的时候,要求数据存放到ContentValues中,这里面的ContentValues其实就是一个 Map,Key值是字段名称,Value值是字段的值。这样,也许你会发现一个问题,那数据类型怎么办?其实在SQLite数据库中就是没有数据类型的,一切都是字符串。
    • mOpenHelper.getReadableDatabase().insert(table,nullColumnHack, values);
      将数据入库,注意这里面有一个nullColumnHack,看文档是和所有字段都是空的记录有关系,我没有去实验他具体的效果,只是随便给他一个字段名称。

    再看一个查的例子:

     

     

    DatabaseHelper mOpenHelper = new DatabaseHelper(context);
    于前文中的相同

    • mOpenHelper.getReadableDatabase().query();
      通过mOpenHelper.getReadableDatabase(),会得到一个SQLiteDatabase类型的只读的数据库连接,在这里直接调用了他的query方法。这个query方法相对复杂,因为他将一个完整的SQL语句拆成了若干个部分:
      • table:表名。相当于SQL的from后面的部分。那如果是多表联合查询怎么办?那就用逗号将两个表名分开,拼成一个字符串作为table的值。
      • columns:要查询出来的列名。相当于SQL的select后面的部分。
      • selection:查询条件,相当于SQL的where后面的部分,在这个语句中允许使用“?”,也就是说这个用法和JDBC中的PreparedStatement的用法相似。
      • selectionArgs:对应于selection的值,selection有几个问号,这里就得用几个值。两者必须一致,否则就会有异常。
      • groupBy:相当于SQL的group by后面的部分
      • having:相当于SQL的having后面的部分
      • orderBy:相当于SQL的order by后面的部分,如果是倒序,或者是联合排序,可以写成类似这样:String orderBy = “id desc, name”;
      • limit:指定结果集的大小,它和Mysql的limit用法不太一样,mysql可以指定从多少行开始之后取多少条,例如“limit 100,10”,但是这里只支持一个数值。
    • c.moveToFirst();
      这一句也比较重要,如果读取数据之前,没有这一句,会有异常。
    • c.getString(1);
      与JDBC一致了,Android不支持按字段名来取值,只能用序号。

    再看一个删除和修改的例子:

     

    有了上面的基础这里就容易理解了,这里的whereClause相当于前面的selection,whereArgs相当于前面的selectionArgs。

     

    这个update的用法,综合select和delete就可以理解。

    注意:

    • Cursor和Databases要及时关闭,不然也会有异常。
    • getWritableDatabase()和getReadableDatabase()在当前的Android版本中貌似可以通用,像上面的insert,用的就是getReadableDatabase。

    在真实的应用中,会对上面这些基本操作做更高一级的抽象和封装,使之更容易使用。在select时,除了用上述的方法,将分段的SQL语句传进去之外,Android还支持一种方法:使用SQLiteQueryBuilder。如果使用的是上述的分段SQL语句的方法,在Android的内部实现中,也是先将分段的SQL使用SQLiteQueryBuilder的静态方法来生成一个真正的SQL的,而且,我没有看出来使用 SQLiteQueryBuilder的优势。

    展开全文
  • 上一篇文章中我们学习了一些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数据库 Android自带数据库SQLite 操作 一步一步 图示教程 附源代码 http://download.csdn.net/detail/wyx100/8670843
  • Android数据库 之 SQLite数据库

    千次阅读 2016-02-24 09:50:21
    Android数据库  一、关系型数据库SQLIte  每个应用程序都要使用数据,Android应用程序也不例外,Android使用开源的、与操作系统无关的SQL数据库—SQLite。SQLite第一个Alpha版本诞生于2000年5月,它是一款轻量...
  • Android数据库升级

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

    千次阅读 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数据库事务

    千次阅读 2012-06-01 22:08:05
     首先android数据库操作(特别是写操作)是非常慢的,将所有操作打包成一个事务能大大提高处理速度。  其次是保证数据的一致性,让一个事务中的所有操作都成功执行,或者失败,或者所有操作回滚。  ...
  • Android数据库监听器+Demo

    千次阅读 2017-08-06 15:54:50
    Android数据库监听器+demo数据库监听 在Android开发中,常常会使用到改变数据库内容之后,马上执行相应的操作,比如:文件删除之后,下载完成之后立即显示某个视图上面的内容。很多人都是使用数据库存储,当数据库...
  • 上一篇文章中,简单介绍了一下android数据库的一些基本概念,那么从本节开始,就实战一下Android数据库的创建和升级。 上文中,也介绍了,SQLiteOpenHelper是一个抽象类,是我们用来创建和升级数据库的最佳实践。...
  • 这篇文章来通过介绍对微信数据库密码的破解来了解下对整个数据库加密的这种Android数据库加密方式。 一、安装、反编译微信,查找本地数据库 1、直接使用apktool反编译微信,这里微信的版本是6.5.8。虽然腾讯现
  • android数据库持久化框架

    千次阅读 2013-11-02 16:32:17
    android数据库持久化框架  android数据库持久化框架  前言  Android中内置了SQLite,但是对于数据库操作这块,非常的麻烦.其实可以试用第3方的数据库持久化框架对之进行结构上调整, 摆脱了访问数据库操作的细节,...
  • Android数据库及网络编程资料推荐,最好有链接!小弟买了一本《android从入门到精通》,数据库和网络编程讲的很少
  • 新人自学android 数据库心得  从sqlite 查看某一表的命令:select * from sqlite_master where type="table" and name=tableName 当创建本地数据库成功时发现eclipse File Manager 并没有数据库文件, 仔细查找...
  • Android数据库信息显示在listview上

    千次阅读 2019-10-03 23:45:50
    1.使用SimpleCursorAdapter将Android数据库信息显示在listview上 adapter = new SimpleCursorAdapter(this, R.layout.user_list_cell, null, new String[]{"name","sex"}, new int[]{R.id.tvName, R.id....
  • Android数据库更新

    千次阅读 2019-04-24 17:54:44
    最近在写一个android开发框架的时候用到了ormlite,也稍微封装了一下。但是如果已经存在一个数据库,我要在其中一个表中插入某一列可怎么办呢?不止如此,我想删除某一列,修改某一列的数据类型,修改某个表的名字,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 46,069
精华内容 18,427
关键字:

android数据库