首页 > 代码库 > GreenDAO开发经验总结

GreenDAO开发经验总结

一、初识GreenDAO

1.1 什么是GreenDAO

greenDao是一个将对象映射到SQLite数据库中的轻量且快速的ORM解决方案。

GreenDAO官网有下面一张图来表示了以上概念:

 

技术分享

Dao呢,英文全称Date Access Object,它是一个数据访问接口,数据访问顾名思义就是与数据库打交道;它是夹在业务逻辑与数据库资源中间进行从关系型数据库到Java对象转换的媒介。

SQLite我们知道是一个安卓原生的一个轻量级的数据库,那么ORM是什么呢?

ORM的全名是Object Relational Mapping,即对象关系映射,简单的说就是对象模型和关系模型的一种映射。前面那句话看不懂也没有关系,其实简单点说就是数据库是一种关系型的,而我们使用的java呢是一种面向对象的编程形式,那么我们想要操作对象一样的操作数据库,ORM就是这样的一种关系到对象的映射,而我们的GreenDAO就是基于ORM解决方案的一种框架,让我们更简单的操作数据库

ORM的优点:

1.让业务代码访问对象,而不是数据库表
2.隐藏了面向对象的逻辑SQL查询详情
3.无需处理数据库实现

1.2 选择GreenDAO原因

首先,我们的Android原生的api带来了很多不便,比如:

 

1.要手动拼装sql
2.要自己操作数据库的常规代码
3.不能自动把数据中的数据映射成对象
4.没有实现级联查询

其次,在GreenDAO的角度来看,是怎样的呢?

1.性能优
2.开发文档比较全面
3.目前非常流行
3.使用方便
4.拓展性强

附:

1.官方网站:http://greenrobot.org/

2.GitHub实例:https://github.com/greenrobot/greenDAO/

3.GreenDAO属性中文参考文档:http://www.boyunjian.com/javadoc/de.greenrobot/greendao/1.3.1/_/de/greenrobot/dao/AbstractDao.html#readKey(Cursor,%20int)

二、操作GreenDAO

2.1 自动生成代码

通过查看GreenDAO的官方网站以及它在GitHub上的代码呢,发现在使用GreenDAO时,需要在java环境下自动生成一下GreenDAO的相关代码,然后再copy到安卓中来使用。自己主要是使用Eclipse来集成GreenDAO的。

2.1.1 下载相关jar包

技术分享

 

2.1.2 新建java项目导入jar包

eclipse新建一个java项目,取名DaoExampleGenerator,然后在java项目导入freemaker-2.3.10greendao-generator-1.3.1两个jar包。新建的java项目主要目的是生成DaoMaster.java   DaoSession.java  Person.java  PersonDao.java 这几个文件将生成的文件放在Android项目中来使用。

技术分享

2.1.3 新建Android工程

DaoExampleGenerator.java的代码下面再说,接下来新建一个android工程,这个android工程才是真正使用GreenDAO的地方。

建完android工程后,将jargreendao-1.3.7放到android工程的libs目录下,工程的目录如下:

技术分享

 

2.1.3 新建Android工程

DaoExampleGenerator.java的代码下面再说,接下来新建一个android工程,这个android工程才是真正使用GreenDAO的地方。

建完android工程后,将jargreendao-1.3.7放到android工程的libs目录下,工程的目录如下:

技术分享

2.1.4 DaoExampleGenerator.java

技术分享

 

上面的DaoExampleGenerator.java中的代码都是greendao官网要求我们这样写的。运行新建的java工程,就会生成技术分享

这几个文件。通过日志输出可以看到生成的那些文件:

 

技术分享

 

这就表示我们的代码自动生成成功了。接下来就是GreenDAO的使用。

 

2.2 GreenDAO的基本操作

 

2.2.1 GreenDAO核心类介绍:

 

GreenDAO4个核心类,分别是:DaoMaster, OpenHelper, DaoSession, XXXDao

 

DaoMaster这个类是数据库的统领,也是整个操作的入口,其管理者数据库,数据库版本号和一些数据库相关信息。DaoMaster可以用来生成下面将要说到的DaoSession。在构造函数中需要对所有的DAO通过registerDaoClass()进行注册,以便DaoMaster对其进行管理。

 

