美文网首页Android架构程序员Android开发经验谈
面向对象的Android数据库框架OOSqlite

面向对象的Android数据库框架OOSqlite

作者: kkmoving | 来源:发表于2016-01-06 15:52 被阅读1438次

喜欢看代码的,直接上代码
GitHub:https://github.com/kkmoving/OOSqliteApp

在Android应用中,对条目类数据的存储通常会用到数据库,Andriod提供sqlite来实现数据库存储。

应用对数据的使用通常是面向对象的,sqlite对数据的存储并不是面向对象的。这里提供一种面向对象的sqlite数据库框架设计OOSqlite(Object-Oriented Sqlite)。

OOSqlite借鉴J2EE中Hibernate的思想,将数据抽象为Entity,并映射为数据库的一张表,一个Entity的实例就是数据表的一条记录。

先来看看OOSqlite的使用
Define your entity

public class User extends OOSqliteEntity {   
  private static final int NAME_COL_ID = 1;
  private static final int AGE_COL_ID = 2; 

  @Indexing
  @ColumnTag(NAME_COL_ID)    
  String name;  

  @ColumnTag(AGE_COL_ID)
  int age;    

  @Transient    
  boolean choosen;        

  float score;        
}

Create

User.insert(user);

Retrieve

User.query(User.class, null);

Update

User.update(user);

Delete

User.delete(user);

OOSqlite设计思想

一个应用可以包含多个DB,一个DB可以包含多个Table,每个Table由若干字段组成。应用启动时创建DB及其所包含的Table;在应用运行过程中,对Table进行CRUD(增查改删)操作。

OOSqlite的核心是将数据抽象为Entity(实体),并将其注册到框架中,框架根据Entity的定义进行转换,并在DB中创建Table;进行CRUD操作时直接对Entity对象进行操作。

框架将DB抽象为OODatabase,它包含DB名称,版本及其Table列表。同时它是与底层数据库交互的媒介。

Table抽象为OOSqliteTable,包含Table名称,字段列表。它提供CRUD操作接口。

字段抽象为OOColumn,包含字段名称,字段类型,是否建索引等信息。同时OOColumn绑定到Entity的Field,用于Entity字段值的获取和设置,便于Entity和Table之间的转换。

数据抽象OOSqliteEntity,它包含数据库基础字段(如数据库id,last_access等)。业务层数据直接继承OOSqliteEntity,并定义业务字段。

OOSqlite实现

OOSqlite的实现包括:DB和Table的创建,CRUD操作。Table的创建涉及到Entity到Table的转换。

DB和Table的创建

OOSqlite框架通过OOSqliteManager来进行统一管理,提供注册DB和Table的方法,以及初始化数据库的方法。在应用启动时,进行DB和Table的注册,并调用初始化方法进行框架的初始化。

OOSqliteManager在初始化时,会对业务层注册的OODatabase进行初始化,通过实例化sqlite的SQLiteOpenHelper来完成物理层DB的创建。

OODatabase持有SQLiteOpenHelper实例引用,用于底层数据库交互;同时OODatabase持有业务层注册的OOSqliteTable列表,在SQLiteOpenHelper的onCreate回调中完成数据表的创建;在onUpgrade和onDowngrade中完成数据表的升级和降级。

Table创建的关键是获取字段列表,字段列表的获取是把Entity类的成员变量转换为数据库字段,然后组装成sql语句,使用SQLiteOpenHelper回传的SQLiteDatabase执行sql语句,创建物理数据表。

Table的升级和降级可能会进行表结构调整,如增加字段,同样通过获取Entity类的新增字段(新增字段使用注解标识,后面会讲到)。

Entity到Table的转换

Entity到Table有两个转换,一个是创建表时对表结构信息的转换,一个是CRUD操作时Entity对象实例和数据库数据的转换。CRUD操作的转换在CRUD操作中会具体讲到,这里先说创建表时的转换。

创建表的转换其实就是通过反射将Entity类的成员信息转换为数据表字段信息,这个转换会生成一个OOColumn列表,保存在OOSqliteTable中,OOSqliteTable使用OOColumn列表生成创建表的sql语句。

