查看原文
其他

微信 WCDB 正式开源——高效易用的移动数据库框架

2017-06-26 于亚豪 终端研发部


前言介绍

腾讯开源微信数据库框架WCDB,它是一个高效、完整、易用的移动数据库框架,基于SQLCipher,支持iOS, macOS和Android。

博客地址:

http://blog.csdn.net/androidstarjack/article/details/73730052

正文

便捷地定义表、索引、约束,并进行增删改查操作

项目演示效果如下:

微信 即时通讯软件

  • 微信(英文名:wechat)是腾讯公司于2011年1月21日推出的一个为智能终端提供即时通讯服务的免费应用程序

  • 微信支持跨通信运营商、跨操作系统平台通过网络快速发送免费语音短信、视频、图片和文字

  • 同时,也可以使用通过共享流媒体内容的资料和基于位置的社交插件“摇一摇”、“漂流瓶”、“朋友圈”、”公众平台“、”语音记事本“等服务插件。

  • 腾讯于6月9日在 GMTC 全球移动技术大会上正式宣布, WCDB(WeChat Database)作为微信的一个开源组件正式对外开源

基本功能

  • 基于SQLCipher的数据库加密

  • 使用连接池实现并发读写

  • 内建 Repair Kit 可用于修复损坏数据库

  • 针对占用空间大小优化的数据库备份/恢复功能

  • 日志输出重定向以及性能跟踪接口

  • 内建用于全文搜索的 mmicu FTS3/4 分词器 入门

官方介绍如下:

WCDB 是一个高效、完整、易用的移动数据库框架,基于 SQLCipher,支持 iOS、macOS 和 Android。

WCDB的好处

  • WINQ(WCDB语言集成查询): 通过WINQ,开发者无须为了拼接SQL的字符串而写一大坨胶水代码。

  • ORM(Object Relational Mapping): WCDB支持灵活、易用的ORM。开发者可以很便捷地定义表、索引、约束,并进行增删改查操作。

  • 多线程高并发: WCDB支持多线程读与读、读与写并发执行,写与写串行执行。

  • 加密:WCDB提供基于SQLCipher的数据库加密。

  • 损坏修复: WCDB内建了Repair Kit用于修复损坏的数据库。

  • 反注入: WCDB内建了对SQL注入的保护。

  • 基于SQLCipher的数据库加密

  • 使用连接池实现并发读写

  • 内建 Repair Kit 可用于修复损坏数据库

  • 针对占用空间大小优化的数据库备份/恢复功能

  • 日志输出重定向以及性能跟踪接口

  • 内建用于全文搜索的 mmicu FTS3/4 分词器

相关代码(SQLiteOpenHelper类)

/** * 类功能描述:</br> * 新数据处理帮助类 * @author 于亚豪 *  博客地址: http://blog.csdn.net/androidstarjack * 公众号: 终端研发部 * @version 1.0 </p> 修改时间:</br> 修改备注:</br> */

public class EncryptedDBHelper extends SQLiteOpenHelper {    
   private static final String TAG = "EncryptedDBHelper";    
   private static final String DATABASE_NAME = "encrypted.db";    
 
