SugarORM SQLCipher 数据库及加密教程

image

1.序言

最近找到了一个非常简单的DB管理工具 名为Sugar ORM 号称不需要编写SQL语句就能实现数据库操作。实际使用了一番发现在数据库表结构不复杂的情况下还是蛮好用的。另外Sugar ORM不提供数据库加密,因此需要配合SQLCipher实现加密。(这两个工具似乎在其他平台也能使用,下面以Android为例)

2.牛刀小试 Sugar ORM

1.导入Module

因为后面需要加入SQLCipher,所以直接下载源码import Module方便修改源码。具体import方法略过。

2.创建数据库基本信息

你的数据库信息需要提前在Manifest里输入,SugarDB才能正确初始化

<application>

<meta-data android:name="DATABASE" android:value="sugar_example.db" />
<meta-data android:name="VERSION" android:value="2" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.example" />

<application/>

DATABASE 是数据库名称,以.db结束 Example: test.db
VERSION 是数据库版本,每次更改数据库表的时候,VERSION需要加一,否则会出现no such table的错误日志
QUERY_LOG 是否输出日志,建议Debug为true,Release为false
DOMAIN_PACKAGE_NAME 以这个包下的类作为数据库表 Example:com.example.test.bean

接着在Application 里初始化
SugarContext.init(this)

3.Are U Ready?

就这么简单,你已经配置好Sugar DB了。
Sugar DB 是以类作为数据库操作的。比如在你的DOMAIN_PACKAGE_NAME 包名下创建一个类

public class Book extends SugarRecord {

  public String title;
  public String edition;

  public Book(){
  }

  public Book(String title, String edition){
    this.title = title;
    this.edition = edition;
  }
}

这个Book类继承自SugarRecord ,现在已经可以对它进行基本增删改查了。

 Book book = new Book();
 book.title = "钢之炼金术师";
 book.edition = "爱德华";
 book.save();

只要输入好类的属性,调用this.save就能插入一个记录到Book表了。下面用kotlin演示

val books = SugarRecord.find(Book::class.java,"title = ?","钢之炼金术师")

查询数据只需要调用SugarRecord.find(Class,whereClause,whereArgs)即可返回符合条件的Book数组。如果查询条件比较多,可以再往后加

val books = SugarRecord.find(Book::class.java,"title = ? && edition = ?","钢之炼金术师","爱德华")

遍历数组即可获得你要的查询后的数据了。

查询表里所有数据
val book = SugarRecord.listAll(Book::class.java)

删除表里所有数据
val book = SugarRecord.deleteAll(Book::class.java)

删除表里特定数据
val books = SugarRecord.deleteAll(Book::class.java,"title = ? && edition = ?","钢之炼金术师","爱德华")

改变数据

   val books = SugarRecord.find(Book::class.java,"title = ? && edition = ?","钢之炼金术师","爱德华")
   books[0].edition = "牛姨"
   books[0].save()

使用Sugar ORM就能相当简单的实现增删改查。

3.配合SQLCipher对数据库加密

1.导入SQLCipher

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

2.分析Sugar DB源码

image.png

这就是Sugar ORM 的目录结构,其实原理并不算特别复杂,就是封装了一层将Object翻译为SQL语句的过程 ,再通过SQLiteDatabase查询。

image.png

比较关键的类
SugarDb 继承了 android.database.sqlite.SQLiteOpenHelper,主要负责数据库的创建,更新,关闭等生命周期,实际更新是通过SchemaGenerator这个类实现的。其他类使用的SQLiteDatabase 也是由该单例类获得。SugarDb.getInstance().getDB()


image.png

SugarRecord 该类负责实际的增删改查,通过调用SugarDb的SQLiteDatabase实现。


image.png

ManifestHelper 该类是个工具类,负责读取Manifest里面meta-data的数据库名,版本等信息


image.png

3.集成SQLCipher

