当前位置: 代码迷 >> 综合 >> GreenDao 3.3.0 基本使用与入门 (一)
  详细解决方案

GreenDao 3.3.0 基本使用与入门 (一)

热度:63   发布时间:2024-01-30 22:58:23.0

GreenDao 引入

GreenDao官网
GreenDaoAPI地址

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 字段

@Entity
public class UserList {@Id(autoincrement = true)//主键自动增长private Long id;

@Index
使用@Index作为一个属性来创建一个索引,默认是使用字段名

@Entity
public class User {@Id private Long id;@Index(unique = true)private String name;
}

@OrderBy
更加某一字段排序 ,例如:@OrderBy(“date ASC”)

@OrderBy("date ASC")
private List<Order> 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类型

@Entity
public class CommentList {@Id(autoincrement = true)private Long id;private int sendId =0;//发送方IDprivate 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类型

@Entity
public class CommentList {@Id(autoincrement = true)private Long id;private Integer sendId =0;//发送方IDprivate Integer receiveId=0;//接收方IDprivate 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 {@Transientprivate 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后还原成原先建表语句,还有人说是修改实体类

@Entity
public 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 注解

该注解用于当实体类中有字段不添加到数据表时使用

@Entity
public class User implements Serializable {@Transientprivate 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工具是无法查看的,想要兼容可以禁用walupdateOpenHelper.getWritableDatabase().disableWriteAheadLogging();//启用 enableWriteAheadLoggingmDaoSession = daoMaster.newSession();//查看数据库更新版本时数据迁移的logMigrationHelper.DEBUG = false;//数据库增删改查时的logQueryBuilder.LOG_SQL = false;QueryBuilder.LOG_VALUES = false;//清空缓存mDaoSession.clear();return this;}@Overridepublic 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;}@Overridepublic void onCreate(Database db) {DaoMaster.createAllTables(db, false);}@Overridepublic void onUpgrade(Database db, int oldVersion, int newVersion) {//把需要管理的数据库表DAO作为最后一个参数传入到方法中MigrationHelper.migrate(db, new MigrationHelper.ReCreateAllTableListener() {@Overridepublic void onCreateAllTables(Database db, boolean ifNotExists) {DaoMaster.createAllTables(db, ifNotExists);}@Overridepublic 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<ReCreateAllTableListener> 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 listList<String> columns = getColumns(db, tempTableName);ArrayList<String> 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<String> getColumns(Database db, String tableName) {List<String> 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);}}
}