最简单的情况是,将Entity类的成员全部映射为数据表的字段,字段名为成员名(当然,可以做一些命名转换,如转为全小写),字段类型由Java基本数据类型转换为数据库字段类型。(暂时支持Java基本数据类型,也就是说该框架仅支持基本数据类型的存储,不涉及外键的关联存储。对于一般Android应用而言, 这其实是足够的。)

复杂的情况是:

  1. 为字段创建索引
  2. 排除非持久化字段。业务层是直接使用Entity对象实例的,可能会在其中定义一些不需要持久化的字段,在真正的数据表中应该排除这些字段。
  3. 扩展表字段。在数据库升级时,可能会增加表字段。
  4. 条件查询指定列 。业务层仅仅定义了Entity的,在条件查询时需要指定列进行条件查询。

这些都通过注解来实现。定义4种注解:
@ Indexing:标识字段需要创建索引。
@Transient:标识字段为非持久化字段。
@TargetVersion(version):为字段绑定一个版本,在数据库升级到相应版本或之上时,增加该字段。
@ColumnTag(col_id):为字段绑定一个col_id,在条件查询时通过一个col_id对应到指定列。同一个Entity中,不同字段指定不用col_id。

创建表转换时,对于标识@Indexing的字段,生成创建索引的sql语句,创建表时同时创建索引;对于标识@Transient的字段,排除在OOColumn列表之外,也就不创建在表中;对于标识@TargetVersion的字段,在Table升级到对应版本及之后时,增加到表中。

对于标识@ColumnTag的字段,OOSqliteTable会保存col_id到OOColumn的映射,这样就可以通过指定的col_id来指定列,实现条件查询。

CRUD

CRUD操作过程中,OODatabase主要负责向下执行,它持有SQLiteOpenHelper实例用于底层sqlite交互。OOSqliteTable主要负责向上提供面向Entity对象的CRUD操作接口,向下通过OODatabase间接与底层数据库交互,并在数据库数据和Entity对象实例之间进行转换。

OOSqliteTable类似于Hibernate中EntityManager的角色,提供面向对象的CRUD操作接口。但是对于业务层来讲,这个角色是不必要的,业务层只定义了业务Entity,并不关心其他的。因此,这里的实现使用一种更简洁方便的方式,CRUD操作直接通过Entity提供静态方法来实现

为了能够提供静态CRUD接口,框架保存了一个Entity的Class到OOSqliteTable的映射,CRUD操作传入相应的Class即可映射到OOSqliteTable,进行真正的CRUD操作。

插入(Create)

public static void insert(LeSqliteEntity entity)

插入操作只需要传入一个Entity对象。

通过对象的Class找到OOSqliteTable,在其中完成插入操作。OOSqliteTable将Entity对象转换为ContentValues,并调用SQLiteDatabase的insert接口,插入到数据库中。Entity对象转换到ContentValues本质上是将Entity对应的OOColumn列表转换到ContentValues中。

查询(Retrieve)

public static List query(Class cls, String selection) 

查询操作传入Entity的Class和selection,指定要查询的表和条件。
通过Class对应的OOSqliteTable调用SQLiteDatabase
的query接口进行查询,并将查询结果Cursor转换化为OOColumn,创建Entity实例并设置Field值。
另外,Entity基类中封装了常用的条件语句接口,可直接调用作为selection,如equalSelection,likeSelection等。

修改(Update)

 public static int update(LeSqliteEntity entity)

修改操作传入Entity对象。
与插入操作类似,将Entity对象转换成ContentValues后,调用SQLiteDatabase的update接口进行更新操作。不同的是修改操作需要事先判断Entity是否为游离实体(不是从数据库取出的实体为游离实体),如果不是游离实体才能更新到数据库。(很好理解,如果是游离实体,应该是插入操作而不是更新操作)。

删除(Delete)

public static int update(LeSqliteEntity entity)

