1. 程式人生 > >資料庫框架GreenDao——比Realm好一點

資料庫框架GreenDao——比Realm好一點

說實話android的資料庫確實比較難用,尤其是資料量較大的時候。使用的SQL語句可能就比較多了。再加上佔位符,體積龐大。於是realm,greendao這些自動建立資料庫的就出現了。

Realm

realm優點:比SQLite更簡潔,更省空間,可以只儲存在記憶體,應用關閉自動清理,自帶加密功能。呼叫方法易讀,易用。

缺點:
1.資料必須與資料庫同步,第一次set之後,更新必須資料庫的資料也要更新,簡直有點霸王條款的意思?我每個物件都要寫兩次,一個對應資料庫,一個對應其他的,這不太科學。
2.不能繼承RealmObject以外的類【給的RealmModel實現方式是假的,繼承就bug】。

資料庫初始化

Realm.init(this);
        RealmConfiguration config = new RealmConfiguration.Builder()
                .name(Config.dbName)
                .schemaVersion(Config.dbVersion)
                //.isMemory()//記憶體快取用
                .encryptionKey(Config.dbKey.getBytes())//加密字串長度64即byte128
                .migration
(DBUtil.getIntance())//升級資料庫處理類(實現RealmMigration介面) .build(); Realm.setDefaultConfiguration(config);

資料庫升級(需要實現RealmMigration )

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");//獲取表 //// //新增@Required的id // 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,因為前面的版本都要升級 // } } } }

realm bean初始化

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;
    }

}

dao層(手寫)

public class UserDao implements BaseDao<User> {
    private UserDao(){
    }

    private Realm realm;
    private Realm getRealm(){
        if(realm == null)
            realm = DBUtil.getIntance().getRealm();
        return realm;
    }

    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) {
                User u=realm.where(User.class).equalTo("id",user.getId()).findFirst();
                u.setName(user.getName());
            }

        });
    }


    @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;
    }
}

realm插入:

