鉴于公司之前一直在使用SQLCipher+Afinal(数据库部分),然后有些根深蒂固的错误,十分期望能换种加密方式。可是,除了SQLCipher,百度了一下没找到什么新的。无意中发现了Realm,看起来还挺好用,决定拿来踩踩坑。
realm导入: module的gradle:
apply plugin: 'com.android.library' apply plugin: 'realm-android' buildscript { repositories { jcenter() } dependencies { classpath "io.realm:realm-gradle-plugin:2.2.1" }官方地址:github:https://github.com/realm/realm-java 1.初始化 初始化的部分,跟SQLite原生一样都要传个Context,想着要不用getApplicationContext()算了,无意中发现居然有个getDefaultInstance(),感觉非常满意,就它了,于是又了下面的初始化: Application中:
private void initRealm() { Realm.init(this); RealmConfiguration config = new RealmConfiguration.Builder() .name(Config.dbName) .schemaVersion(Config.dbVersion) .encryptionKey(Config.dbKey.getBytes()) .migration(DBUtil.getIntance())//升级数据库处理类(实现RealmMigration接口) .build(); Realm.setDefaultConfiguration(config); }这里讲几个点:
建议做个配置文件,方便查询和修改 方法解释 (1)Realm.init(this);//这句是初始化一定要加 (2)RealmConfiguration这个类用于配置realm的一些属性 name - 数据库名 schemaVersion - 数据库版本号,用于识别是否升级 encryptionKey - 密钥,用于加密数据库,只能定64个字母长的String再getBytes(),否则会提示位数不符合的异常,或者按照官方的: byte[] key = new byte[64]; new SecureRandom().nextBytes(key); RealmConfiguration config = new RealmConfiguration.Builder() .encryptionKey(key) .build();如果用官方的注意调试,再查看key值,因为random会导致每次生成的字串都不一样,官方声称这是一种很傻的做法,不过官方两个链接进不去了,所以暂时自己定个密钥算了。
migration - 定义升级的类 当新版和旧版realm版本不同时,会对实现RealmMigration接口的类调用相应的方法2.model - 数据库表行 可以定义注解: @PrimaryKey(主键,没有自增长) @Index(加快该字段查询速度,减慢该字段插入速度) @Ignore(忽略该字段,不放入数据库中) @Requied(非空字段)
注意该类一定要继承RealmObject,据说可以只实现RealmModel,但貌似会报错。 该model还有一个奇葩属性,这里一定要提一下: 除了第一次set,其他时候不能随意set! 否则会报错:只能在update的事务中更新,各种copy都不行 感觉这个特性很坑啊,有的时候编辑,如果没有保存是要恢复原状的,你这让我咋弄咧?
关于model,还有一点要注意: 一定不能重写toString()方法,会报错,这个坑我找了好久才发现= =
为此,将数据库model和普通model添加了转换,事例如下: 数据库User:
public class User extends RealmObject { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public NormalUser toNormalUser(){ NormalUser user = new NormalUser(); user.setId(id); user.setName(name); return user; } }普通User:
public class NormalUser { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public User toDBUser(){ User user = new User(); user.setId(id); user.setName(name); return user; } }接下来写版本更新和获取数据库:
public class DBUtil implements RealmMigration { private static DBUtil sIntance; public DBUtil() { } /** * 双检索单例 * @return */ public static DBUtil getIntance(){ if (sIntance == null) { synchronized (DBUtil.class) { if (sIntance == null) { sIntance = new DBUtil(); } } } return sIntance; } /** * 获取realm对象 * @return */ public Realm getRealm(){ Realm realm =getDefaultInstance(); return realm; } /** * 版本升级处理 * @param realm * @param oldVersion * @param newVersion */ @Override public void migrate(DynamicRealm realm, long oldVersion, long newVersion) { RealmSchema schema = realm.getSchema(); switch ((int)oldVersion){ // case 0:{ // RealmObjectSchema personSchema = schema.get("User");//获取表 // personSchema // .addField("outerId", String.class)//添加字段 // .transform(new RealmObjectSchema.Function() { // @Override // public void apply(DynamicRealmObject obj) { // obj.set("outerId", "1");//为id设置默认值 // } // }); .removeField("age");//移除age属性 // //注意version不要break,因为前面的版本都要升级 // } } } }可以看到,版本升级的部分隐藏了,如果在User中添加一个outerId字段,然后修改数据库版本0->1,可以打开这部分看看效果,我试过了,是可用的。
然后,看下增删改查怎么写:
public class UserDao implements BaseDao<User> { private UserDao(){ } private Realm getRealm(){ return DBUtil.getIntance().getRealm(); } private static UserDao dao; public static UserDao getInstance(){ if(dao == null) dao = new UserDao(); return dao; } @Override public void insert(final User user) throws Exception { getRealm().executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { User u = realm.createObject(User.class); u.setId(user.getId()); u.setName(user.getName()); } }); } @Override public List<User> find() throws Exception { List<User> mlist = null; mlist = getRealm().where(User.class).findAllSorted("id", Sort.ASCENDING); return mlist; } @Override public void update(final User user) throws Exception { getRealm().executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { realm.insertOrUpdate(user); } }); } @Override public void delete(final String id) throws Exception { getRealm().executeTransaction(new Realm.Transaction() { @Override public void execute(Realm realm) { RealmResults<User> result = realm.where(User.class).equalTo("id", id).findAll(); if(result!=null) result.deleteAllFromRealm(); } }); } @Override public void close() { getRealm().close(); } //分页查询 public static <E extends RealmModel> List<E> getLimitList(RealmResults<E> data, int offset, int limit) { List<E> obtainList = new ArrayList(); Realm realm = Realm.getDefaultInstance(); if (data.size() == 0 ){ return obtainList; } for (int i = offset; i < offset + limit; i++) { if (i >= data.size()) { break; } E temp = realm.copyFromRealm(data.get(i)); obtainList.add(temp); } realm.close(); return obtainList; } }//最后一个是分页,参数1realm数据,可先通过
RealmResults <User> result = realm.where(User.class).equalTo("id", id).findAll();获取,参数2:开始的index,参数3:取出数目 例如参数2为0,参数3为20则为取出0-19的数据。注意,realm的findAll取出数据经测试,是乱序的。如果需要排序,请添加findAllSorted(“id”, Sort.ASCENDING);,其中ASCENDING是升序,DESCENDING是降序
realm.insertOrUpdate(user);这一句表示没有该数据时插入,有该数据时更新,建议替换成下面:
User user=realm.where(User.class).equalTo("id",mId).findFirst(); user.setName(name);注意,该部分语句要放到transaction当中 在主页的时候,退出时要注意关闭数据库:
UserDao.getInstance().close();