Android最好用的数据库框架——DBFLOW全面解析

前言

为什么说DBFLOW是最好用的数据库框架?因为它综合了各个数据库的框架的优点,它比GreenDao要使用简单,同时又比ActiveAndroid性能高很多,它是一款操作简单又高效的ORM框架。

初始DBFLOW

官方介绍

根据官方介绍可以知道,DBFLOW是一个为Android设计的简单高效的数据库类库,它基于注解在编程过程中生成操作类,性能高效,操作安全。

DBFLOW正确使用姿势

DBFLOW配置(build.gradle版本3.0以下)

引入apt和maven,配置在工程下gradle里面

  repositories {
    jcenter()
  }
  dependencies {
    classpath 'com.android.tools.build:gradle:2.0.0-beta6'
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

allprojects {
  repositories {
    jcenter()
    maven { url "https://jitpack.io" }
  }

在app的gradle下配置apply plugin: 'com.neenbedankt.android-apt',同时导入依赖包

apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
def dbflow_version = "3.0.0-beta4"
android {
  compileSdkVersion 23
  buildToolsVersion "23.0.2"

  defaultConfig {
    applicationId "cn.taoweiji.dbflowexample"
    minSdkVersion 14
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"
  }
  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }
}

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  apt "com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}"
  compile "com.github.Raizlabs.DBFlow:dbflow-core:${dbflow_version}"
  compile "com.github.Raizlabs.DBFlow:dbflow:${dbflow_version}"
}

以上就是build.gradle版本3.0以下的DBFLOW的配置方式,基本上就是各种导包
--------------------------------------分割线-----------------------------------------
在Android Studio3.0以上版本、build.gradle版本3.0以上,已经把apt的注解方式修改为annotationProcessor,对应在配置也发生了变化,不需要再配置apt相关的设置
build.gradle版本3.0以上DBFLOW相关配置
工程下的gradle文件

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.0'
//        不需要配置apt
//        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://www.jitpack.io" }
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

app下的gradle文件

apply plugin: 'com.android.application'
def dbflow_version = "4.2.1"
android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.kaka.dbflowdemo"
        minSdkVersion 18
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}"
    implementation "com.github.Raizlabs.DBFlow:dbflow-core:${dbflow_version}"
    implementation "com.github.Raizlabs.DBFlow:dbflow:${dbflow_version}"
    // sql-cipher database encyrption (optional)
    implementation "com.github.Raizlabs.DBFlow:dbflow-sqlcipher:${dbflow_version}"

}

建议使用annotationProcessor这种注解方法,不仅是配置简单,而且Android Studio对这种注解方式也进行了优化。

初始化DBFLOW

public class MyApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        //初始化DBFLOW
        FlowManager.init(this);
    }
}

别忘了在清单文件里面把application修改为当前Application

创建数据库

@Database(name = AppDataBase.NAME,version = AppDataBase.VERSION)
public class AppDataBase {
    public static final String NAME="AppDataBase";
    public static final int VERSION=1;
}

创建一个类,需要声明一下数据库的NAME和VERSION,该@Database注释生成一个DatabaseDefinition现在引用名为“AppDatabase.db”的文件中的磁盘上的SQLite数据库。你可以在代码中引用它,DatabaseDefinition db = FlowManager.getDatabase(AppDatabase.class);

创建表

两种方式

第一种(不推荐)

@Table(database = AppDataBase.class)
public class User {
    @PrimaryKey
    UUID id;

    @Column
    String name;

    @Column
    int age;
}

这种是最简单的方式,创建User类,通过注解来声明对应的数据库字段,@PrimaryKey是主键,@Column是字段的意思
第二种(推荐)

@Table(database = AppDataBase.class)
public class User2Model extends BaseModel {

    @PrimaryKey(autoincrement = true)
    private int id;

    @Column
    private String name;

    @Column
    private int age;

    @Column
    private long timeStamp;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public long getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(long timeStamp) {
        this.timeStamp = timeStamp;
    }
}

使用这种方法创建表我们推荐,继承了BaseModel以后会更好更方便地让我们操作数据,代码更加简洁。
注:不要重写构造方法,不要重写构造方法,不要重写构造方法;重要的事情说三遍。

CRUD基本操作

第一种创建表的方式的操作

        User user=new User();
        user.id= UUID.randomUUID();
        user.name="kaka";
        user.age=18;
        ModelAdapter<User> adapter=FlowManager.getModelAdapter(User.class);
        //插入User
        adapter.insert(user);
        //修改名字
        user.name = "Test";
        adapter.update(user);
        User userQuery=SQLite.select().from(User.class).querySingle();
        Log.e(TAG,"id="+userQuery.id+",name="+userQuery.name+",age="+userQuery.age);

第二种创建表的方式

        User2Model userModel=new User2Model();
        userModel.setName("UserModel");
        userModel.setAge(new Random().nextInt(100));
        userModel.save();
        userModel.update();
        userModel.delete();

从代码也可以看出第二种方式的代码更加简洁,打开BaseModel源码看一眼里面重写了getModelAdapter方法

public ModelAdapter getModelAdapter() {
        if (modelAdapter == null) {
            modelAdapter = FlowManager.getModelAdapter(getClass());
        }
        return modelAdapter;
    }