SQLCipher的正常使用可以参考这里
但是我现在想Sugar ORM帮我处理增删改查等操作。而使用的SQLiteDatabase需要是SQLCipher的加密SQLiteDatabase。
因此我们修改的重点就是在SugarDb这个类。
首先我们需要把

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

替换成sqlcipher的工具类

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;

可以看到构造方法报错了,你可以将CursorFactory改为sqlcipher的CursorFactory,或者直接传null


image.png
image.png

onConfigure方法也报错了,因为sqlcipher的SQLiteOpenHelper没有该方法,因此我们将这个配置方法移到onCreate里面


image.png

接下来我们需要一个数据库加密的秘钥

private static final String DB_PWD = "abcdefg123"; //数据库密码
将下面用到的getWritableDatabase getReadableDatabase改成

 getWritableDatabase(DB_PWD)
 getReadableDatabase(DB_PWD)

将getReadableDatabase方法的Override去掉,我们这个SugarDB就已经集成好sqlcipher了

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
import static com.orm.util.ContextUtil.getContext;
import static com.orm.helper.ManifestHelper.getDatabaseVersion;
import static com.orm.helper.ManifestHelper.getDbName;
import static com.orm.SugarContext.getDbConfiguration;

public class SugarDb extends SQLiteOpenHelper {
    private static final String LOG_TAG = "Sugar";
    public static final String DB_PWD="abcdefg123";//数据库密码
    private final SchemaGenerator schemaGenerator;
    private SQLiteDatabase sqLiteDatabase;
    private int openedConnections = 0;

    //Prevent instantiation
    private SugarDb() {
        super(getContext(), getDbName(), null, getDatabaseVersion());
        schemaGenerator = SchemaGenerator.getInstance();
    }

    public static SugarDb getInstance() {
        return new SugarDb();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        schemaGenerator.createDatabase(sqLiteDatabase);

        final SugarDbConfiguration configuration = getDbConfiguration();
        if (null != configuration) {
            sqLiteDatabase.setLocale(configuration.getDatabaseLocale());
            sqLiteDatabase.setMaximumSize(configuration.getMaxSize());
            sqLiteDatabase.setPageSize(configuration.getPageSize());
        }
    }


    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        schemaGenerator.doUpgrade(sqLiteDatabase, oldVersion, newVersion);
    }

    public synchronized SQLiteDatabase getDB() {
        if (this.sqLiteDatabase == null) {
            this.sqLiteDatabase = getWritableDatabase(DB_PWD);
        }

        return this.sqLiteDatabase;
    }

    public synchronized SQLiteDatabase getReadableDatabase() {
        if(ManifestHelper.isDebugEnabled()) {
            Log.d(LOG_TAG, "getReadableDatabase");
        }
        openedConnections++;
        return super.getReadableDatabase(DB_PWD);
    }

    @Override
    public synchronized void close() {
        if(ManifestHelper.isDebugEnabled()) {
            Log.d(LOG_TAG, "getReadableDatabase");
        }
        openedConnections--;
        if(openedConnections == 0) {
            if(ManifestHelper.isDebugEnabled()) {
                Log.d(LOG_TAG, "closing");
            }
            super.close();
        }
    }
}

接下来再将SchemaGenerator 的import android.database.sqlite.SQLiteDatabase;替换成import net.sqlcipher.database.SQLiteDatabase; 如果别的类有出现报错也依次替换包为net.sqlcipher.database

至此,Sugar ORM 跟 SQLCipher的结合已经完成,我们跑一下打开DB文件看效果。

image.png

Well Done!!!

4.尾语

这次从客户提出要加密到集成结束也就花了三个小时,但是这次阅读别人的源码确实也受益匪浅。SQLCipher的内容讲得比较少,建议参考这里再阅读会更能理解。

image

1.序言

