强大的数据库ORM框架-GreenDao项目源码剖析篇
作者: | 蒋东国 |
时间: | 2016年12月9日 星期五 |
应用来源: | hqt APP(测试机型:三星 Note4/S5) |
博客地址: | http://blog.csdn.net/andrexpert/article/details/53539417 |
情景再现:“由上一篇博文( 强大的数据库ORM框架-GreenDao项目构建篇)可知,无论通过哪一种方式我们都可以比较容易的构建出GreenDao项目,并且成功生成相关的数据库管理文件,但是这些源码文件都各有什么作用,我们该如何去使用它们?这将是本篇所要讨论的核心问题。”
首先我们还是从DaoDemoGenerator代码生成器开始,DaoDemoGenerator项目是一个纯Java工程,如果希望在自己的Android项目中引入GreenDao来管理数据库,就必须使用这个生成器来生成Android项目所需的实体类和DAO管理类,该项目仅包含一个类文件DaoDemoGenerator.class。
1.GreenDao生成器源码剖析
/**
*@dscrible Java项目,用于生成Android项目所需的实体和DAO层
*
* Created by jiangdongguo on 2016-11-7 下午10:15:13
*/
public class DaoDemoGenerator {
public static void main(String[] args) throws IOException, Exception {
//设置实体类包名,其中com.example.greendaodemo1是Android项目包名
Schema schema = new Schema(1, "com.example.greendaodemo.bean");
//设置DAO层包名
schema.setDefaultJavaPackageDao("com.example.greendaodemo.dao");
//创建三个与数据库表相对应的实体类
addNote(schema);
//生成代码后存放的路径,GreenDaoDemo是Android项目
new DaoGenerator().generateAll(schema, "../GreenDaoDemo1/src");
}
private static void addNote(Schema schema){
//实体类
Entity personInfo = schema.addEntity("PersonInfo");
//添加id属性
personInfo.addIdProperty();
//依次添加name、age、sex、phone、address属性
personInfo.addStringProperty("name").notNull();
personInfo.addIntProperty("age");
personInfo.addStringProperty("sex").notNull();
personInfo.addIntProperty("phone");
personInfo.addStringProperty("address").notNull();
//实体类
Entity carInfo = schema.addEntity("CarInfo");
//添加id属性
carInfo.addIdProperty();
//依次添加brand属性
carInfo.addStringProperty("brand").notNull();
carInfo.addStringProperty("model");
carInfo.addIntProperty("price").notNull();
carInfo.addStringProperty("date");
carInfo.addStringProperty("place").notNull();
//实体类
Entity workInfo = schema.addEntity("WorkInfo");
//添加id属性
workInfo.addIdProperty();
workInfo.addStringProperty("company").notNull();
workInfo.addStringProperty("address").notNull();
workInfo.addIntProperty("number");
}
}
.dao")语句的作用将相关的Dao管理类生成到包名为” com.example.greendaodemo.dao”目录下,如果dao目录不存在就自动创建它。(3) addNote(Schema schema)方法用来创建与数据库表相对应的实体类,一个Entity对象就对应者一个数据库表,数据库表的创建删除、表字段增删就是在这里进行的。(4) new DaoGenerator().generateAll
(schema, "../GreenDaoDemo1/src")语句开始执行代码生成工作,” ../GreenDaoDemo1/src”为当前工程组中包名为” com.example.greendaodemo”Android项目的src目录。
2.GreenDao项目源码剖析
(1)DaoMaster:GreenDao框架管理类,该类对数据库相关管理操作进行封装,比如它包含两个内部类OpenHelper和DevOpenHelper,OpenHelper继承于SQLiteOpenHelper用于创建所有的数据库表;DevOpenHelper继承于OpenHelper用于数据库升级。另外,它还包含一个newSession()方法用于返回一个DaoSession对象,其是连接GreenDao框架到SQLite数据库的纽带,通过该对象我们可以得到一个与数据库某个表相关的操作对象xxxDao。
// 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(SQLiteDatabase db, boolean ifNotExists) {
PersonInfoDao.createTable(db, ifNotExists);
CarInfoDao.createTable(db, ifNotExists);
WorkInfoDao.createTable(db, ifNotExists);
}
/** Drops underlying database table using DAOs. */
public static void dropAllTables(SQLiteDatabase db, boolean ifExists) {
PersonInfoDao.dropTable(db, ifExists);
CarInfoDao.dropTable(db, ifExists);
WorkInfoDao.dropTable(db, ifExists);
}
public static abstract class OpenHelper extends SQLiteOpenHelper {
public OpenHelper(Context context, String name, CursorFactory factory) {
super(context, name, factory, SCHEMA_VERSION);
}
@Override
public void onCreate(SQLiteDatabase 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, CursorFactory factory) {
super(context, name, factory);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.i("greenDAO", "Upgrading schema from version " + oldVersion
+ " to " + newVersion + " by dropping all tables");
dropAllTables(db, true);
onCreate(db);
}
}
public DaoMaster(SQLiteDatabase db) {
super(db, SCHEMA_VERSION);
registerDaoClass(PersonInfoDao.class);
registerDaoClass(CarInfoDao.class);
registerDaoClass(WorkInfoDao.class);
}
public DaoSession newSession() {
return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
}
public DaoSession newSession(IdentityScopeType type) {
return new DaoSession(db, type, daoConfigMap);
}
}
(2)DaoSession:DaoSession用于获得能够操作数据库的xxxDao对象,它建立了与SQLite数据库之间的连接(会话),其中,getPersonInfoDao()、getCarInfoDao()和getWorkInfoDao()方法用于返回相关的Dao对象,我们在使用GreenDao管理数据库时,真正去操作数据库数据的就是通过这些Dao对象。
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* {@inheritDoc}
*
* @see de.greenrobot.dao.AbstractDaoSession
*/
public class DaoSession extends AbstractDaoSession {
private final DaoConfig personInfoDaoConfig;
private final DaoConfig carInfoDaoConfig;
private final DaoConfig workInfoDaoConfig;
private final PersonInfoDao personInfoDao;
private final CarInfoDao carInfoDao;
private final WorkInfoDao workInfoDao;
public DaoSession(SQLiteDatabase db, IdentityScopeType type, Map<Class<? extends
AbstractDao<?, ?>>, DaoConfig>daoConfigMap) {
super(db);
personInfoDaoConfig = daoConfigMap.get(PersonInfoDao.class).clone();
personInfoDaoConfig.initIdentityScope(type);
carInfoDaoConfig = daoConfigMap.get(CarInfoDao.class).clone();
carInfoDaoConfig.initIdentityScope(type);
workInfoDaoConfig = daoConfigMap.get(WorkInfoDao.class).clone();
workInfoDaoConfig.initIdentityScope(type);
personInfoDao = new PersonInfoDao(personInfoDaoConfig, this);
carInfoDao = new CarInfoDao(carInfoDaoConfig, this);
workInfoDao = new WorkInfoDao(workInfoDaoConfig, this);
registerDao(PersonInfo.class, personInfoDao);
registerDao(CarInfo.class, carInfoDao);
registerDao(WorkInfo.class, workInfoDao);
}
public void clear() {
personInfoDaoConfig.getIdentityScope().clear();
carInfoDaoConfig.getIdentityScope().clear();
workInfoDaoConfig.getIdentityScope().clear();
}
public PersonInfoDao getPersonInfoDao() {
return personInfoDao;
}
public CarInfoDao getCarInfoDao() {
return carInfoDao;
}
public WorkInfoDao getWorkInfoDao() {
return workInfoDao;
}
}
(3)PersonInfo、CarInfo、WorkInfo:持久的实体对象,即bean目录下的实体类,可理解为用于封装与数据库实际表字段对应的相关属性,比如PERSON_INFO表。我们通过操作这些实体类中的属性和方法,就能够实现操作被映射的数据库表。
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. Enable "keep" sections if you want to edit.
/**
* Entity mapped to table "PERSON_INFO".
*/
public class PersonInfo {
private Long id;
/** Not-null value. */
private String name;
private Integer age;
/** Not-null value. */
private String sex;
public PersonInfo() {
}
public PersonInfo(Long id) {
this.id = id;
}
public PersonInfo(Long id, String name, Integer age, String sex) {
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
/** Not-null value. */
public String getName() {
return name;
}
/** Not-null value; ensure this value is available before it is saved to the database. */
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
/** Not-null value. */
public String getSex() {
return sex;
}
/** Not-null value; ensure this value is available before it is saved to the database. */
public void setSex(String sex) {
this.sex = sex;
}
}
(4)PersonInfoDao、CarInfoDao、WorkInfoDao:有生成器生成的数据库操作类,它继承于AbstractDao,封装了所有对数据库表进行增删改成的方法,比如PersonInfoDao中的createTable方法用于创建与PersonInfo属性对应的表PERSON_INFO;dropTable方法用于删除表PERSON_INFO。可以这么说,我们之所以使用GreenDao管理本地数据库无需与SQL语句打交道,就是因为GreenDao框架在这一层已经对大部分数据库操作SQL语句进行了封装。
// THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT.
/**
* DAO for table "CAR_INFO".
*/
public class CarInfoDao extends AbstractDao<CarInfo, Long> {
public static final String TABLENAME = "CAR_INFO";
/**
* Properties of entity CarInfo.<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 Brand = new Property(1, String.class, "brand", false, "BRAND");
public final static Property Model = new Property(2, String.class, "model", false, "MODEL");
public final static Property Price = new Property(3, int.class, "price", false, "PRICE");
public final static Property Date = new Property(4, String.class, "date", false, "DATE");
public final static Property Place = new Property(5, String.class, "place", false, "PLACE");
};
public CarInfoDao(DaoConfig config) {
super(config);
}
public CarInfoDao(DaoConfig config, DaoSession daoSession) {
super(config, daoSession);
}
/** Creates the underlying database table. */
public static void createTable(SQLiteDatabase db, boolean ifNotExists) {
String constraint = ifNotExists? "IF NOT EXISTS ": "";
db.execSQL("CREATE TABLE " + constraint + "\"CAR_INFO\" (" + //
"\"_id\" INTEGER PRIMARY KEY ," + // 0: id
"\"BRAND\" TEXT NOT NULL ," + // 1: brand
"\"MODEL\" TEXT," + // 2: model
"\"PRICE\" INTEGER NOT NULL ," + // 3: price
"\"DATE\" TEXT," + // 4: date
"\"PLACE\" TEXT NOT NULL );"); // 5: place
}
/** Drops the underlying database table. */
public static void dropTable(SQLiteDatabase db, boolean ifExists) {
String sql = "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + "\"CAR_INFO\"";
db.execSQL(sql);
}
/** @inheritdoc */
@Override
protected void bindValues(SQLiteStatement stmt, CarInfo entity) {
stmt.clearBindings();
Long id = entity.getId();
if (id != null) {
stmt.bindLong(1, id);
}
stmt.bindString(2, entity.getBrand());
String model = entity.getModel();
if (model != null) {
stmt.bindString(3, model);
}
stmt.bindLong(4, entity.getPrice());
String date = entity.getDate();
if (date != null) {
stmt.bindString(5, date);
}
stmt.bindString(6, entity.getPlace());
}
/** @inheritdoc */
@Override
public Long readKey(Cursor cursor, int offset) {
return cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0);
}
/** @inheritdoc */
@Override
public CarInfo readEntity(Cursor cursor, int offset) {
CarInfo entity = new CarInfo( //
cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0), // id
cursor.getString(offset + 1), // brand
cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2), // model
cursor.getInt(offset + 3), // price
cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4), // date
cursor.getString(offset + 5) // place
);
return entity;
}
/** @inheritdoc */
@Override
public void readEntity(Cursor cursor, CarInfo entity, int offset) {
entity.setId(cursor.isNull(offset + 0) ? null : cursor.getLong(offset + 0));
entity.setBrand(cursor.getString(offset + 1));
entity.setModel(cursor.isNull(offset + 2) ? null : cursor.getString(offset + 2));
entity.setPrice(cursor.getInt(offset + 3));
entity.setDate(cursor.isNull(offset + 4) ? null : cursor.getString(offset + 4));
entity.setPlace(cursor.getString(offset + 5));
}
/** @inheritdoc */
@Override
protected Long updateKeyAfterInsert(CarInfo entity, long rowId) {
entity.setId(rowId);
return rowId;
}
/** @inheritdoc */
@Override
public Long getKey(CarInfo entity) {
if(entity != null) {
return entity.getId();
} else {
return null;
}
}
/** @inheritdoc */
@Override
protected boolean isEntityUpdateable() {
return true;
}
}
3.GreenDao框架使用经过(1)、(2)的分析,我们应该大概了解了GreenDao框架管理数据库数据的基本构成,接下来就是如何去使用这些自动生成的类。对数据库的管理不外乎就是数据的增删该查,毫无疑问,在之前的分析过程中我们涉及到次数最多的就是PersonInfoDao等类,这些类封装了对相应数据库表的操作。由此可知,针对GreenDao框架的使用,在我们的Android项目中就是要获得各种表对应的xxxDao对象,它们可以通过DaoSession相关方法获得。
(1)DaoApplication.java
继承于Application,是一个全局类,它保证整个应用中有且只有一个SQLiteDatabase、DaoMaste和DaoSession实例,并包含getDaoSession()、getDatabase()方法分别用于返回一个SQLiteDatabase和DaoSession对象
/**
*@dscrible 使用单例模式,以实例化全局只有一个DaoMaste和DaoSessionr实例
*
* Created by jiangdongguo on 2016-11-7 下午5:47:03
*
*/
public class DaoApplication extends Application {
public DaoSession mDaoSession;
public SQLiteDatabase db;
public DaoMaster.DevOpenHelper mOpenHelper;
public DaoMaster mDaoMaster;
@Override
public void onCreate() {
super.onCreate();
initGreenDao();
}
private void initGreenDao(){
//实例化SQLiteOpenHelper,并连接到数据库testDb
mOpenHelper = new DaoMaster.DevOpenHelper(this,"testDb", null);
//以读写方式打开数据库
db = mOpenHelper.getWritableDatabase();
//实例化DaoMaster
mDaoMaster = new DaoMaster(db);
//实例化一个数据库会话对象
mDaoSession = mDaoMaster.newSession();
}
public DaoSession getDaoSession(){
return mDaoSession;
}
public SQLiteDatabase getDatabase(){
return db;
}
}
(2)BaseActivity.class:为了便于对数据库的操作,我们创建一个基类用于获得能够操作数据库的xxxDao对象。当然,从MVP模式角度来说,对数据库的操作更建议将其放到M层,通过继承父类的方式来获取xxxDao对象。
/**
*@dscrible 基类,获得表的DAO对象
*
* Created by jiangdongguo on 2016-11-8 下午5:53:58
*/
public class BaseActivity extends Activity {
protected PersonInfoDao mPersonInfoDao;
protected CarInfoDao mCarInfoDao;
protected WorkInfoDao mWorkInfoDao;
protected DaoSession daoSession;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getTableDao();
}
private void getTableDao() {
daoSession = ((DaoApplication)this.getApplicationContext()).getDaoSession();
mPersonInfoDao = daoSession.getPersonInfoDao();
mCarInfoDao = daoSession.getCarInfoDao();
mWorkInfoDao = daoSession.getWorkInfoDao();
}
}
码字不容易,转载请注明出处:http://blog.csdn.net/andrexpert/article/details/53539417
最后的话:“从自动生成的GreenDao相关文件可知,官方是不建议直接在这些文件中写入自己的代码,因为当数据库需要升级时如果使用生成器重新生成将会使原来的类全部被覆盖。另外就是总结下在最后使用GreenDao的逻辑:
(1)实例化一个SQLiteOpenHelper对象,以便建立与指定数据库(如”testDb”)之间的连接;
(2)调用SQLiteOpenHelper的getWritableDatabase()方法以读写方式打开所连接的数据库;
(3)通过获得的数据库对象SQLiteDatabase来创建GreenDao框架管理者DaoMaster对象;
(4)调用DaoMaster的newSession()方法实例化一个数据库会话对象DaoSession;
(5)通过DaoSession对象获得最终能够操作数据库表的xxxxDao对象”
关于资料与Demo: