Android 你必须了解的ORM框架GreenDao

作者: 肖赛Soaic | 来源:发表于2016-12-13 16:46 被阅读1110次

    简介

    greendao是一个开源的Android开发ORM使SQLite数据库,可以减轻和节省开发时间的同时处理低级别的数据库需求。SQLite是一个很好的嵌入式关系数据库,不过写SQL解析查询结果是相当繁琐和耗时的,greendao可以让你利用java对象来映射到数据库表便捷的使用Java面相对象的API来处理增、删、改、查的操作。

    特点

    1. 性能大(貌似是Android最快的ORM)
    2. 易用性高
    3. 内存消耗极小
    4. 精简的库(小于100KB)
    5. 安全性高(支持SQLCipher来保持用户的数据安全)
    6. 支持RxJava

    资料

    1. 官网:<u>http://greenrobot.org/greendao/</u>
    2. github:<u>https://github.com/greenrobot/greenDAO</u>

    配置环境

    在build.gradle中配置

    apply plugin: 'com.android.library'  
    //应用greendao插件
    apply plugin: 'org.greenrobot.greendao'        
    
    android {
        compileSdkVersion 25
        buildToolsVersion "25.0.0"
        defaultConfig {
            minSdkVersion 15
            targetSdkVersion 25
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    //greendao相关配置
    greendao {
        schemaVersion 1                 //数据库版本号
        daoPackage 'com.greendao.dao'    //greendao自动生成的dao文件包名
        targetGenDir 'src/main/java'    //greendao自动生成的目标文件夹
    }
    
    //从maven中下载greendao插件
    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
        }
    }
    
    //添加greendao需要的编译环境
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:25.0.0'
        compile 'com.android.support:recyclerview-v7:25.0.0'
        compile 'org.greenrobot:greendao:3.2.0'
        compile 'org.greenrobot:greendao-generator:3.2.0'
        compile 'io.reactivex:rxandroid:1.2.1'
        compile 'io.reactivex:rxjava:1.1.6'
    }
    

    然后点击Sync Project with Gradle Files按钮下载编译环境,等待下载完成,环境配置就OK了。

    开始

    GreenDao3.0以后开始用注解配置数据表,使用起来非常快捷方便,下面代码简单的配置了User表,表里面有id和name两个字段

    @Entity
    public class User{
        @Id
        private Long id;
    
        @NotNull
        private String name;
    }
    

    然后用Build里面的Make Project或Make Module来自动生成相关DAO类和get set方法,完成之后,在我们定义好的com.greendao.dao包下也会多出DaoMaster、DaoSession、UserDao三个类,代码这里就不贴出来了,User实体会生成如下:

    @Entity
    public class User{
    
        @Id
        private Long id;
        
        @NotNull
        private String name;
    
        @Generated(hash = 1709734220)
        public User(Long id, @NotNull String name) {
            this.id = id;
            this.name = name;
        }
    
        @Generated(hash = 586692638)
        public User() {
        }
    
        public Long getId() {
            return this.id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return this.name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
    }
    

    <b>数据表注解@Entity的详细配置</b>

    @Entity(
        // 目前还不是很清楚这个字段的功能和意义
        schema = "myschema",
        
        // 是否需要更新、删除属性和刷新方法,默认为true
        active = true,
        
        //指定数据库中的表的名称。默认是实体类的名称。
        nameInDb = "AWESOME_USERS",
        
        // 定义跨越多个列的索引。
        indexes = {
                @Index(value = "name DESC", unique = true)
        },
        
        // 默认为true,创建数据表,如果有多个实体映射到一个表或表创建在greenDao之外,则设置false
        createInDb = false,
    
        // 是否生成一个所有属性的构造函数,无参构造函数默认生成
        generateConstructors = true,
    
        // 是否自动生成get和set方法
        generateGettersSetters = true
    )
    public class User {
      ...
    }
    

    注意:如果定义了 schema="myschema",会报如下错误,具体解决方法暂时还没找到,有知道的小伙伴可以留言告知一下哦

    * What went wrong:
    Execution failed for task ':greendaolibrary:greendao'.
    > Undefined schema \"myschema\" (referenced in entities: User).
    Please, define non-default schemas explicitly inside build.gradle
    

    <b>各个字段的详细注解</b>

    @Id 主键ID注解,属性一般long或Long来定义,如果要设该字段为自增属性,那么可以定义为@Id(autoincrement = true)

    @Property 字段名定义注解,@Property(nameInDb = "USER_NAME"),默认是驼峰法代替下划线,所有字母大写,如 customName 变成 CUSTOM_NAME

    @NotNull 字段不能为空注解

    @Transient 字段不添加到数据表注解

    @Index 字段定义为索引注解,@Index(unique = true)索引添加唯一约束,@Index(name = "indexName")定义索引名

    @Unique 字段定义唯一约束注解,并会创建索引

    封装

    定义DbCore.java类来获取全局唯一的DaoSession对象,代码如下:

    public class DbCore {
        private static final String DEFAULT_DB_NAME = "green_dao.db";
        private static DaoMaster daoMaster;
        private static DaoSession daoSession;
    
        private static Context mContext;
        private static String DB_NAME;
    
        public static void init(Context context) {
            init(context, DEFAULT_DB_NAME);
        }
    
        public static void init(Context context,String dbName) {
            if (context == null) {
                throw new IllegalArgumentException("context can't be null");
            }
            mContext = context.getApplicationContext();
            DB_NAME = dbName;
        }
    
        public static DaoMaster getDaoMaster() {
            if (daoMaster == null) {
                OnlineOpenHelper helper = new OnlineOpenHelper(mContext, DB_NAME, null);
                daoMaster = new DaoMaster(helper.getWritableDatabase());
            }
            return daoMaster;
        }
    
        public static DaoSession getDaoSession() {
            if (daoSession == null) {
                if (daoMaster == null) {
                    daoMaster = getDaoMaster();
                }
                daoSession = daoMaster.newSession();
            }
            return daoSession;
        }
    
        public static void enableQueryBuilderLog(){
            QueryBuilder.LOG_SQL = true;
            QueryBuilder.LOG_VALUES = true;
        }
    }
    

    OnlineOpenHelper升级帮助类

    public class OnlineOpenHelper extends DaoMaster.OpenHelper{
        public OnlineOpenHelper(Context context,String name) {
            super(context, name);
        }
    
        public OnlineOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory) {
            super(context, name, factory);
        }
    
        @Override
        public void onUpgrade(Database db,int oldVersion,int newVersion) {
            Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion );
        }
    }
    

    需在在application中初始化

    DbCore.init(this);                 //初始化GreenDao
    DbCore.enableQueryBuilderLog();    //打印sql语句日志
    

    封装所有的增删改查、获取Dao、获取RxDao、获取QueryBuilder等方法 到BaseService.java

    public class BaseService<T, K>{
        private AbstractDao<T, K> mDao;
    
        public BaseService(AbstractDao dao) {
            mDao = dao;
        }
    
        public void save(T item) {
            mDao.insert(item);
        }
    
        public void save(T... items) {
            mDao.insertInTx(items);
        }
    
        public void save(List<T> items) {
            mDao.insertInTx(items);
        }
    
        public void saveOrUpdate(T item) {
            mDao.insertOrReplace(item);
        }
    
        public void saveOrUpdate(T... items) {
            mDao.insertOrReplaceInTx(items);
        }
    
        public void saveOrUpdate(List<T> items) {
            mDao.insertOrReplaceInTx(items);
        }
    
        public void deleteByKey(K key) {
            mDao.deleteByKey(key);
        }
    
        public void delete(T item) {
            mDao.delete(item);
        }
    
        public void delete(T... items) {
            mDao.deleteInTx(items);
        }
    
        public void delete(List<T> items) {
            mDao.deleteInTx(items);
        }
    
        public void deleteAll() {
            mDao.deleteAll();
        }
    
        public void update(T item) {
            mDao.update(item);
        }
    
        public void update(T... items) {
            mDao.updateInTx(items);
        }
    
        public void update(List<T> items) {
            mDao.updateInTx(items);
        }
    
        public  T query(K key) {
            return  mDao.load(key);
        }
    
        public List<T> queryAll() {
            return mDao.loadAll();
        }
    
        public List<T> query(String where,String... params) {
    
            return mDao.queryRaw(where, params);
        }
    
        public QueryBuilder<T> queryBuilder() {
    
            return mDao.queryBuilder();
        }
    
        public long count() {
            return mDao.count();
        }
    
        public void refresh(T item) {
            mDao.refresh(item);
    
        }
    
        public void detach(T item) {
            mDao.detach(item);
        }
    
        public AbstractDao getDao(){
            return mDao;
        }
    
        public RxDao<T,K> getRxDao(){
            return  mDao.rx();
        }
    
        public QueryBuilder<T> getQueryBuilder(){
            return  mDao.queryBuilder();
        }
        
    }
    

    封装User表的所有操作到UserService.java类中,如后面有多个表,则只需简单继承BaseService如下即可,后面,所有相关User操作只需通过UserService.getInstance()来实现所有操作

    public class UserService extends BaseService<User,Long>{
    
        public UserService() {
            super(DbCore.getDaoSession().getUserDao());
        }
        
        public static UserService getInstance(){
            return SingleLoader.INSTANCE;
        }
        
        private static class SingleLoader{
            final static UserService INSTANCE = new UserService();
        }
    
    }
    

    使用

    <b>增</b>

    public void insert(){
        String userName = user_name.getText().toString();
        if(TextUtils.isEmpty(userName)){
           return; 
        }
        User user = new User(null,userName);
    
        //UserService.getInstance().save(user);                 //方式一
        //UserService.getInstance().getDao().save(user);        //方式二
        UserService.getInstance().getRxDao().insert(user)        //方式三 Rx模式
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<User>() {
                    @Override
                    public void call(User note) {
                        search();
                    }
                });
    }
    

    <b>删</b>

    public void delete(){
        if(mDatas.size()<=0){
            return;
        }
        User user = mDatas.get(0);
        //UserService.getInstance().delete(user);
        //UserService.getInstance().getDao().delete(user);
        UserService.getInstance().getRxDao().delete(user)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Void>(){
                    @Override
                    public void call(Void aVoid){
                        search();
                    }
                });
    }
    

    <b>改</b>

    public void update(){
        String userName = user_name.getText().toString();
        if(TextUtils.isEmpty(userName)){
            return;
        }
        User user = mDatas.get(0);
        user.setName(userName);
        //UserService.getInstance().update(user);
        //UserService.getInstance().getDao().update(user);
        UserService.getInstance().getRxDao().update(user)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<User>(){
                    @Override
                    public void call(User user){
                        search();
                    }
                });
    }
    

    <b>查</b>

    ID降序查询所有数据

    public void search() {
        //List<User> users = UserService.getInstance().getQueryBuilder().orderDesc(UserDao.Properties.Id).build().list();
        UserService.getInstance().getQueryBuilder().orderDesc(UserDao.Properties.Id).rx().list()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<User>>(){
                    @Override
                    public void call(List<User> users){
                        mDatas.clear();
                        for(int i = 0;i<users.size();i++){
                            User user = users.get(i);
                            mDatas.add(user);
                        }
                        adapter.notifyDataSetChanged();
                    }
                });
    }
    

    用Rx模式模糊查询

    public void searchByName() {
        String userName = user_name.getText().toString();
        if(TextUtils.isEmpty(userName)){
            return;
        }
        //方式一
        //List<User> users = UserService.getInstance().getDao().queryBuilder().where(UserDao.Properties.Name.like("%"+userName+"%")).build().list();
        //方式二
        //List<User> users = UserService.getInstance().getDao().queryBuilder().where(new WhereCondition.PropertyCondition(UserDao.Properties.Name, " LIKE ?", "%"+userName+"%")).build().list();
        //方式三
        UserService.getInstance().getQueryBuilder().where(new WhereCondition.PropertyCondition(UserDao.Properties.Name, " LIKE ?", "%"+userName+"%")).rx().list()
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<List<User>>(){
                    @Override
                    public void call(List<User> users){
                        mDatas.clear();
                        for(int i = 0;i<users.size();i++){
                            User user = users.get(i);
                            mDatas.add(user);
                        }
                        adapter.notifyDataSetChanged();
                    }
                });
    }
    

    升级

    <b>字段更新</b>
    这里借鉴了国外大牛写的一个工具类,代码如下:

    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);
        }
    
        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();
                            //Crashlytics.logException(exception);
                        }
    
                        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());
            }
        }
    
        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()));
            //Crashlytics.logException(exception);
            throw exception;
        }
    
        private static 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;
        }
    }
    

    具体应用只需要在OnlineOpenHelperonUpgrade方法中如下使用

    switch(oldVersion){
        //User表添加字段或删除字段更新
        case 1: MigrationHelper.getInstance().migrate(db,UserDao.class);
    }
    

    <b>新增数据表</b>

    @Entity
    public class Role{
    
        @Id
        private Long id;
        
        @NotNull
        private String roleName;
    }
    

    在OnlineOpenHelper的onUpgrade方法添加RoleDao.createTable(db,false),如下:

    switch(oldVersion){
        //User表添加字段或删除字段更新
        case 1: MigrationHelper.getInstance().migrate(db,UserDao.class);
        //第二个参数true or false 是否需要检查是否存在该表
        case 2: RoleDao.createTable(db,false);
    }
    

    到这里greenDao简单的操作处理都基本OK了!

    github地址

    写在最后的话:很多事情都从小事做起,坚持不懈,那么最终你也会从小牛变成大牛的。

    相关文章

      网友评论

      • 戴定康:Please, define non-default schemas explicitly inside build.gradle 这个问题解决了吗
      • Holyn:兄弟,定义了 schema="myschema"会错,这问题有解决思路了吗?
        kapaseker:https://github.com/greenrobot/greenDAO/issues/356
        我不建议使用这个annotation的方式做数据库,schema其实就是不同的数据库,但是目前的annotation方式压根儿不支持这个,所以还是别用这个annotation了,就用generator吧
        肖赛Soaic:@浩子_Android 还没,没有深入去研究这个
      • 时光不止我爱她:那个升级类调用之后还是提示没有增加字段
        肖赛Soaic:@时光不止我爱她 我这边测试都是OK的,你再好好看看咯
        时光不止我爱她:@肖赛Soaic 我增加了
        肖赛Soaic:@时光不止我爱她 数据库版本号还需要加一
      • stareme:学习了
      • 时光不止我爱她:除了rxjava的其他的是UI线吗?
        肖赛Soaic:@时光不止我爱她 数据库操作是没开线程的,如果是操作数据量大,建议开一个线程来处理,可能GreenDao还没适配到rxjava2.0
        时光不止我爱她:@肖赛Soaic 我是想问,数据库操作是线程的吗?还是我要自己开线程?我用的rxjava2.0、不能用它那个rxdao
        肖赛Soaic:@时光不止我爱她 其它的在非UI线程也可以访问

      本文标题:Android 你必须了解的ORM框架GreenDao

      本文链接:https://www.haomeiwen.com/subject/pecymttx.html