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 plugin
android{
...
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
表明这个实体类会在数据库中生成一个与之相对应的表
属性:
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 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;//发送方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类型
@Entity
public 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
使用@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 是没有默认值得,网上说作者不屑于设置,网上搜索后,有很多是修改自动生成的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的建表语句还是原来的
该注解用于当实体类中有字段不添加到数据表时使用
@Entity
public class User implements Serializable {
@Transient
private static final long serialVersionUID = 1L;
@Id(autoincrement = true)
private Long id
这个时候添加一行
@Transient
private static final long serialVersionUID = 1L;
按照上面操作后Build> make project后再自动生成Dao的时候还会生成
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);
}
}
}