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 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 注解详解

@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

@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后还原成原先建表语句,还有人说是修改实体类

@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 {
    @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>[] 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>[] mDaoClasses;
    public DBUpdateOpenHelper(Context context, String name, Class>... daoClasses) {
        super(context, name);
        mDaoClasses=daoClasses;
    }

    public DBUpdateOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, Class>... 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>... 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>... daoClasses) {
        weakListener = new WeakReference<>(listener);
        migrate(db, daoClasses);
    }

    public static void migrate(Database database, MigrationHelper.ReCreateAllTableListener listener, Class>... daoClasses) {
        weakListener = new WeakReference<>(listener);
        migrate(database, daoClasses);
    }

    public static void migrate(Database database, Class>... 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>... 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>... daoClasses) {
        reflectMethod(db, "dropTable", ifExists, daoClasses);
        printLog("【Drop all table by reflect】");
    }

    public static void createAllTables(Database db, boolean ifNotExists,  Class>... 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>... 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>... 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,框架&库学习集,数据库,sqlite,android)