GreenDao 3.3.0 基本使用与入门 (一)
GreenDao 引入
GreenDao官网
GreenDaoAPI地址
GreenDao 3.3.0 基本使用与入门(一)
GreenDao 3.3.0 多表关联使用(二)
GreenDao 3.3.0 增删改查的使用(三)
Porject 目录下 build.gradle 下添加配置
dependencies { classpath 'com.android.tools.build:gradle:4.0.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'//dagger2 apt 配置 classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0' //add plugin greendao配置 }
接下来在app的 build.gradle 中添加
apply plugin: 'org.greenrobot.greendao' // apply pluginandroid{ ... greendao { schemaVersion 5 //数据库版本号,升级数据库需要修改版本号 daoPackage 'com.greendao.dao' //一般为app包名+生成文件的文件夹名 targetGenDir 'src/main/java' //自动生成的greendao代码存放路径 } }dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation 'org.greenrobot:greendao:3.3.0' // add library ....}
这样就完成GreenDao的引入了
Entity 注解详解
@Entity
表明这个实体类会在数据库中生成一个与之相对应的表
属性:
-
schema:告知GreenDao当前实体属于哪个 schema
-
schema active:标记一个实体处于活跃状态,活动实体有更新、删除和刷新方法
-
nameInDb:在数据库中使用的别名,默认使用的是实体的类名,
-
indexes:定义索引,可以跨越多个列
-
createInDb:标记创建数据库表(默认:true)
-
generateConstructors 自动创建全参构造方法(同时会生成一个无参构造方法)(默认:true)
-
generateGettersSetters 自动生成 getters and setters 方法(默认:true)
@Entity( schema = "myschema", active = true, nameInDb = "AWESOME_USERS", indexes = { @Index(value = "name DESC", unique = true) }, createInDb = true, generateConstructors = false, generateGettersSetters = true)public class User { ...}
@Id
对应数据表中的 Id 字段
@Entitypublic class UserList { @Id(autoincrement = true)//主键自动增长 private Long id;
@Index
使用@Index作为一个属性来创建一个索引,默认是使用字段名
@Entitypublic class User { @Id private Long id; @Index(unique = true) private String name;}
@OrderBy
更加某一字段排序 ,例如:@OrderBy(“date ASC”)
@OrderBy("date ASC")private List orders;
@Property
设置一个非默认关系映射所对应的列名,默认是使用字段名,例如:
@Entity (nameInDb = “User”)public class User {@Property(nameInDb = “userName”)private String userName;}
@NotNull
设置数据库表当前列不能为空
@Transient
添加此标记后不会生成数据库表的列
@Unique
表名该属性在数据库中只能有唯一值
@Entity public class User {@Id private Long id;@Unique private String name;}
注意事项
由于greenDAO 3.0 生成的字段添加了非空约束。字段的类型设置为基本类型(如:int)默认会添加非空约束,字段类型设置为对象类型(如:Integer)默认不会添加非空约束,而且最终生成的sql会使用对象类型
定义int类型
@Entitypublic class CommentList { @Id(autoincrement = true) private Long id; private int sendId =0;//发送方ID private int receiveId;//接收方ID //------ CommentListDao.java ------ /** Creates the underlying database table. */ public static void createTable(Database db, boolean ifNotExists) { String constraint = ifNotExists? "IF NOT EXISTS ": ""; db.execSQL("CREATE TABLE " + constraint + "\"COMMENT_LIST\" (" + // "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id "\"SEND_ID\" INTEGER NOT NULL ," + // 1: sendId "\"RECEIVE_ID\" INTEGER NOT NULL ," + // 2: receiveId "\"SEND_TYPE\" INTEGER NOT NULL ," + // 3: sendType
定义Integer类型
@Entitypublic class CommentList { @Id(autoincrement = true) private Long id; private Integer sendId =0;//发送方ID private Integer receiveId=0;//接收方ID private Integer sendType=0;//发送方用户类型 // ------ CommentListDao.java ------ /** Creates the underlying database table. */ public static void createTable(Database db, boolean ifNotExists) { String constraint = ifNotExists? "IF NOT EXISTS ": ""; db.execSQL("CREATE TABLE " + constraint + "\"COMMENT_LIST\" (" + // "\"_id\" INTEGER PRIMARY KEY AUTOINCREMENT ," + // 0: id "\"SEND_ID\" INTEGER," + // 1: sendId "\"RECEIVE_ID\" INTEGER," + // 2: receiveId "\"SEND_TYPE\" INTEGER," + // 3: sendType "\"RECEIVE_TYPE\" INTEGER," + // 4: receiveType
@Property 建表字段命名
使用@Entity建表的时候,默认情况下所有的表名和字段名都是大写的,使用nameInDb 定义表名和类名
@Entity(nameInDb ="User")public class User implements Serializable { @Transient private static final long serialVersionUID = 1L; @Id(autoincrement = true ) private Long id; @Property(nameInDb = "name") private String name="Tom";
GreenDao 3.3.0版本 设置默认值
默认GreenDao 是没有默认值得,网上说作者不屑于设置,网上搜索后,有很多是修改自动生成的xxxDao 里面的 createTable 中的建表SQL语句
/** 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 ," + // 0: id "\"NAME\" TEXT," + // 1: name "\"AGE\" INTEGER NOT NULL ," + // 2: age "\"SEX\" TEXT," + // 3: sex "\"HEIGHT\" INTEGER NOT NULL ," + // 4: height "\"WEIGHT\" TEXT);"); // 5: weight }
也许由于版本的不同,反正3.3没有成功,Android Studio 执行Make Project后还原成原先建表语句,还有人说是修改实体类
@Entitypublic class User { @Id(autoincrement = true) private Long id; private String name="Tom"; private int age=0; private String sex; private int height; private String weight;
这里有一个坑,这样的方式是可行的,但是如果你这里修改后,去观察UserDao中的建表语句,还是和之前一样是没有变化的.并没有带上 Default 默认值
这样修改后,在插入一条数据,不带默认值的字段赋值后查看数据表,就会发现默认值已经有了,但是UserDao的建表语句还是原来的
@Transient 注解
该注解用于当实体类中有字段不添加到数据表时使用
@Entitypublic class User implements Serializable { @Transient private static final long serialVersionUID = 1L; @Id(autoincrement = true) private Long id
实体类实现 Serializable 报错
这个时候添加一行
@Transient
private static final long serialVersionUID = 1L;
DaoMaster DaoSession 数据操作和表管理
按照上面操作后Build> make project后再自动生成Dao的时候还会生成
- DaoMaster 建表注册管理表
- DaoSession 对表进行增删改查
public class GreenDBManager implements IDBManager { private static final String DB_NAME = "User.db"; //数据库名 private DaoSession mDaoSession; //private UserTableManager mUserTableManager; private TablesComponent mTablesComponent; private DaoMaster daoMaster; public GreenDBManager init(Application application, String dbName) { Integer dbVersion = DaoMaster.SCHEMA_VERSION; mApplication = application; Class<? extends AbstractDao<?, ?>>[] classes = new Class[]{UserDao.class, UserListDao.class}; //DBUpdateOpenHelper对DaoMaster进行初始化,这样就可以实现数据库升级时的数据迁移 DBUpdateOpenHelper updateOpenHelper = new DBUpdateOpenHelper(application, dbName, mclasses); daoMaster = new DaoMaster(updateOpenHelper.getWritableDatabase()); //9.0的数据库默认启用了wal,用低版本的sqlite工具是无法查看的,想要兼容可以禁用wal updateOpenHelper.getWritableDatabase().disableWriteAheadLogging();//启用 enableWriteAheadLogging mDaoSession = daoMaster.newSession(); //查看数据库更新版本时数据迁移的log MigrationHelper.DEBUG = false; //数据库增删改查时的log QueryBuilder.LOG_SQL = false; QueryBuilder.LOG_VALUES = false; //清空缓存 mDaoSession.clear(); return this; } @Override public DaoSession getDaoSession() { if (mDaoSession==null){ mDaoSession=daoMaster.newSession(); } return mDaoSession; }
在Application 里面初始化init后,就可通过 getDaoSession 对所有表进行操作处理了
DBUpdateOpenHelper.java
public class DBUpdateOpenHelper extends DaoMaster.OpenHelper { Class<? extends AbstractDao<?, ?>>[] mDaoClasses; public DBUpdateOpenHelper(Context context, String name, Class<? extends AbstractDao<?, ?>>... daoClasses) { super(context, name); mDaoClasses=daoClasses; } public DBUpdateOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, Class<? extends AbstractDao<?, ?>>... daoClasses) { super(context, name, factory); mDaoClasses=daoClasses; } @Override public void onCreate(Database db) { DaoMaster.createAllTables(db, false); } @Override public void onUpgrade(Database db, int oldVersion, int newVersion) { //把需要管理的数据库表DAO作为最后一个参数传入到方法中 MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() { @Override public void onCreateAllTables(Database db, boolean ifNotExists) { DaoMaster.createAllTables(db, ifNotExists); } @Override public void onDropAllTables(Database db, boolean ifExists) { DaoMaster.dropAllTables(db, ifExists); } }, mDaoClasses); }}
MigrationHelper.java 用于数据库升级时数据迁移
package com.zk.greendb.helper;import android.database.Cursor;import android.database.SQLException;import android.database.sqlite.SQLiteDatabase;import android.text.TextUtils;import android.util.Log;import org.greenrobot.greendao.AbstractDao;import org.greenrobot.greendao.database.Database;import org.greenrobot.greendao.database.StandardDatabase;import org.greenrobot.greendao.internal.DaoConfig;import java.lang.ref.WeakReference;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.util.ArrayList;import java.util.Arrays;import java.util.List;/** * 用于数据库升级时数据迁移 * from https://github.com/yuweiguocn/GreenDaoUpgradeHelper * * please call {@link #migrate(SQLiteDatabase, Class[])} or {@link #migrate(Database, Class[])} */public class MigrationHelper { public static boolean DEBUG = false; private static final String TAG= MigrationHelper.class.getSimpleName(); private static final String SQLITE_MASTER = "sqlite_master"; private static final String SQLITE_TEMP_MASTER = "sqlite_temp_master"; private static WeakReference weakListener; public interface ReCreateAllTableListener{ void onCreateAllTables(Database db, boolean ifNotExists); void onDropAllTables(Database db, boolean ifExists); } public static void migrate(SQLiteDatabase db, Class<? extends AbstractDao<?, ?>>... daoClasses) { printLog("【The Old Database Version】" + db.getVersion()); Database database = new StandardDatabase(db); migrate(database, daoClasses); } public static void migrate(SQLiteDatabase db, MigrationHelper.ReCreateAllTableListener listener, Class<? extends AbstractDao<?, ?>>... daoClasses) { weakListener = new WeakReference<>(listener); migrate(db, daoClasses); } public static void migrate(Database database, MigrationHelper.ReCreateAllTableListener listener, Class<? extends AbstractDao<?, ?>>... daoClasses) { weakListener = new WeakReference<>(listener); migrate(database, daoClasses); } public static void migrate(Database database, Class<? extends AbstractDao<?, ?>>... daoClasses) { printLog("【Generate temp table】start"); generateTempTables(database, daoClasses); printLog("【Generate temp table】complete"); MigrationHelper.ReCreateAllTableListener listener = weakListener.get(); if (listener != null) { listener.onDropAllTables(database, true); printLog("【Drop all table by listener】"); listener.onCreateAllTables(database, false); printLog("【Create all table by listener】"); } else { dropAllTables(database, true, daoClasses); createAllTables(database, false, daoClasses); } printLog("【Restore data】start"); restoreData(database, daoClasses); printLog("【Restore data】complete"); } private static void generateTempTables(Database db, Class<? extends AbstractDao<?, ?>>... daoClasses) { for (int i = 0; i < daoClasses.length; i++) { String tempTableName = null; DaoConfig daoConfig = new DaoConfig(db, daoClasses[i]); String tableName = daoConfig.tablename; if (!isTableExists(db, false, tableName)) { printLog("【New Table】" + tableName); continue; } try { tempTableName = daoConfig.tablename.concat("_TEMP"); StringBuilder dropTableStringBuilder = new StringBuilder(); dropTableStringBuilder.append("DROP TABLE IF EXISTS ").append(tempTableName).append(";"); db.execSQL(dropTableStringBuilder.toString()); StringBuilder insertTableStringBuilder = new StringBuilder(); insertTableStringBuilder.append("CREATE TEMPORARY TABLE ").append(tempTableName); insertTableStringBuilder.append(" AS SELECT * FROM ").append(tableName).append(";"); db.execSQL(insertTableStringBuilder.toString()); printLog("【Table】" + tableName +"\n ---Columns-->"+getColumnsStr(daoConfig)); printLog("【Generate temp table】" + tempTableName); } catch (SQLException e) { Log.e(TAG, "【Failed to generate temp table】" + tempTableName, e); } } } private static boolean isTableExists(Database db, boolean isTemp, String tableName) { if (db == null || TextUtils.isEmpty(tableName)) { return false; } String dbName = isTemp ? SQLITE_TEMP_MASTER : SQLITE_MASTER; String sql = "SELECT COUNT(*) FROM " + dbName + " WHERE type = ? AND name = ?"; Cursor cursor=null; int count = 0; try { cursor = db.rawQuery(sql, new String[]{"table", tableName}); if (cursor == null || !cursor.moveToFirst()) { return false; } count = cursor.getInt(0); } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) cursor.close(); } return count > 0; } private static String getColumnsStr(DaoConfig daoConfig) { if (daoConfig == null) { return "no columns"; } StringBuilder builder = new StringBuilder(); for (int i = 0; i < daoConfig.allColumns.length; i++) { builder.append(daoConfig.allColumns[i]); builder.append(","); } if (builder.length() > 0) { builder.deleteCharAt(builder.length() - 1); } return builder.toString(); } public static void dropAllTables(Database db, boolean ifExists, Class<? extends AbstractDao<?, ?>>... daoClasses) { reflectMethod(db, "dropTable", ifExists, daoClasses); printLog("【Drop all table by reflect】"); } public static void createAllTables(Database db, boolean ifNotExists, Class<? extends AbstractDao<?, ?>>... daoClasses) { reflectMethod(db, "createTable", ifNotExists, daoClasses); printLog("【Create all table by reflect】"); } /** * dao class already define the sql exec method, so just invoke it */ private static void reflectMethod(Database db, String methodName, boolean isExists, Class<? extends AbstractDao<?, ?>>... daoClasses) { if (daoClasses.length < 1) { return; } try { for (Class cls : daoClasses) { Method method = cls.getDeclaredMethod(methodName, Database.class, boolean.class); method.invoke(null, db, isExists); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } private static void restoreData(Database 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"); if (!isTableExists(db, true, tempTableName)) { continue; } try { // get all columns from tempTable, take careful to use the columns list List columns = getColumns(db, tempTableName); ArrayList properties = new ArrayList<>(columns.size()); for (int j = 0; j < daoConfig.properties.length; j++) { String columnName = daoConfig.properties[j].columnName; if (columns.contains(columnName)) { properties.add(columnName); } } if (properties.size() > 0) { final String columnSQL = TextUtils.join(",", properties); StringBuilder insertTableStringBuilder = new StringBuilder(); insertTableStringBuilder.append("INSERT INTO ").append(tableName).append(" ("); insertTableStringBuilder.append(columnSQL); insertTableStringBuilder.append(") SELECT "); insertTableStringBuilder.append(columnSQL); insertTableStringBuilder.append(" FROM ").append(tempTableName).append(";"); db.execSQL(insertTableStringBuilder.toString()); printLog("【Restore data】 to " + tableName); } StringBuilder dropTableStringBuilder = new StringBuilder(); dropTableStringBuilder.append("DROP TABLE ").append(tempTableName); db.execSQL(dropTableStringBuilder.toString()); printLog("【Drop temp table】" + tempTableName); } catch (SQLException e) { Log.e(TAG, "【Failed to restore data from temp table 】" + tempTableName, e); } } } private static List getColumns(Database db, String tableName) { List columns = null; Cursor cursor = null; try { cursor = db.rawQuery("SELECT * FROM " + tableName + " limit 0", null); if (null != cursor && cursor.getColumnCount() > 0) { columns = Arrays.asList(cursor.getColumnNames()); } } catch (Exception e) { e.printStackTrace(); } finally { if (cursor != null) cursor.close(); if (null == columns) columns = new ArrayList<>(); } return columns; } private static void printLog(String info){ if(DEBUG){ Log.d(TAG, info); } }}
更多相关文章
- android 短信 彩信 数据库 转
- Android(安卓)TabWidget底部显示效果
- android 5.0系统主题颜色相关
- Android中自制通讯录中显示出数据库中的姓名和电话号码进行打电
- 方法数据库android轻量型数据库sqlite的使用方法汇总
- Android如何连接SQLServer数据库
- Android4.4.2配置修改记录
- Android(安卓)SQLiteOpenHelper的使用心得
- Android(安卓)USER 版本与ENG 版本的差异--MTK官方解释