GreenDao使用CRUD及数据库结构升级
今天要说的是众所周知的greenddao,之前一直都是手动sql,不仅在操作数据库的时候比较麻烦,而且还容易出错,朋友推荐了greendao这个轻量级的数据库框架。废话不多说,进入正题!
greendao的构建,是参见这篇文章:greenDao构建
还是把这步骤也说一下,方便大家也方便自己。
studio用户添加依赖:
compile 'de.greenrobot:greendao:1.3.7'
sourceSets {main {java.srcDirs = ['src/main/java', 'src/main/java-gen']}}
1 .在 .src/main 目录下新建一个与 java 同层级的「java-gen」目录,用于存放由 greenDAO 生成的 Bean、DAO、DaoMaster、DaoSession 等类。
2.新建「GREENDAO GENERATOR」模块 (纯 JAVA 工程)
1)通过 File -> New -> New Module -> Java Library -> 填写相应的包名与类名 -> Finish.(注意此处是javalibrary)
2)在新创建的module的gradle中,配置 daoexamplegenerator 工程的 build.gradle,添加 dependencies.
compile 'de.greenrobot:greendao-generator:1.3.1'
3.编写ExampleDaoGenerator 这个类:
关于这个类的编写,直接上代码,注释详细:
首先贴出main方法:
public static void main(String [] args) throws Exception {//注意导包// 正如你所见的,你创建了一个用于添加实体(Entity)的模式(Schema)对象。// 两个参数分别代表:数据库版本号与自动生成代码的包路径。
// Schema schema = new Schema(1, "me.itangqi.greendao");
// 当然,如果你愿意,你也可以分别指定生成的 Bean 与 DAO 类所在的目录,只要如下所示:Schema schema = new Schema(2, "me.itangqi.bean"); schema.setDefaultJavaPackageDao("me.itangqi.dao");// 模式(Schema)同时也拥有两个默认的 flags,分别用来标示 entity 是否是 activie 以及是否使用 keep sections。// schema2.enableActiveEntitiesByDefault();// schema2.enableKeepSectionsByDefault();// 一旦你拥有了一个 Schema 对象后,你便可以使用它添加实体(Entities)了。addNote(schema);addPerson(schema);// 最后我们将使用 DAOGenerator 类的 generateAll() 方法自动生成代码,此处你需要根据自己的情况更改输出目录(既之前创建的 java-gen)。// 其实,输出目录的路径可以在 build.gradle 中设置,有兴趣的朋友可以自行搜索,这里就不再详解。new DaoGenerator().generateAll(schema, "D:/StudioProjects/GreenDaoDemo/app/src/main/java-gen");}
private static void addPerson(Schema schema) {Entity person=schema.addEntity("Person");person.addIdProperty();person.addStringProperty("name").notNull();person.addIntProperty("sex");person.addIntProperty("grade");person.addLongProperty("birthTime");person.addIntProperty("age");}
private static void addNote(Schema schema) {// 一个实体(类)就关联到数据库中的一张表,此处表名为「Note」(既类名)Entity note = schema.addEntity("Note");// 你也可以重新给表命名// note.setTableName("NODE");// greenDAO 会自动根据实体类的属性值来创建表字段,并赋予默认值// 接下来你便可以设置表中的字段:note.addIdProperty();note.addStringProperty("text").notNull();// 与在 Java 中使用驼峰命名法不同,默认数据库中的命名是使用大写和下划线来分割单词的。// For example, a property called “creationDate” will become a database column “CREATION_DATE”.note.addStringProperty("comment");note.addDateProperty("date");}
以上方法决定表中字段,以及类型。
在此需要注意的时,在做数据库升级的时候需要修改这里的版本号:
Schema schema = new Schema(2, "me.itangqi.bean"); //数据库这里修改版本号
最后执行此java文件,及可生成对应的xxxDao,xxx实体。
二、CRUD操作:
首先oncreate中初始化:
private void setupDatabase() {// 通过 DaoMaster 的内部类 DevOpenHelper,你可以得到一个便利的 SQLiteOpenHelper 对象。// 可能你已经注意到了,你并不需要去编写「CREATE TABLE」这样的 SQL 语句,因为 greenDAO 已经帮你做了。// 注意:默认的 DaoMaster.DevOpenHelper 会在数据库升级时,删除所有的表,意味着这将导致数据的丢失。// 所以,在正式的项目中,你还应该做一层封装,来实现数据库的安全升级。DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "test.db", null);db = helper.getWritableDatabase();// 注意:该数据库连接属于 DaoMaster,所以多个 Session 指的是相同的数据库连接。daoMaster = new DaoMaster(db);daoSession = daoMaster.newSession();}
获取你需要操作的表XXXdao
daoSession.getPersonDao().insert(person);
下面来说说CRUD方法,以及常用的实现方式:
插入:
private long add(){long age=3000;Person person=new Person(null,"张大",1,age,18);//添加一些对象
// daoSession.getPersonDao().insertInTx(persons);//添加单个对象
// daoSession.getPersonDao().insert(person);return daoSession.getPersonDao().insert(person);}
更新:
private void update(){//按照某一条件更新数据Person person = persons.get(persons.size() - 1);person.setName("张大大");daoSession.getPersonDao().update(person);//批量更新/*public void updateBatch(List<Note> notes) throws Exception {daoSession.getNoteDao().updateInTx(notes);}*/}
删除:
private void delete(){//根据条件删除?daoSession.getPersonDao().delete(persons.get(0));Long key = daoSession.getPersonDao().getKey(persons.get(persons.size() - 1));Log.e("key=",""+key);/* //删除全部1daoSession.deleteAll(Person.class);//删除全部2daoSession.getPersonDao().deleteAll();*//*//批量删除public void deleteBatch(List<Person> notes) {daoSession.getNoteDao().deleteInTx(notes);}// 批量按主键删除public void deleteBatchByKey(List<Long> pkIds) {daoSession.getNoteDao().deleteByKeyInTx(pkIds);}*///sql删除
// daoSession.getDatabase().execSQL(deletesql);}
查询:
private void query(){//根据条件查询
// Query<Person> build = daoSession.getPersonDao().queryBuilder()
// .where(PersonDao.Properties.Sex.eq(1))
// .build();
// PersonDao.Properties.Age.gt() 大于 lt小于
// PersonDao.Properties.Age.ge() 大于等于 le小于等于
// PersonDao.Properties.Name.eq()
// PersonDao.Properties.Name.notEq("")//不等于
// List <Integer> list=new ArrayList<>();
// list.add(1);
// list.add(2);
// PersonDao.Properties.Age.in(list); 查询条件在集合中有
// PersonDao.Properties.Name.between(1,5); 在某个区间
// PersonDao.Properties.Name.isNull(); 为null
// PersonDao.Properties.Name.like("%g"); 以g结尾 _g%
// PersonDao.Properties.Name.primaryKey 判断是否是主键
// QueryBuilder<Person> builder = daoSession.getPersonDao().queryBuilder();
// builder.where(PersonDao.Properties.Age.gt(0))
// .limit(2)//取的条数
// .offset(0) //开始位置
// .orderAsc(PersonDao.Properties.Age);//升序
// List<Person> list = builder.build().list();List<Person> list = daoSession.getPersonDao().queryRaw("where "+PersonDao.Properties.Age.columnName + " >= ? and " + PersonDao.Properties.Sex.columnName + " = ?", new String[]{"2", "0"});
// daoSession.getDatabase().execSQL();for (Person person:list) {Log.e("条件查询",person.toString());}}
查询全部:
private void queryAll(){//查询全部的第一种方式/* Query<Person> build = daoSession.getPersonDao().queryBuilder().build();persons.clear();persons.addAll(build.list());for (Person person:persons) {Log.e("查询全部",person.toString());}*///查询全部的第二种方式/*List<Person> p = daoSession.getPersonDao().loadAll();for (Person person:p) {Log.e("查询全部",person.toString());}*///查询全部的第三种方式/*List<Person> p = daoSession.loadAll(Person.class);for (Person person:p) {Log.e("查询全部",person.toString());}*///查询全部的第四种方式List<Person> p = daoSession.queryBuilder(Person.class).build().list();for (Person person:p) {Log.e("查询全部",person.toString());persons.add(person);}}
greendao中没有提供全面的api根据某一条件进行,更新或者删除等,如需要可按照原生的数据库操作方式进行操作:
daoSession.getDatabase().delete()
三、数据库的升级:
查看DaoMaster中的升级方法,你会发现,在做数据库升级的方法中是删除所有的数据,然后在创建表,那么这样就无疑会导致数据库中的原有数据丢失。
/** * 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); }
}
在网上查看了一些提供的数据库升级方法,感觉都是很复杂或者不可用,下面介绍一种可用简单的数据库升级方式,此方法也是墙外找的,嘿嘿,还是拿来主义,其思路与一般数据库升级也一样,创建一个临时的表,将原来的数据保存到临时的表中,在删除原来的表,创建新表把临时表中的数据迁移到新的结构表中,删除临时表的过程。下面首先看看一个辅助类MigrationHelper:
package com.choe.greendaodemo.utils;import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;import de.greenrobot.dao.AbstractDao;
import de.greenrobot.dao.internal.DaoConfig;
import me.itangqi.dao.DaoMaster;/*** Created by cyk */
public class MigrationHelper {private static final String CONVERSION_CLASS_NOT_FOUND_EXCEPTION = "MIGRATION HELPER - CLASS DOESN'T MATCH WITH THE CURRENT PARAMETERS";private static MigrationHelper instance;public static MigrationHelper getInstance() {if(instance == null) {instance = new MigrationHelper();}return instance;}public void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {generateTempTables(db, daoClasses);DaoMaster.dropAllTables(db, true);DaoMaster.createAllTables(db, false);restoreData(db, daoClasses);}/*** 生成临时列表* @param db* @param daoClasses*/private void generateTempTables(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {for(int i = 0; i < daoClasses.length; i++) {DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);String divider = "";String tableName = daoConfig.tablename;String tempTableName = daoConfig.tablename.concat("_TEMP");ArrayList<String> properties = new ArrayList<>();StringBuilder createTableStringBuilder = new StringBuilder();createTableStringBuilder.append("CREATE TABLE ").append(tempTableName).append(" (");for(int j = 0; j < daoConfig.properties.length; j++) {String columnName = daoConfig.properties[j].columnName;if(getColumns(db, tableName).contains(columnName)) {properties.add(columnName);String type = null;try {type = getTypeByClass(daoConfig.properties[j].type);} catch (Exception exception) {exception.printStackTrace();}createTableStringBuilder.append(divider).append(columnName).append(" ").append(type);if(daoConfig.properties[j].primaryKey) {createTableStringBuilder.append(" PRIMARY KEY");}divider = ",";}}createTableStringBuilder.append(");");db.execSQL(createTableStringBuilder.toString());StringBuilder insertTableStringBuilder = new StringBuilder();insertTableStringBuilder.append("INSERT INTO ").append(tempTableName).append(" (");insertTableStringBuilder.append(TextUtils.join(",", properties));insertTableStringBuilder.append(") SELECT ");insertTableStringBuilder.append(TextUtils.join(",", properties));insertTableStringBuilder.append(" FROM ").append(tableName).append(";");db.execSQL(insertTableStringBuilder.toString());}}/*** 存储新的数据库表 以及数据* @param db* @param daoClasses*/private void restoreData(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) {for(int i = 0; i < daoClasses.length; i++) {DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]);String tableName = daoConfig.tablename;String tempTableName = daoConfig.tablename.concat("_TEMP");ArrayList<String> properties = new ArrayList();for (int j = 0; j < daoConfig.properties.length; j++) {String columnName = daoConfig.properties[j].columnName;if(getColumns(db, tempTableName).contains(columnName)) {properties.add(columnName);}}StringBuilder insertTableStringBuilder = new StringBuilder();insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" (");insertTableStringBuilder.append(TextUtils.join(",", properties));insertTableStringBuilder.append(") SELECT ");insertTableStringBuilder.append(TextUtils.join(",", properties));insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";");StringBuilder dropTableStringBuilder = new StringBuilder();dropTableStringBuilder.append("DROP TABLE ").append(tempTableName);db.execSQL(insertTableStringBuilder.toString());db.execSQL(dropTableStringBuilder.toString());}}private String getTypeByClass(Class<?> type) throws Exception {if(type.equals(String.class)) {return "TEXT";}if(type.equals(Long.class) || type.equals(Integer.class) || type.equals(long.class)) {return "INTEGER";}if(type.equals(Boolean.class)) {return "BOOLEAN";}Exception exception = new Exception(CONVERSION_CLASS_NOT_FOUND_EXCEPTION.concat(" - Class: ").concat(type.toString()));exception.printStackTrace();throw exception;}private static List<String> getColumns(SQLiteDatabase db, String tableName) {List<String> columns = new ArrayList<>();Cursor cursor = null;try {cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 1", null);if (cursor != null) {columns = new ArrayList<>(Arrays.asList(cursor.getColumnNames()));}} catch (Exception e) {Log.v(tableName, e.getMessage(), e);e.printStackTrace();} finally {if (cursor != null)cursor.close();}return columns;}
}
然后在DaoMaster中进行调用:
@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by migrating all tables data");MigrationHelper.getInstance().migrate(db,UserDao.class,ItemDao.class);}
这样就完成了数据库的升级以及数据的迁移。这种方式是不是很简单粗暴? 但在做数据库升级的时候有几点需要注意,第一点也是上文中提到的需要修改数据库的版本号,然后重新运行gernerator这个java类,重新构建实体、构造方法等。第二因为每次运行gernerator方法,都会删除之前实体和dao文件,因此为了不影响app的使用,还需要自行手动的添加一个之前表格式的构造(如果你之前是按照构造方法创建实体的话)。第三,即使数据库表结构没有变动的表最好也在migrate中传进去,因为有人说会出现一些你想不到的错误,博主也没测试过!虽然理论上不会产生错误,但还是为了保险起见,都做一个变化吧!
最后最重要的一点是,修改onUpgrade方法中的代码,否则之前所做的都会前功尽弃,如果这样上线的话,还会导致数据的丢失。这点切记!!!
忘了说了,此处代码都是写在activity中,但博主建议写在application中!
Demo下载地址:
greendao测试代码
GreenDao使用CRUD及数据库结构升级相关推荐
- Code First 下自动更新数据库结构(Automatic Migrations)
示例 Web.config <?xml version="1.0" encoding="utf-8"?> <configuration> ...
- 用 Flask 来写个轻博客 (8) — (M)VC_Alembic 管理数据库结构的升级和降级
Blog 项目源码:https://github.com/JmilkFan/JmilkFan-s-Blog 目录 目录 前文列表 扩展阅读 Alembic 查看指令 manager db 的可用选项 ...
- Android版本升级同时Sqlite数据库的升级及之前数据的保留-转
http://www.cnblogs.com/wang340/archive/2013/05/06/3063135.html http://www.eoeandroid.com/forum.php?m ...
- XTTS,又一个值得你重视的Oracle数据库迁移升级利器
嘉宾简介 杨志洪 [DBA+社群]上海发起人 数据管理专家.Oracle ACE.OCM. SHOUG/ZJOUG核心成员.DAMA会员/CCF会员,译著<Oracle核心技术>. 在Or ...
- Pre-Upgrade Utility---下载并运行Oracle数据库预升级实用程序 (文档 ID 1577379.1)
如何下载并运行Oracle数据库预升级实用程序 (文档 ID 1577379.1) 文档内容 目标 脚本来源 解决方案 脚本指导 下载并安装说明 请参阅下表以确定您需要的pre-upgrade脚本的版 ...
- 1. 设计数据库结构
这个Golang后端大师班中,我们将学习如何使用Golang.PostgreSQL和Docker从头开始设计.开发和部署一个完整的后端. 课程包括: 设计数据库模式并从中自动生成 SQL 代码 深入了 ...
- 数据库结构比对,再初始数据比对方法
在公司项目中,我有幸负责制作"升级脚本".升级脚本,无疑两步,先结构比对,再初始数据比对. 一.结构比对 结构比对比较简单,使用navicat for mysql 工具," ...
- 魔兽私服服务端 MANGOS 数据库结构表中文解释
MANGOS 数据库结构表 ============================================= achievement_reward 巫妖王的奖励成就 ...
- GreenDao3.2.2集成使用以及数据库的升级
GreenDao3.2.2集成使用以及数据库的升级 一概念 是一个将对象映射到 SQLite 数据库中的轻量且快速的 ORM 解决方案. ...
最新文章
- 小冰拉开人生第二幕:AI时代的“Office”问世,你的虚拟男友“复活”了
- LeSS is More - 大规模敏捷开发框架LeSS实践(一)
- MikroTik RouterOS获取在线终端和在线IP总数并自动对IP做限速(转)
- 英语影视台词---八、the shawshank redemption
- 公众号新上线微信小游戏(疯狂猜图)
- 密码学笔记——eval(function(p,a,c,k,e,d) 加密破解
- 1. K近邻算法(KNN)
- php表格位于页面中央,怎么为表格所在的页面添加编辑限制保护
- 华为开发者大会鸿蒙2.0系统,鸿蒙2.0来了!华为开发者大会HDC 2020宣布
- 关于微信和微信小程序
- Linux虚拟机挂载新的硬盘
- scanf的用法大全
- 【转载】 mybatis入门系列四之动态SQL
- KGB知识图谱深入挖掘金融行业的知识关联
- QNX 7.1 交叉编译 boost 1.76
- 香港美国CERA机房你怎么选择?
- sencha touch 相机,相册调用
- 通用计算机的发展历程是巨型机大型机小型机,计组1——计算机系统概述
- 汪国真:只要热爱生命,一切都在意料之中
- STM32入门笔记——GPIO的初始化
热门文章
- 基于springBoot的开源运维监控工具——WGCLOUD
- 蓝牙权限管理android,基于蓝牙与Android设备的控制系统设计
- 框架学习——带你了解SpringBoot框架
- 《AANet: Adaptive Aggregation Network for Efficient Stereo Matching》
- SCAU 9502 ARDF
- c语言程序设计答案第6章,C语言程序设计答案(黄保和编)第6章
- 蒙特利尔大学工学院流程
- 无稳态多谐振荡器分析
- 2021年量子计算机奖,2021美国天才奖公布获奖名单!
- 第一章 信息安全的基本元素