try {
                    dao.insert(user);
                    dao.insert(user2);
                    dao.insert(user3);
                    List<User> users = dao.find();
                    for (int i = 0; i < users.size(); i++) {
                        Toast.makeText(this, "插入資料" + users.get(i), Toast.LENGTH_LONG).show();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

realm刪除:

try {
                    List<User> users = dao.find();
                    if (users != null && users.size() > 0) {
                        User user = users.get(0);
                        String id = user.getId();
                        dao.delete(id);
                        Toast.makeText(this, "刪除資料", Toast.LENGTH_LONG).show();
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }

realm更新

List<User> users = null;
                try {
                    users = dao.find();
                    if (users != null && users.size() > 0) {
                        NormalUser user = users.get(0).toNormalUser();
                        user.setName("userChange");
                        dao.update(user.toDBUser());
                        Toast.makeText(this,"更新資料"+user, Toast.LENGTH_LONG).show();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

realm查詢

try {
                    List<User> users = dao.find();
                    for(User user:users){
                        Toast.makeText(this,user.toString(), Toast.LENGTH_LONG).show();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

GreenDao

greenDao優點:可和Sqlcipher連用【一直要用sqlcipher,感激】,entity的get,set和構造器自動生成,封裝了dao方法。
缺點:
1.不能繼承!【確切說是不能繼承欄位,繼承欄位不會成為表字段,如果有抽象方法還是可以繼承的】
2.沒有主鍵的時候刪除和更新失敗,不過還好也可以用SQL代替,勉強忍受。必須sql這點太奇怪了,sqlite本身都沒這樣要求啊。對於短的sql語句,操作不方便,還不如直接sql。
3.我用了3+的版本,驚訝的是居然不能升級。。。只能建立臨時表,好無奈,身為資料庫居然不能升級(升級後資料被刪除),只能臨時表拷貝。不過這方法不容易出現數據庫升級失敗的奇怪問題,或許可以沿用。

為什麼都不能繼承欄位呢?完全不懂啊。。。同事用的greendao介紹給我,看到他用的抽象父類抽象公共get,set方法感覺好無奈。。。不然就只能做個介面卡了,一樣的麻煩。實在不行就只能自己想辦法寫一個封裝了,自己寫的只能簡化sql呼叫過程,其他的應該做不到。。。有空再解決吧。

資料庫初始化

ublic class DBManager {
    private static final String PWD = "123";

    private static DaoSession encryptedSession;
    private static final String DB_NAME = "my.db";
    public static DaoSession getSession(){
        if(encryptedSession == null){
            DBHelper dbHelper = new DBHelper(CacheForAndorid.getSuperContext(),DB_NAME,null);
            encryptedSession = new DaoMaster(dbHelper.getEncryptedWritableDb(PWD)).newSession();
        }
        return encryptedSession;
    }

    private static DBUserDao userDao;
    public static DBUserDao getMyDBDao(){
        if(userDao == null){
            userDao = getSession().getDBUserDao();
        }
        return userDao;
    }

    private static DBUserNoKeyDao noKeyDao;
    public static DBUserNoKeyDao getNoKeyDao(){
        if(noKeyDao == null){
            noKeyDao = getSession().getDBUserNoKeyDao();
        }
        return noKeyDao;
    }
}

資料庫更新

public class MigrationHelper {

    private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";
    private static MigrationHelper instance;

    public static MigrationHelper getInstance() {
        if (instance == null) {
            instance = new MigrationHelper();
        }
        return instance;
    }

    public void migrate(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {

        generateTempTables(db, daoClasses);
        DaoMaster.dropAllTables(db, true);
        DaoMaster.createAllTables(db, false);
        restoreData(db, daoClasses);
    }

    /**
     * 生成臨時列表
     *
     * @param db
     * @param daoClasses
     */
    private void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);

            String divider = "";
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList<>();

            StringBuilder createTableStringBuilder = new StringBuilder();

            createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");

            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if (getColumns(db, tableName).contains(columnName)) {
                    properties.add(columnName);

                    String type = null;

                    try {
                        type = getTypeByClass(daoConfig.properties[j].type);
                    } catch (Exception exception) {
                        exception.printStackTrace();
                    }

                    createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);

                    if (daoConfig.properties[j].primaryKey) {
                        createTableStringBuilder.append(" PRIMARY KEY");
                    }

                    divider = ",";
                }
            }
            createTableStringBuilder.append(");");

            db.execSQL(createTableStringBuilder.toString());

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tableName).append(";");

            db.execSQL(insertTableStringBuilder.toString());

        }
    }

    /**
     * 儲存新的資料庫表 以及資料
     *
     * @param db
     * @param daoClasses
     */
    private void restoreData(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) {
        for (int i = 0; i < daoClasses.length; i++) {
            DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);
            String tableName = daoConfig.tablename;
            String tempTableName = daoConfig.tablename.concat("_TEMP");
            ArrayList<String> properties = new ArrayList();

            for (int j = 0; j < daoConfig.properties.length; j++) {
                String columnName = daoConfig.properties[j].columnName;

                if (getColumns(db, tempTableName).contains(columnName)) {
                    properties.add(columnName);
                }
            }

            StringBuilder insertTableStringBuilder = new StringBuilder();

            insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(") SELECT ");
            insertTableStringBuilder.append(TextUtils.join(",", properties));
            insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");

            StringBuilder dropTableStringBuilder = new StringBuilder();
            dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);
            db.execSQL(insertTableStringBuilder.toString());
            db.execSQL(dropTableStringBuilder.toString());
        }
    }

    private String getTypeByClass(Class<?> type) throws Exception {
        if (type.equals(String.class)) {
            return "TEXT";
        }
        if (type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {
            return "INTEGER";
        }
        if (type.equals(Boolean.class)) {
            return "BOOLEAN";
        }

        Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));
        exception.printStackTrace();
        throw exception;
    }

    private List<String> getColumns(Database db, String tableName) {
        List<String> columns = new ArrayList<>();
        Cursor cursor = null;
        try {
            cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);
            if (cursor != null) {
                columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));
            }
        } catch (Exception e) {
            Log.v(tableName, e.getMessage(), e);
            e.printStackTrace();
        } finally {
            if (cursor != null)
                cursor.close();
        }
        return columns;
    }
}
public class DBHelper extends DaoMaster.OpenHelper {

    public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }

    /**
     * 資料庫升級
     * @param db
     * @param oldVersion
     * @param newVersion
     */
    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        //操作資料庫的更新 有幾個表升級都可以傳入到下面
        MigrationHelper.getInstance().migrate(db,DBUserDao.class);
    }

}

bean

有主鍵

@Entity
public class DBUser {
    @Id(autoincrement = true)
    private Long id;
    private String name;
}

無主鍵

@Entity
public class DBUserNoKey {
    private String name;
    private String password;
}

新增資料(dao層不用寫,點選build自動生成)

有無主鍵相同

DBManager.getMyDBDao().insert(user);
        ToastUtil.shorts("儲存成功");

刪除

有主鍵

DBManager.getMyDBDao().delete(user);

無主鍵(必須sql,否則失敗)

DBManager.getSession().getDatabase().execSQL("delete from DBUSER_NO_KEY where name='"+userName+"'");

修改

有主鍵

DBManager.getMyDBDao().update(oldUser);

無主鍵(一樣必須SQL,不sql就bug)

DBManager.getSession().getDatabase()
                    .execSQL("update DBUSER_NO_KEY set password=? where name=?"
                            ,new Object[]{newUser.getPassword(),newUser.getName()});

查詢

有無主鍵相同

Query<DBUserNoKey> query= DBManager.getNoKeyDao().queryBuilder().build();
        List<DBUserNoKey> dbUsers = query.list();
        for(DBUserNoKey dbUser:dbUsers){
            ToastUtil.longs("id="+dbUser.getName()+"\nname="+dbUser.getPassword());
        }

補充,greendao的版本配置在app的build.gradle裡面
greendao {
schemaVersion 3//此處為版本號
daoPackage ‘生成dao等資料庫檔案包名’
targetGenDir ‘src/main/java’
}