(翻译)又一个Android Sqlite库: Cupboard

(翻译)又一个Android Sqlite库: Cupboard

Tags: android
原文: https://guides.codepath.com/android/Easier-SQL-with-Cupboard


简介

Cupboard可以用来在应用中管理sqlite.作者是 Hugo Visser. 这里是作者对于Cupboard的一些说明.这是一个轻量级的易于使用的库,它专为Android平台设计,不像ORMlite那样臃肿.

使用Android自身提供的SQLiteOpenHelper 也能达到目的,但是往往要写很多重复性的代码.

与大多数ORM库相似,Cupboard将java类与数据库的表映射在一起,同时java类中的属性对应数据库表的列字段.

开始使用

可以下载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)'

配置

需要自定义一个SQLiteOpenHelper.(在接下来的例子中,将会使用一个名为"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时,每一个POJO类都必须包含一个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;
    }
}

注意:在这些类中,类名将成为创建的数据库表的名字,变量的名字将成为列字段的名字.所以命名时注意要遵守SQLite里的命名规范.
然后需要在之前自定义的DatabaseHelper里进行注册.注册方法是创建一个static的代码块.

static {
      cupboard().register(Bunny.class);
  }

使用注解配置字段名

cupboard支持 @Column()@Ignore() 注解.

@Column()可以指定一个名字作为要创建的列字段的名字.
@Ignore()可以忽略一个变量,即不参与创建数据库表.

例如:

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;

    ...

增删改查

创建记录

// 在数据库中新增一只兔子
Bunny bunny = ...
long id = cupboard().withDatabase(db).put(bunny);

如果bunny对象设置了"_id"变量的值,那么将会update指定的行.
如果"_id"变量为null,那么将会在数据库表中创建新的一行数据.
两种情况下,put()函数都会返回对应的数据行的_id的值.

查询

Bunny bunny = cupboard().withDatabase(db).get(Bunny.class, 12L);

意思是查询"_id值为12"的Bunny对象.如果结果不存在,get()将会返回null.
更复杂的用法:

//查询第一个Bunny对象
Bunny bunny = cupboard().withDatabase(db).query(Bunny.class).get();
// 获得查询结果的Cursor对象
Cursor bunnies = cupboard().withDatabase(db).query(Bunny.class).getCursor();
try {
  // 遍历产寻结果
  QueryResultIterable<Bunny> itr = cupboard().withDatabase(db).query(Bunny.class).iterate();
  for (Bunny bunny : itr) {
    // do something with bunny
  }
} finally {
  // 关闭cursor
  itr.close();
}
// 查询第一个名字为Max的Bunny对象
Bunny bunny = cupboard().withDatabase(db).query(Bunny.class).withSelection( "name = ?", "Max").get();

修改记录

只需先查询出一条数据,然后修改一些变量的值,然后使用put方法即可更新数据:

cupboard().withDatabase(db).put(b);

批量地对查询结果进行修改:

//假设需求是将名字为"max"的Bunny的名字改成"Max"
ContentValues values = new ContentValues(1);
values.put("name", "Max")
// 批量地对查询结果进行修改
cupboard().withDatabase(db).update(Book.class, values, "name = ?", "max");

删除记录

// 删除"_id = 12"的记录
cupboard().withDatabase(db).delete(Bunny.class, 12L);

// 传入java对象进行删除
cupboard().withDatabase(db).delete(bunny);
// 传入查询条件进行删除,例如删除所有名字为"Max"的Bunny的记录
cupboard().withDatabase(db).delete(Bunny.class, "name = ?", "Max");

升级数据库

假设想要给某个已经存在的model增加一个属性,或者想要增加一个新的model.cupboard提供的数据库升级方案如下:

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中的数据库的version.通常是加1.

public class PracticeDatabaseHelper extends SQLiteOpenHelper {
  private static final String DATABASE_NAME = "cupboardTest.db";
  private static final int DATABASE_VERSION = 2;
  ...

3.在onUpgrade函数里写升级逻辑.你必须考虑到所有的可能性.例如从1升级到3,从2升级到3等等.

public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      // 这行代码会尝试创建新的列或新的表
      cupboard().withDatabase(db).upgradeTables();

      // 有可能需要给一些新增的列设置默认值
      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 . 包含了一些基本的增删改查和升级数据库的例子.

常见问题

Cupboard怎么处理重复ID的情况?例如,数据库里存放的是twitter上的用户,每个用户的id都是唯一的,我想确保数据库里没有重复的twitter用户的id.有没有办法指定某一列是主键?

你可以启用@Index和 @CompositeIndex这两个注解,方法是:

Cupboard cupboard = new CupboardBuilder().useAnnotations().build();

这两个注解的相关介绍见: https://bitbucket.org/qbusict/cupboard/wiki/existingData

如何定义某一列的数据类型(int或text)? Cupboard 会自动判断所需的数据类型吗?

字段的类型是由model类的属性的类型自动判断得出的.

如何存储日期格式的数据?

Cupboard中的日期被存储为Long类型.

long time = Calendar.getInstance().getTimeInMillis();

获取时可以按照自己的需求去格式化.(使用SimpleDateFormat,例如"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")

如何表示1对1的关联关系?

Cupboard不是一个真正的ORM库,它不处理关联关系,否则就会让事情变得很复杂.如果你需要类似级联删除的功能,可以手动实现.

如何删除表中的所有数据?

public static void clearAllBunnyData(db) {
    db.execSQL("DELETE FROM " + Bunny.class.getSimpleName());
}

资料

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

推荐阅读更多精彩内容