OpenHelper: 根据官方文档,GreenDAO中总共有四种OpenHelper,分别是 OpenHelper, DevOpenHelper,EncryptedOpenHelper 和EncryptedDevOpenHelper;前两个是用来操作未加密数据库,后两个是用来操作加密数据库;DevOpenHelper 和 EncryptedDevOpenHelper 会在升级的时候删除所有表并重建,所以只建议在开发时使用。

 

OpenHelper的用来对数据库进行管理,如创建表,删除表,获取可读写数据库等。另外,OpenHelper还有对数据库创建,数据库升级和数据库开启等事件的监听,分别在onCreate(), onUpgrade(), onOpen()。

 

  DaoSession: 管理所有的XXXDao, DaoSession中也会有增删改查的方法, 其可以直接通过要插入实体的类型找到对应的XXXDao后再进行操作。当没有找到实体对应的Dao时,会抛出 org.greenrobot.greendao.DaoException: No DAO registered for class XXX 的错误。

 

XXXDao: 对实体进行操作,有比DaoSession更丰富的操作,如loadAll, insertInTx.

 

2.2.2 Dao的初始化过程(也是GreenDAO创建数据库和表的过程)

 

这个过程比较复杂,首先看一下初始化dao的方法。

 

 1 //创建数据库、表
 2 private void openDb() {
 3         /* 
 4          * 创建数据库:三个参数:参数1:上下文,参数2:库名,参数3:游标工厂
 5          * 调用DaoMaster中的DevOpenHelper方法,DevOpenHelper方法继承自OpenHelper,而OpenHelper的父类为SQLiteOpenHelper。
 6          * SQLiteOpenHelper主要用来创建数据库。该类的构造器中,调用Context中的方法创建并打开一个指 定名称的数据库对象。
 7          * 如果打开的数据库版本号与当前的版本号不同则会调用ononUpgrade()方法,删除数据库中的所有表重新初始化调用onCreate(),在父类中完成表的创建。
 8          */
 9         helper = new DaoMaster.DevOpenHelper(MainActivity.this, "person.db",
10                 null);
11         //注意:该数据库连接属于DaoMaster,所以多个Session指的是相同的数据库连接。
12         //getWritableDatabase()方法是以读写的方式打开数据库
13         master = new DaoMaster(helper.getWritableDatabase());
14         session = master.newSession();
15         // 获得person表操作数据库类的对象
16         dao = session.getPersonDao();
17     }

 

我这里最后返回的是dao

上面的过程是固定不变的,由官方文档推荐使用,同时官方推荐将获取DaoMaster对象的放到Application层,这样将避免多次创建生成Session对象。

首先看一下helper的初始化过程。

 

1 public DevOpenHelper(Context context, String name, CursorFactory factory) {
2             super(context, name, factory);
3         }

 

调用父类的构造方法

 

 1 /**
 2  * SQLiteOpenHelper主要用来创建数据库,该类的构造器中,调用Context中的方法创建并打开一个指定名称的数据库对象。
 3  * CursorFactory是创建Cursor的工厂类,参数是为了可以自定义Cursor创建。
 4  * 
 5  * @author zh
 6  * 
 7  */
 8 public static abstract class OpenHelper extends SQLiteOpenHelper {
 9         public OpenHelper(Context context, String name, CursorFactory factory) {
10             super(context, name, factory, SCHEMA_VERSION);
11         }
12         // 当数据库被首次创建时执行该方法,一般将创建表等初始化操作在该方法中执行。
13         @Override
14         public void onCreate(SQLiteDatabase db) {
15             Log.i("greenDAO", "Creating tables for schema version "
16                     + SCHEMA_VERSION);
17             createAllTables(db, false);
18         }
19     }

 

在父类中完成数据库的创建。

 