跟第一种实现的方式ModelAdapter<User> adapter=FlowManager.getModelAdapter(User.class);其实是一样的,只不过更简单一些。
查询:
DBFLOW在查询方法提高了很强大的支持,无论你是要排列、分组还是各种条件查询都可以满足

 List<User2Model> user2Models=SQLite.select().from(User2Model.class).where(User2Model_Table.age.greaterThan(18),User2Model_Table.name.eq("UserModel"))
                .orderBy(OrderBy.fromNameAlias(NameAlias.of("id")))
                .groupBy(NameAlias.of("id"))
                .queryList();
        if (user2Models.size()!=0){
            for (User2Model user2Model:user2Models){
                Log.e(TAG,"id="+user2Model.getId()+",name="+user2Model.getName()+",age="+user2Model.getAge());
            }
        }else{
            Toast.makeText(this,"数据为0",Toast.LENGTH_SHORT).show();
        }

看到这样小伙伴们可能要问了User2Model_Table这个是个什么鬼,之前在对DBFLOW介绍的时候也说到了,DBFLOW是根据注解在编译过程生成对应的操作类,在你建好表(User2Model)以后,Make Project一下(或者ReBuild只要让代码重新编译一下)就会生成对应User2Model_Table操作类对象,这个XXX_Table操作对象在条件搜索过程都会被用到。

事务操作、批量操作

事务操作是我们数据库不得不去了结,DBFLOW当然也支持了事务的相关操作。

 FlowManager.getDatabase(AppDataBase.class).executeTransaction(new ITransaction() {
            @Override
            public void execute(DatabaseWrapper databaseWrapper) {
                for (int i=0;i<100;i++){
                    User2Model userModel=new User2Model();
                    userModel.setName("UserModel");
                    userModel.setAge(new Random().nextInt(100));
                    userModel.save(databaseWrapper);
                }
            }
        });

这种是同步事务的方法,虽然DBFLOW执行的速度非常快,效率也很好,但是我们不推荐,因为当我们批量操作数据越来越大的时候,同步事务就会变成一个耗时的操作,而耗时操作我们要放在子线程里执行,这样就需要异步执行事务的方法

//异步事务
        FlowManager.getDatabase(AppDataBase.class).beginTransactionAsync(new ITransaction() {
            @Override
            public void execute(DatabaseWrapper databaseWrapper) {
                for (int i=0;i<100;i++){
                    User2Model userModel=new User2Model();
                    userModel.setName("UserModel");
                    userModel.setAge(new Random().nextInt(100));
                    userModel.save(databaseWrapper);
                }
            }
        }).success(new Transaction.Success() {
            @Override
            public void onSuccess(@NonNull Transaction transaction) {
                Log.e(TAG,"onSuccess()");
            }
        }).error(new Transaction.Error() {
            @Override
            public void onError(@NonNull Transaction transaction, @NonNull Throwable error) {
                Log.e(TAG,"onError()");
            }
        }).build().execute();

一波非常简洁的链式操作,并且提供好了事务操作成功和失败的回调

数据库升级(增加表、修改表字段等)

如果要增加表,除了要创建表以外,还要修改数据库版本号,如果没有修改数据库版本号,那么再调表对象的时候就会报出一个经典错误,no such table,但凡报这个错误都是你数据版本号没有修改。
如果要修改表的字段或者增加字段,相对来说稍微麻烦一点。DBFLOW专门提供了AlterTableMigration<T>这个类来修改表相关的资料。以User2Model这个表为例,假如说我们要增加一个字段timeStamp是long类型的,不仅改数据库版本号,同时在类对象中增加 @Column
private long timeStamp; 字段以外,还需要增加咱们刚才提到的AlterTableMigration的子类来修改

@Migration(version = AppDataBase.VERSION,database = AppDataBase.class)
public class Migration1 extends AlterTableMigration<User2Model> {
    public Migration1(Class<User2Model> table) {
        super(table);
    }

    @Override
    public void onPreMigrate() {
        super.onPreMigrate();
        addColumn(SQLiteType.INTEGER,User2Model_Table.timeStamp.getNameAlias().name());
    }
}

继承AlterTableMigration重写onPreMigrate,在里面写你需要
修改表的操作,这里我们需要增加一个字段addColumn(SQLiteType.INTEGER,User2Model_Table.timeStamp.getNameAlias().name()); 这样就ok了

总结

DBFLOW是一个很强大的ORM框架,不仅有文中提到这些基本操作,它还支持Kotlin语言、RxJava以及各种高级用法,不过这些一般在我们客户端使用的情况不多,这里不作为重点介绍,感兴趣的小伙伴可以看一下官网的介绍https://agrosner.gitbooks.io/dbflow/content/StoringData.html

最后的最后放上我的github地址:https://github.com/kaka10xiaobang/DBFLOW_DEMO

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,637评论 18 139
  • 在年轻的时候,选择一次跨国长途旅行,让自己在旅途中观看世界,认识自我,明白自己到底需要什么,这一年就叫做“间隔年”...
    大象熊猫阅读 208评论 2 1
  • 喜欢一个人的感觉是什么样的?或许从没有什么固定守恒的标准答案:只是因为你是你,不是别人;只是因为有聊不完的共同话题...
    纠缠的离骚阅读 481评论 0 3
  • 轩辕烈3轩辕烈和轩辕灵儿,两人青梅走马一起长大。轩辕霸也很宠爱轩辕灵儿。但他却不想放过轩辕鸿 “灵儿,起来,不要去...
    老祝读书阅读 316评论 3 2
  • 在我们这个快速发展的时代,我相信大家对于心理学也不陌生,在我们身边有各种形形色色的书《心理学教你成功之道》,《教...
    夏木波比阅读 1,040评论 5 10