首页 > 代码库 > Android ORM——初识greenDAO 3及使用greenDAO 3前应该掌握的一些知识点(一)

Android ORM——初识greenDAO 3及使用greenDAO 3前应该掌握的一些知识点(一)

引言

总所周知,SQLite——内嵌于Android中一个占用内存极小的关系型,作为我们Android存储领域中重要的一员 ,或多或少都曾接触到数据库。即使Android系统中提供了许多操作SQLite的API,但是在业务开发中还是需要手动去编写原生SQL语句,这不仅复杂、不好维护,更重要的是不高效,常常会因为SQL语句写错而增加了开发成本,于是便出现了ORM(对象关系映射)框架,简化了编写SQL语句的开发工作,其中比较著名的有GreenDao、OrmLite、Litepal等。

一、greenDAO 3概述

greenDAO 3是基于注解来定义 schemasentities,而greenDAO 3之前的版本则要求开发人员有一个单独的生成器Java项目来定义的。
技术分享
如上图所示,greenDAO 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案。(greenDAO is a light & fast ORM solution that maps objects to SQLite databases.)它性能最大化,可能是Android平台上最快的ORM框架 易于使用的API 最小的内存开销 依赖体积小 支持数据库加密 强大的社区支持
技术分享

二、greenDAO的重大角色

D for Data,A for Access,O for Object(如果没理解错的话)。 所以最重要的三大主角肯肯定非:DAO**s、**DaoMasterDaoSession莫属,正所谓红花还需绿叶衬,除了三大主角还包含其他角色SchemaPropertiesEntity等共同完成,接下来接单介绍下他们的功能和联系:

1、DAOs

DAOs是负责直接实现增删改查数据库的,直接映射着我们要操作的JeanBean,所以默认情况下命名为XxxDAO。简而言之,要想操作数据库,都得先通过DaoSession的getXxxDAO系方法获得对应的DAO,再调用对应的方法, DAOs里生成数据库语句, 将成员变量与表里的参数绑定对应起来, 并且在它的父类AbstractDao 里实现了相对DaoSession的操作数据的方法(也可以从DaoSession 父类的源码里发现DaoSession 的那些方法本质上就是DAOs的封装),其实只需看看我们自己的DAO代码即可猜到他的角色了。

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/** 
 * DAO for table "USER".
*/
public class UserDao extends AbstractDao<User, Long> {

    public static final String TABLENAME = "USER";

    /** Creates the underlying database table. */
    public static void createTable(Database db, boolean ifNotExists) {
        String constraint = ifNotExists? "IF NOT EXISTS ": "";
        db.execSQL("CREATE TABLE " + constraint + "\"USER\" (" + //
                "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL ," + // 0: _id
                "\"NAME\" TEXT," + // 1: name
                "\"AVATAR_URL\" TEXT," + // 2: avatarUrl
                "\"IS_VERFYED\" INTEGER NOT NULL );"); // 3: isVerfyed
    }

    /** Drops the underlying database table. */
    public static void dropTable(Database db, boolean ifExists) {
        String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"USER\"";
        db.execSQL(sql);
    }

    @Override
    protected final void bindValues(DatabaseStatement stmt, User entity) {
        stmt.clearBindings();
        stmt.bindLong(1, entity.get_id());

        String name = entity.getName();
        if (name != null) {
            stmt.bindString(2, name);
        }

        String avatarUrl = entity.getAvatarUrl();
        if (avatarUrl != null) {
            stmt.bindString(3, avatarUrl);
        }
        stmt.bindLong(4, entity.getIsVerfyed() ? 1L: 0L);
    }  

    @Override
    public User readEntity(Cursor cursor, int offset) {
        User entity = new User( //
            cursor.getLong(offset + 0), // _id
            cursor.isNull(offset + 1) ? null : cursor.getString(offset + 1), // name
            cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // avatarUrl
            cursor.getShort(offset + 3) != 0 // isVerfyed
        );
        return entity;
    }
    ...

}

2、DaoMaster