删除操作传入Entity对象。
OOSqliteTable根据其数据库ID,调用SQLiteDatabase的delete接口,实现删除操作。与更新操作类似,删除操作事先也会判断Entity是否为游离实体。

以上就是面向对象的Android数据库框架的设计与实现。下面讲讲基于这个框架,应用层如何使用数据库。

使用

对于OOSqlite的使用,包括以下几个方面:

  1. 定义业务Entity
  2. 注册业务DB和Table,调用中间件初始化
  3. 调用业务Entity的CRUD操作
  4. 扩展表字段

下面举个例子,以应用需要批量保存用户信息为例。

定义业务Entity

首先我们需要一张表来存储用户信息,先定义实体User继承自OOSqliteEntity。User字段包括:
姓名:name。通常会指定姓名进行查询,因此name是需要创建索引的,另外需要指定name进行查询。
年龄:age
是否选中:choosen。这是业务层需要的临时字段,不需要持久化。
得分:score

 public class User extends OOSqliteEntity {   
      private static final int NAME_COL_ID = 1;
      private static final int AGE_COL_ID = 2; 

      @Indexing
      @ColumnTag(NAME_COL_ID)    
      String name;  

      @ColumnTag(AGE_COL_ID)
      int age;    

      @Transient    
      boolean choosen;        

      float score;        
 }

注册业务DB和Table,中间件初始化

注册DB,DB名称可以随便取,这里叫app.db,版本号定义1。中间件初始化调用LeSqliteManager的初始化接口完成,调用的时机可以在Application的onCreate。

public class DemoApplication extends Application {
    @Override    
    public void onCreate() {
        super.onCreate();        
        DatabaseManager.init(this);    
    }
}

public class DatabaseManager {    

    public static void init(Context context) {        
        OODatabase database = new OODatabase("app.db", 1); 
        database.registerEntity(User.class);

        OOSqliteManager.getInstance().register(database);
        OOSqliteManager.getInstance().initAllDatabase(context);    
    }   
}

Table的注册直接注册Entity的Class即可。如果需要在数据表创建、升级和降级回调中执行业务逻辑,可以实例化OOSqliteTable注册到OODatabase 中, 在OOTableListener中完成实现业务逻辑。
比如,想要在数据表创建后即添加一个初始的用户。

调用OODatabase的registerTable接口:

database.registerEntityWithListener(User.class, User.createListener());

实现OOTableListener,并实例化OOSqliteTable:

public static OOTableListener createListener() {
    return new OOTableListener() {

        @Override
        public void onCreate() {
            User user = new User();
            user.name = "admin";

            User.insert(user);
        }

        @Override
        public void onUpgrade(int oldVersion, int newVersion) {
            if (oldVersion < 2 && newVersion >= 2) {
                User user = new User();
                user.name = "upgrade";

                User.insert(user);
            }
        }

        @Override
        public void onDowngrade(int oldVersion, int newVersion) {

        }

        @Override
        public void onReady() {

        }
    };
}

CRUD操作

添加用户

public static void add(String name, int age) {
    User user = new User();
    user.name = name;
    user.age = age;

    User.insert(user);
}

查询所有用户

同步查询

public static List<User> queryAll() {
    return User.query(User.class, null);
}

异步查询

public static void asyncQueryAll() {
    User.queryAsync(User.class, null, new LeQueryCallback() {
        @Override
        public void onQuerySuccess(List list) {
            
        }
    });
}

按名字查询,并按年年龄升序排序

public static List<User> queryByNameOrderByAge(String name) {
    String selection = User.equalSelection(getNameColumn(), name);
    return User.query(User.class, selection, getAgeColumn(), true);
}

private static OOColumn getNameColumn() {
    return getColumn(User.class, NAME_COL_ID);
}

private static OOColumn getAgeColumn() {
    return getColumn(User.class, AGE_COL_ID);
}

通过@ColumnTag绑定的字段ID找到对应的OOColumn,现使用equalSelection接口生成selection,并指定年龄需要升序排序的列。

自定义查询
查询年龄大于30的用户

