GreenDao使用CRUD及数据库结构升级
来源:互联网 发布:数据交换方式有哪些 编辑:程序博客网 时间:2024/05/02 02:09
今天要说的是众所周知的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); /* //删除全部1 daoSession.deleteAll(Person.class); //删除全部2 daoSession.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中进行调用:
@Override public 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及数据库结构升级
- GreenDao数据库结构升级
- GreenDao 数据库简单使用及数据库升级更新
- GreenDAO 3.0 数据库集成,使用,升级
- Android GreenDao使用(三)数据库升级
- Android GreenDao实现CRUD和升级详解
- GreenDAO数据库版本升级
- GreenDAO数据库版本升级
- GreenDao数据库升级解决方案
- greendao数据库升级
- greenDao数据库升级
- Greendao 数据库升级解决方案
- GreenDao之数据库升级
- greenDao数据库升级
- GreenDao数据库升级
- GreenDao数据库升级方案
- greendao 数据库升级
- greenDao数据库升级
- NYOJ 17 单调递增最长子序列
- 使用Fragment应用放置后台很久被系统回收出现crash
- git学习笔记-----第一次提交
- Android自定义控件—CouponsView
- 队列和堆栈
- GreenDao使用CRUD及数据库结构升级
- Web服务器启动时加载缓存
- drawRect方法&贝塞尔路径&上下文
- 【light-oj】-1104 - Birthday Paradox(数学,概率)
- 发布web总结
- python的import与from...import的不同之处
- CoreData初识
- 大数据入门,你需要懂这四个常识
- Wireshark的Pcap文件格式分析及解析源码