DaoMaster 它掌管着数据库对象(SQLiteDatabase) 并且它还管理所有的 DAO 和DAOSession。首先通过AbstractDaoMaster的registerDaoClass方法创建DaoConfig对象,注册一个实体类,并添加到DAOMaster的daoConfigMap对象中,以Map的形式保存信息,表示该实体类需要持久化,待为其创建数据表。具体的过程与原生的有所不同,使用过SQLite 原生SQL方式的,应该都知道在Android系统中为了便于对SQLiteDatabase做数据库操作提供了SQLiteOpenHelper类,它里面有两个抽象方法分别是onCreate()和onUpdate()分别在数据库第一次建立和升级的时候被调用。而greenDAO则是在DAOMaster中添加了OpenHelper继承了DatabaseOpenHelper(其中DatabaseOpenHelper继承了SQLiteOpenHelper),并在onCreate()方法中调用createAllTables(db, false)创建所有的数据表;而DevOpenHelper则继续继承OpenHelper并在onUpdate()在方法中删除所有数据表并重新创建,createAllTables和dropAllTables方法比较简单,只是简单地调动DAOs的数据表创建和删除方法。最后,再通过newSession系列方法初始化DaoSession

// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
 * Master of DAO (schema version 1): knows all DAOs.
 */
public class DaoMaster extends AbstractDaoMaster {
    public static final int SCHEMA_VERSION = 1;

    /** Creates underlying database table using DAOs. */
    public static void createAllTables(Database db, boolean ifNotExists) {
        UserDao.createTable(db, ifNotExists);
    }

    /** Drops underlying database table using DAOs. */
    public static void dropAllTables(Database db, boolean ifExists) {
        UserDao.dropTable(db, ifExists);
    }

    /**
     * WARNING: Drops all table on Upgrade! Use only during development.
     * Convenience method using a {@link DevOpenHelper}.
     */
    public static DaoSession newDevSession(Context context, String name) {
        Database db = new DevOpenHelper(context, name).getWritableDb();
        DaoMaster daoMaster = new DaoMaster(db);
        return daoMaster.newSession();
    }

    public DaoSession newSession(IdentityScopeType type) {
        return new DaoSession(db, type, daoConfigMap);
    }

    public DaoMaster(Database db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(UserDao.class);
    }

    /**
     * Calls {@link #createAllTables(Database, boolean)} in {@link #onCreate(Database)} -
     */
    public static abstract class OpenHelper extends DatabaseOpenHelper {
        public OpenHelper(Context context, String name) {
            super(context, name, SCHEMA_VERSION);
        }

        public OpenHelper(Context context, String name, CursorFactory factory) {
            super(context, name, factory, SCHEMA_VERSION);
        }

        @Override
        public void onCreate(Database db) {
            Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);
            createAllTables(db, false);
        }
    }

    /** WARNING: Drops all table on Upgrade! Use only during development. */
    public static class DevOpenHelper extends OpenHelper {
        public DevOpenHelper(Context context, String name) {
            super(context, name);
        }

        public DevOpenHelper(Context context, String name, 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 + " by dropping all tables");
            dropAllTables(db, true);
            onCreate(db);
        }
    }
    ...
}

3、DaoSession

DaoSession 管理着所有的DAO 对象主要用于提供获取DAOs的接口,每一个DaoMaster持有一个数据库连接,通过DaoMaster的newSession()方法可以实例化多个Session,这些Session对应同一个数据库连接,但是系统会为每一个Session分配内存,在这片内存中会为实体进行缓存。每一个Session对应一个Identity scope。一个新的Session就代表一个会话,通过同一个会话中的DAOs进行的数据库操作,greenDAO会对其优化,例如查询操作会对结果缓存之类的机制等等。

public class AbstractDaoSession {
    private final Database db;
    private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;

    public AbstractDaoSession(Database db) {
        this.db = db;
        this.entityToDao = new HashMap<Class<?>, AbstractDao<?, ?>>();
    }

    protected <T> void registerDao(Class<T> entityClass, AbstractDao<T, ?> dao) {
        entityToDao.put(entityClass, dao);
    }

    /** Convenient call for {@link AbstractDao#queryRaw(String, String...)}. */
    public <T, K> List<T> queryRaw(Class<T> entityClass, String where, String... selectionArgs) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, K> dao = (AbstractDao<T, K>) getDao(entityClass);
        return dao.queryRaw(where, selectionArgs);
    }

    /** Convenient call for {@link AbstractDao#queryBuilder()}. */
    public <T> QueryBuilder<T> queryBuilder(Class<T> entityClass) {
        @SuppressWarnings("unchecked")
        AbstractDao<T, ?> dao = (AbstractDao<T, ?>) getDao(entityClass);
        return dao.queryBuilder();
    }

    public AbstractDao<?, ?> getDao(Class<? extends Object> entityClass) {
        AbstractDao<?, ?> dao = entityToDao.get(entityClass);
        if (dao == null) {
            throw new DaoException("No DAO registered for " + entityClass);
        }
        return dao;
    }
    ...
}