   private static final String OLD_DATABASE_NAME = "plain-text.db";    private static final int DATABASE_VERSION = 2;    private Context mContext;    private String mPassphrase;    public EncryptedDBHelper(Context context, String passphrase) {        // 调用“加密”版本的超类构造函数。        super(context, DATABASE_NAME, passphrase.getBytes(), null, null, DATABASE_VERSION,                null);        // 保存上下文对象供以后使用。        mContext = context;        mPassphrase = passphrase;    }
   
   @Override    public void onCreate(SQLiteDatabase db) {        
       // 检查数据库plain-text.db是否存在 ,存在 如果是这样,将其导出到新的加密库中的。        File oldDbFile = mContext.getDatabasePath(OLD_DATABASE_NAME);        if (oldDbFile.exists()) {            Log.i(TAG, "Migrating plain-text database to encrypted one.");            //SQLiteOpenHelper在调用onCreate()之前开始一个事务。 我们必须结束事务才能附加一个新的数据库。            db.endTransaction();            
           // 将旧数据库附加到新创建的加密数据库。            String sql = String.format("ATTACH DATABASE %s AS old KEY '';",            DatabaseUtils.sqlEscapeString(oldDbFile.getPath()));            db.execSQL(sql);          
           // 导出旧数据库。            db.beginTransaction();            DatabaseUtils.stringForQuery(db, "SELECT sqlcipher_export('main', 'old');", null);            db.setTransactionSuccessful();            db.endTransaction();            
           // 获取旧的数据库版本供以后升级。            int oldVersion = (int) DatabaseUtils.longForQuery(db, "PRAGMA old.user_version;", null);            
           // 分离旧数据库并输入新的事务。            db.execSQL("DETACH DATABASE old;");            
           // 旧数据库现在可以删除。            oldDbFile.delete();            
           // 在进一步的操作之前,还原事务。            db.beginTransaction();            
           // 检查我们是否需要升级架构。            if (oldVersion > DATABASE_VERSION) {                onDowngrade(db, oldVersion, DATABASE_VERSION);            } else if (oldVersion < DATABASE_VERSION) {                onUpgrade(db, oldVersion, DATABASE_VERSION);            }        } else {            Log.i(TAG, "Creating new encrypted database.");            
           // 如果旧数据库不存在,请进行真正的初始化。            db.execSQL("CREATE TABLE message (content TEXT, "                    + "sender TEXT);");        }        
       // 损坏恢复的备份主信息。        RepairKit.MasterInfo.save(db, db.getPath() + "-mbak", mPassphrase.getBytes());    }  
   @Override    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {        Log.i(TAG, String.format("Upgrading database from version %d to version %d.",                oldVersion, newVersion));      
        //将新列添加到数据库升级的消息表中。        db.execSQL("ALTER TABLE message ADD COLUMN sender TEXT;");      
        //损坏恢复的备份主信息        RepairKit.MasterInfo.save(db, db.getPath() + "-mbak", mPassphrase.getBytes());    } }

WCDB如何查询数据库的相关示例:

new AsyncTask<Void, Void, Cursor>() {      
       @Override        protected void onPreExecute() {            mAdapter.notifyDataSetChanged();        }
       @Override        protected Cursor doInBackground(Void... params) {          
         if (mDB == null || !mDB.isOpen())          
              return null;            String message = "Message inserted on " + DATE_FORMAT.format(new Date());          
               if (mDBVersion == 1) {                mDB.execSQL("INSERT INTO message VALUES (?);",                        
               new Object[]{"yyh"});              
               return mDB.rawQuery("SELECT rowid as _id, content, '???' as sender FROM message;", null);            } else {                mDB.execSQL("INSERT INTO message VALUES (?, ?);",                        
               new Object[]{"yyh", "男"});          
               return mDB.rawQuery("SELECT rowid as _id, content, sender FROM message;",null);            }        }
       @Override        protected void onPostExecute(Cursor cursor)      
            if (cursor == null)            
            return;            list = getAllStudent(cursor);            mAdapter.changeCursor(list);        }    }.execute();

WWCDB如何插入表的相关示例:

final DateFormat DATE_FORMAT = SimpleDateFormat.getDateTimeInstance();  
 new AsyncTask<Void, Void, Cursor>() {      
      @Override        protected void onPreExecute() {            mAdapter.notifyDataSetChanged();        }        
      
      @Override        protected Cursor doInBackground(Void... params) {            
          if (mDB == null || !mDB.isOpen())                
          return null;            String message = "Message inserted on " + DATE_FORMAT.format(new Date());            
          if (mDBVersion == 1) {                mDB.execSQL("INSERT INTO message VALUES (?);",  new Object[]{"yyh"});                
               return mDB.rawQuery("SELECT rowid as _id, content, '???' as sender FROM message;",null);            } else {                mDB.execSQL("INSERT INTO message VALUES (?, ?);", new Object[]{"yyh", "男"});              
          return mDB.rawQuery("SELECT rowid as _id, content, sender FROM message;",null);            }        }
       @Override        protected void onPostExecute(Cursor cursor) {            
          if (cursor == null)              
           return;            list = getAllStudent(cursor);            mAdapter.changeCursor(list);        }    }.execute();

WCDB如何删除表的相关示例

if (mDB == null || !mDB.isOpen()){    return  ;     } mDB.execSQL("DELETE FROM message WHERE content"+"=?",new Object[]{"yyh"}); com.tencent.wcdb.Cursor cursor =  mDB.rawQuery("SELECT rowid as _id, content, sender FROM message;",null); list = getAllStudent(cursor); mAdapter.changeCursor(list);

完整demo下载地址:

相关项目下载地址(github):

https://github.com/androidstarjack/MyWCDBStudy

关于WCDB还有很多的地方要去学习,该demo中只是演示的其中的冰山一角,接下来我们还有很多要探寻的。

WCDB官方地址:

WCDB官方地址

https://github.com/Tencent/wcdb/wiki

相信自己,没有做不到的,只有想不到的


让心,在阳光下学会舞蹈

让灵魂,在痛苦中学会微笑

—终端研发部—



如果你觉得此文对您有所帮助,欢迎入群 QQ交流群 :232203809   

微信公众号:终端研发部


            

这里学到的不仅仅是技术


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存