1 /** Creates underlying database table using DAOs. */
2 // 通过DAO层创建底层数据库表
3 public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {
4         // 引用PersonDao中的createTable方法
5         PersonDao.createTable(db, ifNotExists);
6     }

 

 1 public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
 2         String constraint = ifNotExists? "IF NOT EXISTS ": "";
 3         db.execSQL("CREATE TABLE " + constraint + "‘PERSON‘ (" + //
 4                 "‘_id‘ INTEGER PRIMARY KEY ," + // 0: id
 5                 "‘NAME‘ TEXT NOT NULL ," + // 1: name
 6                 "‘AGE‘ INTEGER," + // 2: age
 7                 "‘SEX‘ TEXT," + // 3: sex
 8                 "‘DATE‘ INTEGER," + // 4: date
 9                 "‘DEP‘ TEXT);"); // 5: dep
10 }

这样一来表的创建过程就清楚了,接下来是DaoMaster的初始化。

 

1 public DaoMaster(SQLiteDatabase db) {
2         super(db, SCHEMA_VERSION);
3         // registerDaoClass(java.lang.Class<? extends AbstractDao<?,?>> daoClass)是AbstractDaoMaster中的方法
4         registerDaoClass(PersonDao.class);
5     }

 

显示调用父类的构造方法,接着registerDaoClass(PersonDao.class);(下面的为官网源代码)

 

 

 

 

 

 

 

 1 public AbstractDaoMaster(SQLiteDatabase db, int schemaVersion) {
 2     this.db = db;
 3     this.schemaVersion = schemaVersion;
 4     daoConfigMap = new HashMap<Class<? extends AbstractDao<?, ?>>, DaoConfig>();
 5 }
 6 
 7 protected void registerDaoClass(Class<? extends AbstractDao<?, ?>> daoClass) {
 8 // DaoConfig是greenDAO的一个内部类. DaoConfig 为DAOS存储了基本的数据, 并交给AbstractDaoMaster来保存. 这个类用来接受从DAO类中所必需的信息。
 9            DaoConfig daoConfig = new DaoConfig(db, daoClass);
10             daoConfigMap.put(daoClass, daoConfig);
11 }

 

看到,上面一句:newDaoConfig()很关键(下面的为官网源代码)

 

 

 1 public DaoConfig(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>> daoClass) {
 2     this.db = db;
 3     try {
 4         this.tablename = (String) daoClass.getField("TABLENAME").get(null);
 5         Property[] properties = reflectProperties(daoClass);
 6         this.properties = properties;
 7         allColumns = new String[properties.length];
 8         List<String> pkColumnList = new ArrayList<String>();
 9         List<String> nonPkColumnList = new ArrayList<String>();
10         Property lastPkProperty = null;
11         for (int i = 0; i < properties.length; i++) {
12             Property property = properties[i];
13             String name = property.columnName;
14             allColumns[i] = name;
15             if (property.primaryKey) {
16                 pkColumnList.add(name);
17                 lastPkProperty = property;
18             } else {
19                 nonPkColumnList.add(name);
20             }
21         }
22         String[] nonPkColumnsArray = new String[nonPkColumnList.size()];
23         nonPkColumns = nonPkColumnList.toArray(nonPkColumnsArray);
24         String[] pkColumnsArray = new String[pkColumnList.size()];
25         pkColumns = pkColumnList.toArray(pkColumnsArray);
26         pkProperty = pkColumns.length == 1 ? lastPkProperty : null;
27         statements = new TableStatements(db, tablename, allColumns, pkColumns);
28         if (pkProperty != null) {
29             Class<?> type = pkProperty.type;
30             keyIsNumeric = type.equals(long.class) || type.equals(Long.class) || type.equals(int.class)|| type.equals(Integer.class) 
31        ||type.equals(short.class) || type.equals(Short.class)| type.equals(byte.class) || type.equals(Byte.class);
32         } else {
33             keyIsNumeric = false;
34         }
35     } catch (Exception e) {
36         throw new DaoException("Could not init DAOConfig", e);
37     }
38 }

 

上面的这个方法就是完成DaoConfig的配置的,通过反射机制,获取我们的Dao类,比如说PersonDao.class,具体的代码可以看上面,就是通过反射。注意statements是TableStatements类型的。

继续,newSession()

 

 

1 public DaoSession newSession() {
2         return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
3 }

 