4、Entity

Entity本质上就是添加了@Entity注解的JavaBean,并且与数据库对应的表建立了映射关系

5、Properties

Properties是DAOs里的一个静态内部类,把Entity里的属性封装为Property对象,负责创建、删除实体对应的表,真正执行SQL语句,简而言之就是每一个Property对象对应着Entity里第一个数据成员变量,同时也对应着数据表的的一个列元素。

public class UserDao extends AbstractDao<User, Long> {

    public static final String TABLENAME = "USER";

    /**
     * Properties of entity User.<br/>
     * Can be used for QueryBuilder and for referencing column names.
     */
    public static class Properties {
        public final static Property _id = new Property(0, long.class, "_id", true, "_id");
        public final static Property Name = new Property(1, String.class, "name", false, "NAME");
        public final static Property AvatarUrl = new Property(2, String.class, "avatarUrl", false, "AVATAR_URL");
        public final static Property IsVerfyed = new Property(3, boolean.class, "isVerfyed", false, "IS_VERFYED");
    }
    ...
    }

6、Schema

Schema模式、架构的意思,其实作用和SQL Server数据库里的大同小异,都是为了提升数据库自身的性能,从我们应用greenDAO方面来说,也可以不必深究,就拿SQL Server 2000来说,对应的对象命名是“服务器.数据库.用户名.对象”,但2000之后版本的对象命名改为“服务器.数据库.Schema.对象”。这让规划数据库对象命名时更有弹性。 架构是形成单个命名空间的数据库实体的集合。命名空间是一个集合,其中每个元素的名称都是唯一的。

三、引入并配置greeDAO

1、在Project的build.gradle脚本下引入

必须配置:

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath ‘com.android.tools.build:gradle:2.2.3‘

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
    //添加greeDAO start
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath ‘org.greenrobot:greendao-gradle-plugin:3.2.0‘
    }
    //添加greeDAO end
}

可选配置:

//与
allprojects {
    repositories {
        jcenter()
        // 使用数据库升级辅助GreenDaoUpgradeHelper时添加
        maven { url "https://jitpack.io" }
    }
}

2、配置module下的build.gradle

必须配置:

apply plugin: ‘org.greenrobot.greendao’

dependencies {
    compile fileTree(dir: ‘libs‘, include: [‘*.jar‘])
    androidTestCompile(‘com.android.support.test.espresso:espresso-core:2.2.2‘, {
        exclude group: ‘com.android.support‘, module: ‘support-annotations‘
    })
    testCompile ‘junit:junit:4.12‘

    //greeDAO start
    compile ‘org.greenrobot:greendao:3.2.0‘
    // 数据库加密时添加
    compile ‘net.zetetic:android-database-sqlcipher:3.5.1‘
    // 使用数据库升级辅助GreenDaoUpgradeHelper时添加
    compile ‘com.github.yuweiguocn:GreenDaoUpgradeHelper:v1.2.0‘
    //greeDAO end
}

可选配置:

//与dependencies平级,主要用于配置数据库相关信息的及自动生成的“DAO”资源的目录
greendao {

    schemaVersion 1//数据库版本号

    daoPackage ‘crazymo.src.greendao‘//设置DaoMaster、DaoSession、Dao包名

    targetGenDir ‘src/main/java‘//设置DaoMaster、DaoSession、Dao目录

    //targetGenDirTest:设置生成单元测试目录

    //generateTests:设置自动生成单元测试用例

}

完成以上配置工作之后,建立一个实体Bean,再点击build,则可以完成DAO相关资源文件的建立。
技术分享

四、使用greeDAO 3的步骤及基本术语

1、建立实体Entity

所谓实体,本质上它就是一个普通的JavaBean,特殊之处在于它被添加了greenDAO的@Entity注解来表达持久化的存储数据模型,与SQlite数据库中的表建立了映射关系,简单来说,一个JavaBean的实例对应数据库表记录中的一行,JavaBean中的数据成员变量对应着表中的列头值

/**/
public class User {

    private long _id;
    private String name;
    private String avatarUrl;
    private boolean isVerfyed;
}

2、greeDAO 3 注解的基本语法

greenDAO 3是支持多种注解的,可以分为:实体@Entity注解基本属性注解索引注解关系注解

