(翻譯)又一個(gè)Android Sqlite庫: Cupboard
Tags: android
原文: https://guides.codepath.com/android/Easier-SQL-with-Cupboard
簡介
Cupboard可以用來在應(yīng)用中管理sqlite.作者是 Hugo Visser. 這里是作者對于Cupboard的一些說明.這是一個(gè)輕量級的易于使用的庫,它專為Android平臺設(shè)計(jì),不像ORMlite那樣臃腫.
使用Android自身提供的SQLiteOpenHelper 也能達(dá)到目的,但是往往要寫很多重復(fù)性的代碼.
與大多數(shù)ORM庫相似,Cupboard將java類與數(shù)據(jù)庫的表映射在一起,同時(shí)java類中的屬性對應(yīng)數(shù)據(jù)庫表的列字段.
開始使用
可以下載jar包: https://search.maven.org/remote_content?g=nl.qbusict&a=cupboard&v=LATEST
也可以使用Maven:
<dependency>
<groupId>nl.qbusict</groupId>
<artifactId>cupboard</artifactId>
<version>(insert latest version)</version>
</dependency>
也可以使用Gradle:
compile 'nl.qbusict:cupboard:(insert latest version)'
配置
需要自定義一個(gè)SQLiteOpenHelper.(在接下來的例子中,將會(huì)使用一個(gè)名為"Bunny"的POJO類,意思是兔子)
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import static nl.qbusict.cupboard.CupboardFactory.cupboard;
public class PracticeDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "cupboardTest.db";//文件名
private static final int DATABASE_VERSION = 1;//版本號
public PracticeDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
static {
// 注冊model
cupboard().register(Bunny.class);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 建表
cupboard().withDatabase(db).createTables();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 升級表
// Note that existing columns will not be converted
cupboard().withDatabase(db).upgradeTables();
}
}
然后可以這樣使用:
PracticeDatabaseHelper dbHelper =
new PracticeDatabaseHelper(this);
db = dbHelper.getWritableDatabase();
如何定義Model
定義Model
使用cupboard時(shí),每一個(gè)POJO類都必須包含一個(gè)Long類型的名為"_id"的變量.
public class Bunny {
public Long _id; // for cupboard
public String name; // 兔子的名字
public Integer cuteValue ; // 兔子的可愛程度
public Bunny() {
this.name = "noName";
this.cuteValue = 0;
}
public Bunny(String name, Integer cuteValue) {
this.name = name;
this.cuteValue = cuteValue;
}
}
注意:在這些類中,類名將成為創(chuàng)建的數(shù)據(jù)庫表的名字,變量的名字將成為列字段的名字.所以命名時(shí)注意要遵守SQLite里的命名規(guī)范.
然后需要在之前自定義的DatabaseHelper里進(jìn)行注冊.注冊方法是創(chuàng)建一個(gè)static的代碼塊.
static {
cupboard().register(Bunny.class);
}
使用注解配置字段名
cupboard支持 @Column() 和 @Ignore() 注解.
@Column()可以指定一個(gè)名字作為要?jiǎng)?chuàng)建的列字段的名字.
@Ignore()可以忽略一個(gè)變量,即不參與創(chuàng)建數(shù)據(jù)庫表.
例如:
public class Bunny {
public Long _id; // for cupboard
public String name; // bunny name
@Column("cute_value")
public Integer cuteValue; // bunny cuteness
@Ignore
public Boolean isAwake;
...
增刪改查
創(chuàng)建記錄
// 在數(shù)據(jù)庫中新增一只兔子
Bunny bunny = ...
long id = cupboard().withDatabase(db).put(bunny);
如果bunny對象設(shè)置了"_id"變量的值,那么將會(huì)update指定的行.
如果"_id"變量為null,那么將會(huì)在數(shù)據(jù)庫表中創(chuàng)建新的一行數(shù)據(jù).
兩種情況下,put()函數(shù)都會(huì)返回對應(yīng)的數(shù)據(jù)行的_id的值.
查詢
Bunny bunny = cupboard().withDatabase(db).get(Bunny.class, 12L);
意思是查詢"_id值為12"的Bunny對象.如果結(jié)果不存在,get()將會(huì)返回null.
更復(fù)雜的用法:
//查詢第一個(gè)Bunny對象
Bunny bunny = cupboard().withDatabase(db).query(Bunny.class).get();
// 獲得查詢結(jié)果的Cursor對象
Cursor bunnies = cupboard().withDatabase(db).query(Bunny.class).getCursor();
try {
// 遍歷產(chǎn)尋結(jié)果
QueryResultIterable<Bunny> itr = cupboard().withDatabase(db).query(Bunny.class).iterate();
for (Bunny bunny : itr) {
// do something with bunny
}
} finally {
// 關(guān)閉cursor
itr.close();
}
// 查詢第一個(gè)名字為Max的Bunny對象
Bunny bunny = cupboard().withDatabase(db).query(Bunny.class).withSelection( "name = ?", "Max").get();
修改記錄
只需先查詢出一條數(shù)據(jù),然后修改一些變量的值,然后使用put方法即可更新數(shù)據(jù):
cupboard().withDatabase(db).put(b);
批量地對查詢結(jié)果進(jìn)行修改:
//假設(shè)需求是將名字為"max"的Bunny的名字改成"Max"
ContentValues values = new ContentValues(1);
values.put("name", "Max")
// 批量地對查詢結(jié)果進(jìn)行修改
cupboard().withDatabase(db).update(Book.class, values, "name = ?", "max");
刪除記錄
// 刪除"_id = 12"的記錄
cupboard().withDatabase(db).delete(Bunny.class, 12L);
// 傳入java對象進(jìn)行刪除
cupboard().withDatabase(db).delete(bunny);
// 傳入查詢條件進(jìn)行刪除,例如刪除所有名字為"Max"的Bunny的記錄
cupboard().withDatabase(db).delete(Bunny.class, "name = ?", "Max");
升級數(shù)據(jù)庫
假設(shè)想要給某個(gè)已經(jīng)存在的model增加一個(gè)屬性,或者想要增加一個(gè)新的model.cupboard提供的數(shù)據(jù)庫升級方案如下:
1.給model增加屬性
public class Bunny {
public Long _id; // for cupboard
public String name; // bunny name
public String furColor; // 新增字段,意思是兔毛的顏色
public Integer cuteValue ; // bunny cuteness
...
2.修改AndroidManifest.xml中的數(shù)據(jù)庫的version.通常是加1.
public class PracticeDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "cupboardTest.db";
private static final int DATABASE_VERSION = 2;
...
3.在onUpgrade函數(shù)里寫升級邏輯.你必須考慮到所有的可能性.例如從1升級到3,從2升級到3等等.
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 這行代碼會(huì)嘗試創(chuàng)建新的列或新的表
cupboard().withDatabase(db).upgradeTables();
// 有可能需要給一些新增的列設(shè)置默認(rèn)值
if (newVersion == 2) {
ContentValues cv = new ContentValues();
cv.put("furColor", "black");
cupboard().withDatabase(db).update(Bunny.class, cv);
}
}
例子工程
例子見: https://github.com/koalahamlet/cupboardTestApp/tree/master/app/src/main . 包含了一些基本的增刪改查和升級數(shù)據(jù)庫的例子.
常見問題
Cupboard怎么處理重復(fù)ID的情況?例如,數(shù)據(jù)庫里存放的是twitter上的用戶,每個(gè)用戶的id都是唯一的,我想確保數(shù)據(jù)庫里沒有重復(fù)的twitter用戶的id.有沒有辦法指定某一列是主鍵?
你可以啟用@Index和 @CompositeIndex這兩個(gè)注解,方法是:
Cupboard cupboard = new CupboardBuilder().useAnnotations().build();
這兩個(gè)注解的相關(guān)介紹見: https://bitbucket.org/qbusict/cupboard/wiki/existingData
如何定義某一列的數(shù)據(jù)類型(int或text)? Cupboard 會(huì)自動(dòng)判斷所需的數(shù)據(jù)類型嗎?
字段的類型是由model類的屬性的類型自動(dòng)判斷得出的.
如何存儲(chǔ)日期格式的數(shù)據(jù)?
Cupboard中的日期被存儲(chǔ)為Long類型.
long time = Calendar.getInstance().getTimeInMillis();
獲取時(shí)可以按照自己的需求去格式化.(使用SimpleDateFormat,例如"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
如何表示1對1的關(guān)聯(lián)關(guān)系?
Cupboard不是一個(gè)真正的ORM庫,它不處理關(guān)聯(lián)關(guān)系,否則就會(huì)讓事情變得很復(fù)雜.如果你需要類似級聯(lián)刪除的功能,可以手動(dòng)實(shí)現(xiàn).
如何刪除表中的所有數(shù)據(jù)?
public static void clearAllBunnyData(db) {
db.execSQL("DELETE FROM " + Bunny.class.getSimpleName());
}