public static List<User> queryAgeOlderThan30() {
    OOColumn ageCol = getAgeColumn();
    String selection = ageCol.mName + ">30";

    return User.query(User.class, selection);
}

更新分数

mUser.score += 1;
User.update(mUser);

删除用户

User.delete(mUser);

扩展表字段
前面我们已经生成的数据库,如果需要扩展数据表的字段,比如User需要增加一个是否激活字段,那么只需要在User中再定义@TargetVersion标识的字段,TargetVersion的值定为2,表示数据库升级版本2及之后时,增加该字段。然后,在注册DB时,指定DB的版本为2。
定义active字段

@TargetVersion(2)
boolean active;

初始化时指定DB版本为2

OODatabase database = new OODatabase("app.db", 2);

总结

面向对象的Android数据库中间件的设计和使用就介绍完了。它实现了简单的面向对象数据库操作,对于业务层数据库存储实现来讲非常方便。这里说简单,是因此它仅支持基础数据类型(文本,整型,浮点数据,布尔值,二进制数据)的存储,没有实现外键的关联存储,也不支持Lazy加载。尽管简单,对于大部分的Andriod应用数据库存储需求,已经足够了_

说了这么多,show me the code!!!

GitHub: https://github.com/kkmoving/OOSqliteApp

相关文章

  • 面向对象的Android数据库框架OOSqlite

    喜欢看代码的,直接上代码GitHub:https://github.com/kkmoving/OOSqliteAp...

  • LitePal

    简介 LitePal是一款开源的Android数据库开发框架,采用了对象关系映射(ORM)的模式(即将面向对象的语...

  • 2018-06-23 LitePal

    Android数据库框架 - LitePal LitePal是一款开源的Android数据库框架,它采用了对象关系...

  • Python ORM - pymysql&sqlalchemy

    Python3主要是面向对象的编码风格,访问数据库也可以使用ORM框架来实现面向对象,本文介绍pymysql和sq...

  • GreenDao框架简析

    GreenDao是一款开源的面向Android的轻便、快捷的ORM(对象映射)框架,将Java对象映射到SQLit...

  • 移动架构03-数据库框架

    移动架构03-数据库框架 一、前言 这是一个比GreenDao更小巧、更易用的面向对象数据库框架。 如果你只需要简...

  • 6.5 使用LitePal操作数据库

    LitePal简介 开源的Android数据库框架 采用对象关系映射(ORM),封装常用数据库功能 项目主页:ht...

  • 常用的Android开源库

    常用的Android开源库 1.LitePal LitePal是一款开源的Android数据库框架,采用了对象关系...

  • Android - base - LitePal

    Android 数据库ORM:对象关系映射框架 大纲 LitePal 环境配置 核心类 创建和升级数据库 CRUD...

  • GreenDao的基本使用

    GreenDAO 是一款开源的面向 Android 的轻便、快捷的 ORM 框架,将 Java 对象映射到 SQL...

网友评论

  • a460f11452f3:为什么我定义一张表操作成功,第二张表就不能创了。死活显示找不到该表 :fearful:
    a460f11452f3:@kkmoving public class MsgClass extends OOSqliteEntity {
    public static final int cid = 2;

    @indexing
    @ColumnTag(cid)
    int id;
    String name;
    int type;
    String icon;
    String direction;
    a460f11452f3:@kkmoving OODatabase database = new OODatabase("neighbor.db", 1);
    database.registerEntity(Userinfo.class);
    database.registerEntity(MsgClass.class);//找不到这个表
    OOSqliteManager.getInstance().register(database);
    OOSqliteManager.getInstance().initAllDatabase(context);
    kkmoving:@大树底下的苹果 定义之后有注册么(database.registerEntity),或者你把代码给我看下
  • 匹诺曹pino:很轻量 操作方便~赞一个
    kkmoving:@匹诺曹pino 😊
  • 7de928b3e4a2:是否是基于java反射机制的实现呀
    kkmoving: @fmworld 是的
  • 曾樑:Cool

本文标题:面向对象的Android数据库框架OOSqlite

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