@Entity(
        // schema 名,多个 schema 时设置关联实体。插件产生不支持,需使用产生器
        // schema = "myschema",

        // 标记一个实体是否处于活动状态,活动实体有 update、delete、refresh 方法。默认为 false
        active = false,

        // 表名,默认为类名
        nameInDb = "AWESOME_USERS",

        // 定义多列索引
        indexes = {
                @Index(value = http://www.mamicode.com/"name DESC", unique = true)
        },

        // 标记是否创建表,默认 true。多实体对应一个表或者表已创建,不需要 greenDAO 创建时设置 false
        createInDb = true,

        // 是否产生所有参数构造器。默认为 true。无参构造器必定产生
        generateConstructors = true,

        // 如果没有 get/set 方法,是否生成。默认为 true
        generateGettersSetters = true
)
public class User {
    // 主键,autoincrement设置自增
     @Id(autoincrement = true)
     private Long id;

     // 唯一,默认索引
     @Unique
     private Long userId;

     // 列名,默认使用变量名。变化:customName --> CUSTOM_NAME
     @Property(nameInDb = "USERNAME")
     private String name;

     // 索引,unique设置唯一,name设置索引别名
     @Index(unique = true)
     private long fk_dogId;

     // 非空
     @NotNull
     private String horseName;

     // 忽略,不持久化,可用关键字transient替代
     @Transient
     private int tempUsageCount;

     // 对一,实体属性与外联实体中的ID相对应。默认自动自动生成。fk和对象联动,同时改变。对象懒加载
     @ToOne(joinProperty = "fk_dogId")
     private Dog dog;

     // 对多,referencedJoinProperty 指定实体中与外联实体属性相对应的外键
     @ToMany(referencedJoinProperty = "fk_userId")
     private List<cat> cats;

     // 对多,@JoinProperty注解:name 实体中的属性;referencedName 外联实体中的属性。
     @ToMany(joinProperties = {
                     @JoinProperty(name = "horseName", referencedName = "name")
     })
     private List<horse> horses;

     // 对多,@JoinEntity注解:entity 中间表;sourceProperty 实体属性;targetProperty 外链实体属性
     @ToMany
     @JoinEntity(
                     entity = JoinSheepToUser.class,
                     sourceProperty = "uId",
                     targetProperty = "sId"
     )
     private List<sheep> sheep;

}

@Entity
public class JoinSheepToUser {

    @Id
    private Long id;

    private Long uId;

    private Long sId;
}

@Entity
public class Sheep {

    @Id
    private Long id;

    private String name;
}

2.1、实体@Entity注解

@Entity注解可以表明该类是需要持久化的类

@Entity(
        // If you want to have more than one schema, you can tell greenDAO
        // to which schema an entity belongs (pick any string as a name).
        schema = "myschema",

        // Flag to make an entity "active": Active entities have update,
        // delete, and refresh methods.
        active = true,

        // Specifies the name of the table in the database.
        // By default, the name is based on the entities class name.
        nameInDb = "AWESOME_USERS",

        // Define indexes spanning multiple columns here.
        indexes = {
                @Index(value = "name DESC", unique = true)
        },

        // Flag if the DAO should create the database table (default is true).
        // Set this to false, if you have multiple entities mapping to one table,
        // or the table creation is done outside of greenDAO.
        createInDb = false
)
  • schema:设置当前实体所属的schema

  • active:标记一个实体处于活跃状态,活动实体有更新、删除和刷新方法,默认false

  • nameInDb:在数据库中使用的表名,默认使用的是实体的类名

  • indexes:定义索引,可以跨越多个列

  • createInDb:标记是否创建表,默认 true。当多实体对应一个表或者表已创建,或不需要 greenDAO 创建时设置 false

  • generateGettersSetters:,自动生成getter和setter默认true

  • generateConstructors:,自动生成构造方法,默认true

2.2、基本属性注解

  • @Id:主键 long 型,可以通过@Id(autoincrement = true)设置自增长

  • @Property:可以自定义该属性在数据表的列名,默认是使用字段名,例:@Property(nameInDb = “name”)

  • @NotNull:设置数据库表当前列不能为空

  • @Transient:不对该属性持久化,即添加此标记后不会在数据表中创建对应的列

2.3、索引注解

  • @Index:为一个属性来创建一个索引(即在这个属性对应的列名上创建索引),通过name设置索引别名,也可以通过unique给索引添加约束,有name和unique两个参数,name可以为索引指定名称,unique与数据表中建立索引的unique含义一致

  • @Unique:向数据库添加了一个唯一的约束

2.4、关系注解

在建立数据库时,每一个实体类会建立一张数据表,代表一个关系,而不同实体之间必然存在一定的关系,反映到数据表上也需要建立关系。

2.4.1、@ToOne:定义与另一个实体(一个实体对象)的关系1:1

比如说在我们数据库中存在User用户表和Avatar表分别负责存储用户信息和图片信息,每一条User信息都对应这一个头像的图片信息,很明显User表和Avatar存在着1:1的关系(即所谓的外键),就可以通过@ToOne(joinProperty = “avatarId”)来定义,那么最终在数据表中则会有 avatarId 这一列作为外键,与Picture数据表建立联系,如果你没有指定 avatarId , greenDAO也会在数据表中生成一列属性其作用与指定的avatarId相同,代码如下:

@Entity
public class User {
    @Id
    private Long id;
    private String name;
    private Long avatarId;//
    @ToOne(joinProperty = "avatarId")
    private Avatar Avatar;

    ....
}

@Entity
public class Avatar {
    @Id
    private long avatarId;

    ...
}

2.4.2、 @ToMany:定义与多个实体对象的关系1:N

一对多的关系,定义了一个实体对象对应着多个实体对象,比如一个用户对应多个Order, 在建立数据表示会在目标实体(即一对多的那个多的实体类)的数据表中建立外键,指向源实体类(一对多中的一那个实体类)的数据表。目标数据表中的外键属性由@ToMany(referencedJoinProperty = “XXXX”)指定,最终在代码中可以使用User#getOrders()获取user的所有对应的order的List, ownerId是在Order中的外键,指向User的主键Id。

@Entity
public class User {
    @Id
    private Long id;
    private String name;
    private Long avatarId;
    @ToOne(joinProperty = "avatarId")
    private Avatar avatar;
    @ToOne
    private Avatar thumbnailPicture;
    @ToMany(referencedJoinProperty = "ownerId")
    private List<Order> oders;

    ...
}

@Entity
public class Order {
    @Id
    private long id;
    private long ownerId;

    ...
}

2.4.3、joinProperties为指定属性设置关系

较为复杂的关系可以使用joinProperties属性,其值可以设置一系列的@JoinProperty(name = “XXXX”, referencedName = “YYYY”),可以建立目标实体类中YYYY属性指向源实体类XXXX的属性,其中YYYY为非空,XXXX为unique的,不一定是主键,这样的关系可以有多个,引用官方例子

@Entity
public class User {
    @Id private Long id;
    @Unique private String authorTag;

    @ToMany(joinProperties = {
            @JoinProperty(name = "authorTag", referencedName = "ownerTag")
    })
    private List<Site> ownedSites;
}

@Entity
public class Site {
    @Id private Long id;
    @NotNull private String ownerTag;
}

3、为实体JavaBean添加上相应的注解

/*指定该Bean为greeDAO的实体*/
@Entity
public class User {
    @Id(autoincrement = true)//定义_id为主键并且自增
    @NotNull //定义不为空约束
    private long _id;
    private String name;
    private String avatarUrl;
    private boolean isVerfyed;
}

4、初始化greenDAO并创建数据库

一般建议在Application中初始化数据库(以下这种方式有很大的优化空间),直接使用SQlite的时候我们通过SQLiteOpenHelper的子类来获取DB对象,而greenDAO也类似,我们通过他的DevOpenHelper来获取。

//name:数据库的名字;cursorFactory:游标工厂,一般不用,传入null或者使用两个参数的方法即可
DevOpenHelper(Context context,String name)
DevOpenHelper(Context context,String name,CursorFactory factory)
public class GreenDAOApplication extends Application {
    private final static String DBNAME="REBOT_DB" ;
    public static DaoSession daoSession=null;
    @Override
    public void onCreate() {
        super.onCreate();
        initGreenDAO(this);
    }
    /**
     * 初始化并创建数据库及其结构
     */
    private void initGreenDAO(Context context){
        DaoMaster.DevOpenHelper openHelper=new DaoMaster.DevOpenHelper(context,DBNAME);
        Database database=openHelper.getWritableDb();
        DaoMaster daoMaster=new DaoMaster(database);
        daoSession=daoMaster.newSession();
    }
    public static DaoSession getDaoSession(){
        return daoSession;
    }
}

以上为入门greenDAO所应该掌握的所有知识,下一篇再进入实战操作。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Android ORM——初识greenDAO 3及使用greenDAO 3前应该掌握的一些知识点(一)