最近找到了一个非常简单的DB管理工具 名为Sugar ORM 号称不需要编写SQL语句就能实现数据库操作。实际使用了一番发现在数据库表结构不复杂的情况下还是蛮好用的。另外Sugar ORM不提供数据库加密,因此需要配合SQLCipher实现加密。(这两个工具似乎在其他平台也能使用,下面以Android为例)

2.牛刀小试 Sugar ORM

1.导入Module

因为后面需要加入SQLCipher,所以直接下载源码import Module方便修改源码。具体import方法略过。

2.创建数据库基本信息

你的数据库信息需要提前在Manifest里输入,SugarDB才能正确初始化

<application>

<meta-data android:name="DATABASE" android:value="sugar_example.db" />
<meta-data android:name="VERSION" android:value="2" />
<meta-data android:name="QUERY_LOG" android:value="true" />
<meta-data android:name="DOMAIN_PACKAGE_NAME" android:value="com.example" />

<application/>

DATABASE 是数据库名称,以.db结束 Example: test.db
VERSION 是数据库版本,每次更改数据库表的时候,VERSION需要加一,否则会出现no such table的错误日志
QUERY_LOG 是否输出日志,建议Debug为true,Release为false
DOMAIN_PACKAGE_NAME 以这个包下的类作为数据库表 Example:com.example.test.bean

接着在Application 里初始化
SugarContext.init(this)

3.Are U Ready?

就这么简单,你已经配置好Sugar DB了。
Sugar DB 是以类作为数据库操作的。比如在你的DOMAIN_PACKAGE_NAME 包名下创建一个类

public class Book extends SugarRecord {

  public String title;
  public String edition;

  public Book(){
  }

  public Book(String title, String edition){
    this.title = title;
    this.edition = edition;
  }
}

这个Book类继承自SugarRecord ,现在已经可以对它进行基本增删改查了。

 Book book = new Book();
 book.title = "钢之炼金术师";
 book.edition = "爱德华";
 book.save();

只要输入好类的属性,调用this.save就能插入一个记录到Book表了。下面用kotlin演示

val books = SugarRecord.find(Book::class.java,"title = ?","钢之炼金术师")

查询数据只需要调用SugarRecord.find(Class,whereClause,whereArgs)即可返回符合条件的Book数组。如果查询条件比较多,可以再往后加

val books = SugarRecord.find(Book::class.java,"title = ? && edition = ?","钢之炼金术师","爱德华")

遍历数组即可获得你要的查询后的数据了。

查询表里所有数据
val book = SugarRecord.listAll(Book::class.java)

删除表里所有数据
val book = SugarRecord.deleteAll(Book::class.java)

删除表里特定数据
val books = SugarRecord.deleteAll(Book::class.java,"title = ? && edition = ?","钢之炼金术师","爱德华")

改变数据

   val books = SugarRecord.find(Book::class.java,"title = ? && edition = ?","钢之炼金术师","爱德华")
   books[0].edition = "牛姨"
   books[0].save()

使用Sugar ORM就能相当简单的实现增删改查。

3.配合SQLCipher对数据库加密

1.导入SQLCipher

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

2.分析Sugar DB源码

image.png

这就是Sugar ORM 的目录结构,其实原理并不算特别复杂,就是封装了一层将Object翻译为SQL语句的过程 ,再通过SQLiteDatabase查询。

image.png

比较关键的类
SugarDb 继承了 android.database.sqlite.SQLiteOpenHelper,主要负责数据库的创建,更新,关闭等生命周期,实际更新是通过SchemaGenerator这个类实现的。其他类使用的SQLiteDatabase 也是由该单例类获得。SugarDb.getInstance().getDB()


image.png

SugarRecord 该类负责实际的增删改查,通过调用SugarDb的SQLiteDatabase实现。


image.png

ManifestHelper 该类是个工具类,负责读取Manifest里面meta-data的数据库名,版本等信息


image.png

3.集成SQLCipher