下面的代码中 registerDao(Person.class, personDao) 

 1 public DaoSession(SQLiteDatabase db, IdentityScopeType type,
 2             Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) {
 3         // 调用父类的数据库
 4         super(db);
 5         // 将PersonDao中的重要数据复制保存一份并付给personDaoConfig
 6         personDaoConfig = daoConfigMap.get(PersonDao.class).clone();
 7         // 判断从PersonDao传递过来的数据的类型范围
 8         personDaoConfig.initIdentityScope(type);
 9         // 调用PersonDao的父类AbstractDao中的构造方法。
10         personDao = new PersonDao(personDaoConfig, this);
11         //protected <T> void registerDao(java.lang.Class<T> entityClass, AbstractDao<T,?> dao)(官网中只给出了这个,对于含义并未做出解释)
12         registerDao(Person.class, personDao);
13     }

下面这个段代码来源于官网,initIdentityScope()这个函数就是用来判断类型范围的,一般我们不需要管。

 

 1 public void initIdentityScope(IdentityScopeType type) {
 2     if (type == IdentityScopeType.None) {
 3         identityScope = null;
 4     } else if (type == IdentityScopeType.Session) {
 5         if (keyIsNumeric) {
 6             identityScope = new IdentityScopeLong();
 7         } else {
 8             identityScope = new IdentityScopeObject();
 9         }
10     } else {
11         throw new IllegalArgumentException("Unsupported type: " + type);
12     }
13 }

 

2.2.3 CRUD操作

在我的这个Android项目中,CRUD操作通过Button按钮的点击事件来完成,如图所示:

技术分享

 

添加数据:

添加数据直接调用USER中的构造器,直接将从手机屏幕获取到的数据转存进DAO层中,最后提交给数据库。

 

1 case R.id.addBtn:
2             // 添加数据
3             addDate();
4             break;

 

 1 private void addDate() {
 2         USER user = new USER(Long.valueOf(mId.getText().toString()),
 3                 mName.getText().toString(), Integer.valueOf(mAge.getText()
 4                         .toString()), mDep.getText().toString());
 5         Long msg = dao.insert(user);
 6         mId.setText("");
 7         mName.setText("");
 8         mAge.setText("");
 9         mDep.setText("");
10 
11         Log.i("MAIN_TAG", "添加成功 personID" + msg);
12     }

官网代码:

技术分享

 

删除数据

我所做的删除数据是最简单的删除全部数据,暂时没有按照条件删除等那种复杂的数据删除。

 

1     case R.id.deleteBtn:
2             list = dao.queryBuilder().list();
3             for (USER person1 : list) {
4                 dao.delete(person1);
5             }

 

官网代码:

技术分享

 

更改数据

1         case R.id.updateBtn:
2             list = dao.queryBuilder().list();
3             USER user = list.get(0);
4             user.setName("啦啦");
5             user.setAge(16);
6 
7             updateDate(user);
8             break;
1 private void updateDate(USER user) {
2         dao.insertOrReplace(user);
3 
4          }

官网代码

技术分享

 

查询数据

这里是查询数据库中的所有的数据

 

1 case R.id.queryBtn:
2     queryDate();
3     break;

 

1   private void queryDate() {
2         list = dao.queryBuilder().list();
3         Log.i("MAIN_TAG", "查询结果" + list);
4     }

官网代码

技术分享

 

2.2.4 GreenDAO对数据表的操作

技术分享

 

增加表字段

如上图所示,当需要增加字段时,只要在之前java工程中添加想输入的字段的名字即可,然后在重新生成。对于重新生成这里只要你不更改传入的数据库的版本号(如上图示),那么当你增加完新的字段后原来的数据库中的数据依然会被保留,因此不必担心原来的数据存在丢失的风险。

 

增加新表

同样如图示,如果想在数据库中增加新的表,只需在之前的java工程中按照图中标注的那样即可添加新的表。如果你想让之前的数据和表都存在,那就千万不要更改

Schema schema = new Schema(2, "com.example.greendao"); 

 

中的版本号,否则原来的数据和表都将会更新删除。

技术分享

 

 

 

 

 

 

 

 

 

 

 

GreenDAO开发经验总结