Android 你必須了解的ORM框架GreenDao

簡介

greendao是一個開源的Android開發(fā)ORM使SQLite數(shù)據(jù)庫役拴,可以減輕和節(jié)省開發(fā)時間的同時處理低級別的數(shù)據(jù)庫需求祭隔。SQLite是一個很好的嵌入式關(guān)系數(shù)據(jù)庫妆丘,不過寫SQL解析查詢結(jié)果是相當(dāng)繁瑣和耗時的逝她,greendao可以讓你利用java對象來映射到數(shù)據(jù)庫表便捷的使用Java面相對象的API來處理增兢孝、刪窿凤、改、查的操作跨蟹。

特點

  1. 性能大(貌似是Android最快的ORM)
  2. 易用性高
  3. 內(nèi)存消耗極小
  4. 精簡的庫(小于100KB)
  5. 安全性高(支持SQLCipher來保持用戶的數(shù)據(jù)安全)
  6. 支持RxJava

資料

  1. 官網(wǎng):<u>http://greenrobot.org/greendao/</u>
  2. github:<u>https://github.com/greenrobot/greenDAO</u>

配置環(huán)境

在build.gradle中配置

apply plugin: 'com.android.library'  
//應(yīng)用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相關(guān)配置
greendao {
    schemaVersion 1                 //數(shù)據(jù)庫版本號
    daoPackage 'com.greendao.dao'    //greendao自動生成的dao文件包名
    targetGenDir 'src/main/java'    //greendao自動生成的目標(biāo)文件夾
}

//從maven中下載greendao插件
buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.0'
    }
}

//添加greendao需要的編譯環(huán)境
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按鈕下載編譯環(huán)境雳殊,等待下載完成,環(huán)境配置就OK了窗轩。

開始

GreenDao3.0以后開始用注解配置數(shù)據(jù)表夯秃,使用起來非常快捷方便痢艺,下面代碼簡單的配置了User表仓洼,表里面有id和name兩個字段

@Entity
public class User{
    @Id
    private Long id;

    @NotNull
    private String name;
}

然后用Build里面的Make Project或Make Module來自動生成相關(guān)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>數(shù)據(jù)表注解@Entity的詳細(xì)配置</b>

@Entity(
    // 目前還不是很清楚這個字段的功能和意義
    schema = "myschema",
    
    // 是否需要更新、刪除屬性和刷新方法国撵,默認(rèn)為true
    active = true,
    
    //指定數(shù)據(jù)庫中的表的名稱陵吸。默認(rèn)是實體類的名稱。
    nameInDb = "AWESOME_USERS",
    
    // 定義跨越多個列的索引介牙。
    indexes = {
            @Index(value = "name DESC", unique = true)
    },
    
    // 默認(rèn)為true,創(chuàng)建數(shù)據(jù)表壮虫,如果有多個實體映射到一個表或表創(chuàng)建在greenDao之外,則設(shè)置false
    createInDb = false,

    // 是否生成一個所有屬性的構(gòu)造函數(shù)耻瑟,無參構(gòu)造函數(shù)默認(rèn)生成
    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>各個字段的詳細(xì)注解</b>

@Id 主鍵ID注解谆构,屬性一般long或Long來定義裸扶,如果要設(shè)該字段為自增屬性,那么可以定義為@Id(autoincrement = true)

@Property 字段名定義注解搬素,@Property(nameInDb = "USER_NAME")呵晨,默認(rèn)是駝峰法代替下劃線,所有字母大寫熬尺,如 customName 變成 CUSTOM_NAME

@NotNull 字段不能為空注解

@Transient 字段不添加到數(shù)據(jù)表注解

@Index 字段定義為索引注解摸屠,@Index(unique = true)索引添加唯一約束,@Index(name = "indexName")定義索引名

@Unique 字段定義唯一約束注解粱哼,并會創(chuàng)建索引

封裝

定義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如下即可桑嘶,后面,所有相關(guān)User操作只需通過UserService.getInstance()來實現(xiàn)所有操作

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降序查詢所有數(shù)據(jù)

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

具體應(yīng)用只需要在OnlineOpenHelperonUpgrade方法中如下使用

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

<b>新增數(shù)據(jù)表</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);
    //第二個參數(shù)true or false 是否需要檢查是否存在該表
    case 2: RoleDao.createTable(db,false);
}

到這里greenDao簡單的操作處理都基本OK了逃顶!

github地址

寫在最后的話:很多事情都從小事做起,堅持不懈充甚,那么最終你也會從小牛變成大牛的以政。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市津坑,隨后出現(xiàn)的幾起案子妙蔗,更是在濱河造成了極大的恐慌傲霸,老刑警劉巖疆瑰,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異昙啄,居然都是意外死亡穆役,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門梳凛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來耿币,“玉大人,你說我怎么就攤上這事韧拒⊙徒樱” “怎么了十性?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長塑悼。 經(jīng)常有香客問我劲适,道長,這世上最難降的妖魔是什么厢蒜? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任霞势,我火速辦了婚禮,結(jié)果婚禮上斑鸦,老公的妹妹穿的比我還像新娘愕贡。我一直安慰自己,他們只是感情好巷屿,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布固以。 她就那樣靜靜地躺著,像睡著了一般嘱巾。 火紅的嫁衣襯著肌膚如雪嘴纺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天浓冒,我揣著相機(jī)與錄音栽渴,去河邊找鬼。 笑死稳懒,一個胖子當(dāng)著我的面吹牛闲擦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播场梆,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼墅冷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了或油?” 一聲冷哼從身側(cè)響起寞忿,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎顶岸,沒想到半個月后腔彰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡辖佣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年霹抛,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片卷谈。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡杯拐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情端逼,我是刑警寧澤朗兵,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站顶滩,受9級特大地震影響矛市,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诲祸,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一浊吏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧救氯,春花似錦找田、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至甲抖,卻和暖如春漆改,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背准谚。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工挫剑, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人柱衔。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓樊破,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唆铐。 傳聞我的和親對象是個殘疾皇子哲戚,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,734評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)艾岂,斷路器顺少,智...
    卡卡羅2017閱讀 134,628評論 18 139
  • 第一感謝死,讓我 得以生王浴。 其次脆炎,感謝可惡的肉體, 作我靈魂的沃壤叼耙。 還要感謝每天從1.5億公里外 趕來的太陽——...
    河南張鵬閱讀 256評論 0 4
  • 再見面以為大家會因為離別而依依不舍腕窥,緊緊相擁粒没,淚流滿面筛婉,當(dāng)她們出現(xiàn)在我的視野里時,卻莫名其妙的感到陌生。 ...
    因為女子閱讀 176評論 0 0
  • 每天8杯水:傷了腎哨毁!五谷雜糧粉:吃出糖尿病源武!每天2萬步:毀了膝蓋扼褪!亂補(bǔ)維生素:吃出結(jié)石!辟谷養(yǎng)生:毀了腸胃粱栖!胡亂進(jìn)...
    閱讀中醫(yī)閱讀 405評論 0 0