SQLCipher的正常使用可以参考这里
但是我现在想Sugar ORM帮我处理增删改查等操作。而使用的SQLiteDatabase需要是SQLCipher的加密SQLiteDatabase。
因此我们修改的重点就是在SugarDb这个类。
首先我们需要把

import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

替换成sqlcipher的工具类

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;

可以看到构造方法报错了,你可以将CursorFactory改为sqlcipher的CursorFactory,或者直接传null


image.png
image.png

onConfigure方法也报错了,因为sqlcipher的SQLiteOpenHelper没有该方法,因此我们将这个配置方法移到onCreate里面


image.png

接下来我们需要一个数据库加密的秘钥

private static final String DB_PWD = "abcdefg123"; //数据库密码
将下面用到的getWritableDatabase getReadableDatabase改成

 getWritableDatabase(DB_PWD)
 getReadableDatabase(DB_PWD)

将getReadableDatabase方法的Override去掉,我们这个SugarDB就已经集成好sqlcipher了

import net.sqlcipher.database.SQLiteDatabase;
import net.sqlcipher.database.SQLiteOpenHelper;
import static com.orm.util.ContextUtil.getContext;
import static com.orm.helper.ManifestHelper.getDatabaseVersion;
import static com.orm.helper.ManifestHelper.getDbName;
import static com.orm.SugarContext.getDbConfiguration;

public class SugarDb extends SQLiteOpenHelper {
    private static final String LOG_TAG = "Sugar";
    public static final String DB_PWD="abcdefg123";//数据库密码
    private final SchemaGenerator schemaGenerator;
    private SQLiteDatabase sqLiteDatabase;
    private int openedConnections = 0;

    //Prevent instantiation
    private SugarDb() {
        super(getContext(), getDbName(), null, getDatabaseVersion());
        schemaGenerator = SchemaGenerator.getInstance();
    }

    public static SugarDb getInstance() {
        return new SugarDb();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        schemaGenerator.createDatabase(sqLiteDatabase);

        final SugarDbConfiguration configuration = getDbConfiguration();
        if (null != configuration) {
            sqLiteDatabase.setLocale(configuration.getDatabaseLocale());
            sqLiteDatabase.setMaximumSize(configuration.getMaxSize());
            sqLiteDatabase.setPageSize(configuration.getPageSize());
        }
    }


    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        schemaGenerator.doUpgrade(sqLiteDatabase, oldVersion, newVersion);
    }

    public synchronized SQLiteDatabase getDB() {
        if (this.sqLiteDatabase == null) {
            this.sqLiteDatabase = getWritableDatabase(DB_PWD);
        }

        return this.sqLiteDatabase;
    }

    public synchronized SQLiteDatabase getReadableDatabase() {
        if(ManifestHelper.isDebugEnabled()) {
            Log.d(LOG_TAG, "getReadableDatabase");
        }
        openedConnections++;
        return super.getReadableDatabase(DB_PWD);
    }

    @Override
    public synchronized void close() {
        if(ManifestHelper.isDebugEnabled()) {
            Log.d(LOG_TAG, "getReadableDatabase");
        }
        openedConnections--;
        if(openedConnections == 0) {
            if(ManifestHelper.isDebugEnabled()) {
                Log.d(LOG_TAG, "closing");
            }
            super.close();
        }
    }
}

接下来再将SchemaGenerator 的import android.database.sqlite.SQLiteDatabase;替换成import net.sqlcipher.database.SQLiteDatabase; 如果别的类有出现报错也依次替换包为net.sqlcipher.database

至此,Sugar ORM 跟 SQLCipher的结合已经完成,我们跑一下打开DB文件看效果。

image.png

Well Done!!!

4.尾语

这次从客户提出要加密到集成结束也就花了三个小时,但是这次阅读别人的源码确实也受益匪浅。SQLCipher的内容讲得比较少,建议参考这里再阅读会更能理解。

点这里发现一个有趣的开发者

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

推荐阅读更多精彩内容