In the previous development process, the database was basically written by Litepal or SQlite. Recently, the new environment was changed, and the database used by the company was GreenDao. Under various circumstances, I am ready to learn about GreenDao and write an article to record the basic use of GreenDao. If you are Android developers, you encountered any problems in the development of the road, welcome to my public number to leave a message to me, we discuss together, join the Android development discussion group, progress together! Article reprint please indicate the source.

This paper is mainly explained from the following aspects

  1. The database structure of the storage
  2. Pros and cons of GreenDao
  3. GreenDao usage configuration
  4. Use GreenDao to add, delete, change and check data
  5. GreenDao annotations are used
  6. GreenDao relationship handling
  7. GreenDao upgrade
  8. GreenDao database encryption
  9. Project address: AserbaosAndroid
  10. conclusion
  11. Refer to the blog

Let’s first look at a wave of final renderings: there is the project address at the end of the article;

1. Storage database structure

Before learning database, we first have to design their own database, no nonsense, the following is my study of the database structure, all the data behind please refer to this figure for learning:

2. Introduction to GreenDao

Simple introduction of GreenDao, too troublesome can directly jump to the use of GreenDao start to see.

What is GreenDao?

GreenDAO is an open source Android ORM(” Object/relational mapping “) that saves development time in our database development process through ORM(called “object/relational mapping”)!

GreenDao official documentation

  1. GreenDao: Android ORM for your SQLite database
  2. Github address for GreenDao
  3. GreenDao’s Google discussion forum
  4. GreenDao Encryption SQLCipher for Android official description address
  5. GreenDao uses documents

What does GreenDao do?

With GreenDao, we can operate databases more quickly. We can store, update, delete, and query Java objects using simple object apis.

Pros and cons of GreenDao?

  1. GreenDao, OrmLite and ActiveAndroid ORM solutions:

  2. Easy-to-use powerful apis that cover relationships and connections;

  3. Minimum memory consumption;

  4. Small library size (<100KB) to keep build times low and avoid 65K method limits;

  5. Database encryption: greenDAO supports SQLCipher to ensure user data security;

3. Use of GreenDao

GreenDao three core classes: DaoMaster, DaoSession, XXXDao, these three classes are automatically created, do not need to write your own creation!

  • DaoMaster: : DaoMaster holds database objects (SQLiteDatabase) and manages DAO classes (rather than objects) for a particular schema. It has static methods to create tables or delete them. Its internal classes OpenHelper and DevOpenHelper are SQLiteOpenHelper implementations that create schemas in the SQLite database.
  • DaoSession: Manages all available DAO objects for a particular schema, which you can retrieve using one of the getter methods. DaoSession also provides some general persistence methods, such as insert, load, update, refresh, and delete of entities.
  • XXXDao: Data access object (DAO) persists and queries entities. For each entity, greenDAO generates a DAO. It has more persistence methods than DaoSession, such as count, loadAll, and insertInTx.
  • Entities: persistent object. Typically, entity objects represent a database line using standard Java attributes (such as a POJO or JavaBean).

1. Import Gradle plug-in and generate Dao code

To use GreenDao in your Android project, you need to add the GreenDao Gradle plugin and add the GreenDao library:

  1. The import plug-in
// Add to the build.gradle file of the Project: buildscript { repositories { jcenter() mavenCentral() // add repository } dependencies { classpath'com. Android. Tools. Build: gradle: 3.1.2'
        classpath 'org. Greenrobot: greendao - gradle - plugin: 3.2.2' // add plugin
    }
}

Copy the code
  1. Configure dependencies
// Add the apply plugin to the build.gradle file of Moudle:app.'com.android.application'
apply plugin: 'org.greenrobot.greendao' // apply plugin
 
dependencies {
    implementation 'org. Greenrobot: greendao: 3.2.2' // add library
}
Copy the code
  1. Configure database information
Greendao {schemSchemAversion 1 // Database version number daoPackage'com.aserbao.aserbaosandroid.functions.database.greenDao.db'// Set DaoMaster, DaoSession, and Dao package name targetGenDir'src/main/java'// set DaoMaster/DaoSession/Dao directory. GenerateTestsfalse/ / set totrueTo automatically generate unit tests. targetGenDirTests'src/main/java'// The base directory where the generated unit tests should be stored. Default is SRC/androidTest/Java. }Copy the code

Build> Make Project in Android Studio, rewrite the Build Project, GreenDao integration is complete!

2. Create a storage object entity class

To store data with GreenDao, simply declare the @Entity annotation in front of the stored data class and have GreenDao generate the necessary code for it:

@Entity
public class Student {
    @Id(autoincrement = true) Long id; @Unique int studentNo; // int age; // Age String telPhone; // Phone number String sex; // Gender String name; // Name String address; // Home address String schoolName; // School name String grade; // What grade...... Getter and setter and constructor method...... }Copy the code

3. GreenDao initialization

We can maintain a global session in the Application. We use Applicaiton to initialize the database:

/** * GreenDao is initialized directly in Application */ private voidinitGreenDao() {
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "aserbao.db");
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
    }
    
    private DaoSession daoSession;
    public DaoSession getDaoSession() {
        return daoSession;
    }
Copy the code

After initialization, re-rebuild the project and you will find that three class files are generated in the targetGenDir directory. This is automatically generated by GreenDao! That the database has been connected, we only need to add, delete, change and check the database operation on the line. Let’s Go!

4. Use GreenDao to add, delete, modify and check

Increased 1.

Insert () inserts data

 @Override
    public void insertData(Thing s) {
     DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
            for (int i = 0; i < 1000; i++) {
               Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("Male");
                        } else {
                            student.setSex("Female");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "Age"); student.setSchoolName(RandomValue.getSchoolName()); daoSession.insert(student); }}Copy the code

**insertOrReplace()** Data exists to replace, data does not exist to insert

@Override
    public void insertData(Thing s) {
    DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
            for (int i = 0; i < 1000; i++) {
                 Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("Male");
                        } else {
                            student.setSex("Female");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "Age"); student.setSchoolName(RandomValue.getSchoolName()); daoSession.insertOrReplace(student); // Insert or replace}}Copy the code

2. Delete

There are two ways to delete: delete() and deleteAll(); Delete a single node and delete all nodes.

 @Override
    public void deleteData(Student s) {
    	DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        daoSession.delete(s);
    }

Copy the code
 @Override
    public void deleteAll() {
    	DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        daoSession.deleteAll(Student.class);
    }
Copy the code

3. Change

Update to modify:

@Override
    public void updataData(Student s) {
    	DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        daoSession.update(s);
    }
Copy the code

4. Check

The query methods are as follows:

  • LoadAll () : Queries all data.
  • QueryRaw () : Query by condition.
  • QueryBuilder () : Facilitates query creation, as explained below.
 public List queryAll(){
        List<Student> students = daoSession.loadAll(Student.class);
        return students;
    }
Copy the code
  @Override
    public void queryData(String s) {
       List<Student> students = daoSession.queryRaw(Student.class, " where id = ?", s);
        mDataBaseAdapter.addNewStudentData(students);
    }
Copy the code

Use of QueryBuilder

Writing SQL can be difficult and prone to errors that are only noticed at run time. The QueryBuilder class lets you create your entities without SQL custom queries, and helps detect errors already at compile time.

Let’s start with the common QueryBuilder methods:

  • where(WhereCondition cond, WhereCondition… CondMore): query condition, parameter is the query condition!
  • or(WhereCondition cond1, WhereCondition cond2, WhereCondition… CondMore): nested condition or, used as with or.
  • and(WhereCondition cond1, WhereCondition cond2, WhereCondition… CondMore): nested condition and, used in the same way as and.
  • Join (Property sourceProperty, Class destinationEntityClass): a multi-table query. List () returns list, and the other three return values all implement Closeable. Note that the cursor is closed when no data is used:
  • List () All entities are loaded into memory. The result is usually an ArrayList without magic. Easiest to use.
  • The listLazy () entity is loaded into memory on demand. Once an element in the list is accessed for the first time, it is loaded and cached for future use. It must be closed.
  • ListLazyUncached () “virtual” list of entities: Any access to list elements causes their data to be loaded from the database. It must be closed.
  • ListIterator () lets us iterate over the results by loading data on demand (lazy). Data is not cached. It must be closed.
  • OrderAsc () in ascending order by an attribute;
  • OrderDesc () in descending order by an attribute;

GreenDao SQL statement in the abbreviation, we also understand the source in the Property, when you can use their own point into the query:

  • Eq () : “equal (=? ‘) is equal to “;
  • NotEq () : “not equal (‘<>? ‘)” does not equal;
  • Like () : “Like?” Value is equal to the;
  • Between () : “Between? AND ?” Take the middle range;
  • In () : “in(” in command;
  • NotIn () : “NOT IN (” NOT IN command;
  • Gt () : “>?” Greater than;
  • Lt () : ”
  • Ge () : “> =?” Greater than or equal to;
  • Le () : “< =? “Less than or equal to;
  • IsNull () : “IS NULL”;
  • IsNotNull () : “IS NOT NULL”;

1. Use QueryBuilder to perform query operations

1. Query simple conditions

Alter TABLE Student select * from Student;

  public List queryAllList(){ DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession(); QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class); List<Student> list = qb.list(); // Check all datareturn list;
    }
Copy the code

Select * from Student where Name = ‘1’;

 public List queryListByMessage(String name){
		 DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);
        QueryBuilder<Student> studentQueryBuilder = qb.where(StudentDao.Properties.Name.eq("一")).orderAsc(StudentDao.Properties.Name); List<Student> studentList = studentQueryBuilder.list(); // Check the current datareturn list;
    }
Copy the code

2. Original query

Query through the original SQL query statement! In fact, the purpose of QueryBuilder mentioned above is to facilitate the preparation of SQL query statements, to avoid our own mistakes in the process of writing! A brief introduction to write database through QueryBuilder, the method is as follows:

public List queryListBySqLDaoSession = ((AserbaoApplication) getApplication()).getdaosession (); Query<Student> query = daoSession.queryBuilder(Student.class).where( new WhereCondition.StringCondition("_ID IN " +
                        "(SELECT _ID FROM STUDENT WHERE _ID > 5)")
        ).build();
        List<Student> list = query.list();
        return list;
    }
Copy the code

3. Nested query conditions

Select * from ‘Id’ where Id > 5 and Id < 10 and Name = ‘1’;

public List queryList(){
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class);
        qb = daoSession.queryBuilder(Student.class);
        List<Student> list2 = qb.where(StudentDao.Properties.Name.eq("一"),
                qb.and(StudentDao.Properties.Id.gt(5),
                        StudentDao.Properties.Id.le(50))).list();
        return  list2;
    }
Copy the code

Take 10 data whose Id is greater than 1 and offset 2

 public List queryListByOther(){ DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession(); QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class); // if Id > 1, result = [2,3,4,5,6,7,8,9,10,11]; / / offset (2) the said offset back 2, the results for,5,6,7,8,9,10,11,12,13 [4]; List<Student> list = qb.where(StudentDao.Properties.Id.gt(1)).limit(10).offset(2).list();return list;
    }
Copy the code

4. Perform multiple search operations

After you build the Query using QueryBuilder, you can reuse the Query object for later execution. This is more efficient than always creating new Query objects. If the query parameters have not changed, you can call the list/unique method again. The value of a conditional parameter can be modified using the setParameter method:

 public List queryListByMoreTime(){ DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession(); QueryBuilder<Student> qb = daoSession.queryBuilder(Student.class); // if Id > 1, result = [2,3,4,5,6,7,8,9,10,11]; / / offset (2) the said offset back 2, the results for,5,6,7,8,9,10,11,12,13 [4]; Query<Student> query = qb.where(StudentDao.Properties.Id.gt(1)).limit(10).offset(2).build(); List<Student> list = query.list(); // SetParameter to modify the above query condition. For example, set the above condition to 10 values whose Id is greater than 5 and offset by two digits. Query. SetParameter (0, 5); List<Student> list1 = query.list();return list1;
    }
Copy the code

5. Use QueryBuilder in multiple threads

If you use a Query in multiple threads, you must call forCurrentThread () to get the Query instance of the current thread. The object instance of Query is bound to the owning thread that builds the Query.

This allows you to safely set parameters on Query objects without interference from other threads. If another thread tries to set parameters on a query or execute a query bound to another thread, an exception is thrown. Like this, you don’t need synchronous statements. In fact, you should avoid locking, because this can cause deadlocks if concurrent transactions use the same Query object.

Each time forCurrentThread () is called, the parameter is set as the initial parameter when the query is built using its builder.

2. Use QueryBuilder to delete data in batches

Batch deletes using QueryBuilder do not delete a single entity, but all entities that meet certain criteria. To perform a batch delete, create QueryBuilder, call its buildDelete () method, and then execute the returned DeleteQuery.

Example: Delete all other data in the database whose ID is greater than 5

public boolean deleteItem(){
        DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
        QueryBuilder<Student> where = daoSession.queryBuilder(Student.class).where(StudentDao.Properties.Id.gt(5));
        DeleteQuery<Student> deleteQuery = where.buildDelete();
        deleteQuery.executeDeleteWithoutDetachingEntities();
        return false;
    }
Copy the code

5. Explanatory notes

GreenDao 3 uses annotations to define models and entities. As mentioned earlier, annotations can be used to quickly build database tables, including setting primary keys, increments, whether values are unique, etc…

Let’s look at a simple use of annotations:

@Entity
public class Student {
    @Id(autoincrement = true) Long id; @Unique int studentNo; // int age; // Age String telPhone; // Phone number String sex; // Gender String name; // Name String address; // Home address String schoolName; // School name String grade; // What grade...... Getter and setter and constructor method...... }Copy the code

1. @ the Entity annotation

The @Entity annotation is an essential annotation for GreenDao, and GreenDao will only create the corresponding table if the @Entity annotation is used in the Entity class. Of course we can also configure some details using @Entity:

  • Schema: If you have multiple schemas, you can tell GreenDao which schema you currently belong to.
  • Active: Indicates that an entity is active. Active entities have update, delete, and refresh methods.
  • NameInDb: The alias used in the data, which defaults to the entity’s class name.
  • Set it to False if the DAO should create database tables (the default is true), if you have multiple entities mapped to a table, or if the table was created outside of greenDAO.
  • CreateInDb: flag to create a database table.
  • GenerateGettersSetters: If missing, whether getter and setter methods for the property should be generated.
@Entity(

        schema = "myschema",
        active = true,
        nameInDb = "AWESOME_USERS",
        indexes = {
                @Index(value = "message DESC", unique = true)
        },
        createInDb = false,
        generateConstructors = true,
        generateGettersSetters = true
)
public class Student{	
	……
}
Copy the code

2. Basic attribute annotation (@ID, @property, @notnull, @TRANSIENT)

The @id @id annotation selects the long/long attribute as the entity Id. On the database side, it is the primary key. Increment = true Autoincrement = true autoincrement = true autoincrement = true Autoincrement = true Autoincrement = true Autoincrement = true .

@Entity
public class Student {
    @Id(autoincrement = true) Long id; ... }Copy the code

@Property allows you to define non-default column names to which attributes are mapped. If not, GreenDAO will use the field name in SQL-ish (uppercase, underline not camel case, for example name will become name). Note: You can currently only use inline constants to specify column names.

@Entity
public class Student {
    @Id(autoincrement = true)
    Long id;
    @Property (nameInDb="name") // Set the table property name in the database"name"If this parameter is not set, the table property name in the database is"NAME"String name; ... }Copy the code

@notnull: Set the current column of the database table not to be empty.

@TRANSIENT: columns of the database table will not be generated after the markup is added. Marks a property to exclude from persistence. Use them for temporary states and so on. Alternatively, you can use the Transient keyword in Java.

3. Index notes

  • @index: Use @index as an attribute to create an Index, set the Index alias by name, and add constraints to the Index by unique.
  • @unique: Adds a Unique constraint to the index to force all values to be Unique.
@Entity
public class Student {
    @Id(autoincrement = true)
    Long id;
    @Property(nameInDb="name")
    @Index(unique = true) String name; ... }Copy the code

Insert (name); insert (name); insert (name);

10-08 20:59:46. 274, 31939-31939 / com. Example. Aserbao. Aserbaosandroid E/AndroidRuntime: FATAL EXCEPTION: the main Process: com.example.aserbao.aserbaosandroid, PID: 31939 android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: STUDENT. Name (Sqlite code 2067), (OS error-2 :No such file or directory)......Copy the code

If you use the insertOrReplace() method to add data, there is no duplicate data in the current database, but the id of the duplicate data is changed! This is especially important if the project uses the ID field for sorting.

4. Relationship notes

There are two main relational annotations in GreenDao:

  • ToOne: Defines a relationship with another entity (an entity object)
  • ToMany: Define relationships with multiple entity objects. We’ll get to that in a minute.

6. One-to-one, one-to-many, many-to-many relationship table creation

In common projects, we often use multi-table associations, as mentioned in the database table structure setup at the beginning of this article! Let’s see how GreenDao implements multiple table associations.

1. One to one

One student corresponds to one ID number:

  1. We’ll set an annotation @toOne (joinProperty = “name”) in Student
  2. When Student is created, the corresponding data is passed to IdCard; Code part:

Student code:

@Entity
public class Student {
    @Id(autoincrement = true) Long id; @Unique int studentNo; // int age; // Age String telPhone; // Phone number String sex; // Gender String name; // Name String address; // Home address String schoolName; // School name String grade; // What grade @toone (joinProperty ="name") IdCard student; ... The getter and setter... }Copy the code

IdCard IdCard code:

@Entity public class IdCard { @Id String userName; // username @unique String idNo; // Id number...... The getter and setter... }Copy the code

Insert a set of data:

public void addStudent(){
						DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
 						Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("Male");
                        } else {
                            student.setSex("Female");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "Age"); student.setSchoolName(RandomValue.getSchoolName()); daoSession.insert(student); // Insert the corresponding IdCard data IdCard IdCard = new IdCard(); idCard.setUserName(userName); idCard.setIdNo(RandomValue.getRandomID()); daoSession.insert(idCard); }Copy the code

Ok, data is ready! The database table insertion is now complete.

2. One to many

A person with more than one credit card practice:

  1. Student @ToMany(referencedJoinProperty = “studentId”)
  2. We set up CreditCard to write the corresponding id primary key;

Student code:

@Entity
public class Student {
    @Id(autoincrement = true) Long id; @Unique int studentNo; // int age; // Age String telPhone; // Phone number String sex; // Gender String name; // Name String address; // Home address String schoolName; // School name String grade; // referencedJoinProperty ="StudentId") // This studentId is the studentId List
      
        creditCardsList; ... The getter and setter... }
      Copy the code

CreditCard code:

@Entity public class CreditCard { @Id Long id; Long studentId; Long teacherId; String userName; // Holder name String cardNum; / / card number StringwhichBank; // which bank is int cardType; // Card level, classification 0 ~ 5...... The getter and setter... }Copy the code

Add data code:

public void addStudent(){
						DaoSession daoSession = ((AserbaoApplication) getApplication()).getDaoSession();
 						Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("Male");
                        } else {
                            student.setSex("Female");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "Age"); student.setSchoolName(RandomValue.getSchoolName()); daoSession.insert(student); // Insert the corresponding CreditCard datafor(int j = 0; j < random.nextInt(5) + 1 ; j++) { CreditCard creditCard = new CreditCard(); creditCard.setUserId(id); creditCard.setUserName(userName); creditCard.setCardNum(String.valueOf(random.nextInt(899999999) + 100000000) + String.valueOf(random.nextInt(899999999) + 100000000)); creditCard.setWhichBank(RandomValue.getBankName()); creditCard.setCardType(random.nextInt(10)); daoSession.insert(creditCard); }}Copy the code

3. Many to many

A student has more than one teacher, and a teacher has more than one student. Practice:

  1. We need to create a student Teacher manager (StudentAndTeacherBean) that corresponds to student and teacher ids;

  2. We need to add a comment to the student object:

    @ToMany @JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = “studentId”,targetProperty = “teacherId”) List teacherList;

  3. We need to add the annotation @tomany to the teacher object

    @JoinEntity(entity = StudentAndTeacherBean.class,sourceProperty = “teacherId”,targetProperty = “studentId”) List studentList;

StudentAndTeacherBean code:

@Entity
public class StudentAndTeacherBean {
    @Id(autoincrement = true) Long id; Long studentId; // Student ID Long teacherId; // Teacher ID...... The getter and setter... }Copy the code

Student code:

@Entity
public class Student {
    @Id(autoincrement = true) Long id; @Unique int studentNo; // int age; // Age String telPhone; // Phone number String sex; // Gender String name; // Name String address; // Home address String schoolName; // School name String grade; What grade / / @ ToMany @ JoinEntity (entity = StudentAndTeacherBean. Class,sourceProperty = "studentId",targetProperty = "teacherId") List<Teacher> teacherList; ... The getter and setter... }Copy the code

The Teacher code:

@Entity
public class Teacher {
    @Id(autoincrement = true) Long id; @Unique int teacherNo; // int age; // Age String sex; // Gender String telPhone; String name; // Name String schoolName; // School name String subject; / / subject @ ToMany @ JoinEntity (entity = StudentAndTeacherBean. Class,sourceProperty = "teacherId",targetProperty = "studentId") List<Student> studentList; ... The getter and setter... }Copy the code

Data addition:

public void addData(){
                       Student student = new Student();
                        student.setStudentNo(i);
                        int age = mRandom.nextInt(10) + 10;
                        student.setAge(age);
                        student.setTelPhone(RandomValue.getTel());
                        String chineseName = RandomValue.getChineseName();
                        student.setName(chineseName);
                        if (i % 2 == 0) {
                            student.setSex("Male");
                        } else {
                            student.setSex("Female");
                        }
                        student.setAddress(RandomValue.getRoad());
                        student.setGrade(String.valueOf(age % 10) + "Age");
                        student.setSchoolName(RandomValue.getSchoolName());
                        daoSession.insert(student);

                        Collections.shuffle(teacherList);
                        for (int j = 0; j < mRandom.nextInt(8) + 1; j++) {
                            if(j < teacherList.size()){ Teacher teacher = teacherList.get(j); StudentAndTeacherBean teacherBean = new StudentAndTeacherBean(student.getId(), teacher.getId()); daoSession.insert(teacherBean); }}}Copy the code

Ok, success;

7. Upgrade the database

GreenDao OpenHelper has an onUpgrade(Database db, int oldVersion, int newVersion) method, which will be called when the Database version is changed during Database initialization. We can override the onUpgrade method by inheriting OpenHelper:

GreenDao upgrade idea:

  1. Create temporary table TMP_, copy original database into temporary table;
  2. Drop the original table before;
  3. Create a new table;
  4. Copy the data from the temporary table to the new table, and finally drop the TMP_ table.

There are two classes: MyDaoMaster(OpenHelper inheritance) and MigrationHelper(database operation).

Modify DaoMaster creation in Application:

 		MyDaoMaster helper = new MyDaoMaster(this, "aserbaos.db");
//      DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "aserbao.db");
        SQLiteDatabase db = helper.getWritableDatabase();
        DaoMaster daoMaster = new DaoMaster(db);
        daoSession = daoMaster.newSession();
Copy the code

MyDaoMaster code:

public class MyDaoMaster extends OpenHelper {
    private static final String TAG = "MyDaoMaster";
    public MyDaoMaster(Context context, String name) {
        super(context, name);
    }

    public MyDaoMaster(Context context, String name, SQLiteDatabase.CursorFactory factory) {
        super(context, name, factory);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        super.onUpgrade(db, oldVersion, newVersion);
        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);
            }
        },ThingDao.class);
        Log.e(TAG, "onUpgrade: " + oldVersion + " newVersion = "+ newVersion); }}Copy the code

MigrationHelper code:

public final class MigrationHelper {

    public static boolean DEBUG = false;
    private static String TAG = "MigrationHelper";
    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, ReCreateAllTableListener listener, Class<? extends AbstractDao<? ,? > >... daoClasses) { weakListener = new WeakReference<>(listener); migrate(db, daoClasses); } public static void migrate(Database database, 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);

        ReCreateAllTableListener listener = null;
        if(weakListener ! = null) { 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();
    }


    private static void dropAllTables(Database db, boolean ifExists, @NonNull Class<? extends AbstractDao<? ,? > >... daoClasses) { reflectMethod(db,"dropTable".ifExists, daoClasses);
        printLog("【Drop all table by reflect】");
    }

    private static void createAllTables(Database db, boolean ifNotExists, @NonNull Class<? extends AbstractDao<? ,? > >... daoClasses) { reflectMethod(db,"createTable".ifNotExists, daoClasses);
        printLog("【Create all table by reflect】");
    }

    /**
     * dao class already define the sql execmethod, so just invoke it */ private static void reflectMethod(Database db, String methodName, boolean isExists, @NonNull 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<TableInfo> newTableInfos = TableInfo.getTableInfo(db, tableName);
                List<TableInfo> tempTableInfos = TableInfo.getTableInfo(db, tempTableName);
                ArrayList<String> selectColumns = new ArrayList<>(newTableInfos.size());
                ArrayList<String> intoColumns = new ArrayList<>(newTableInfos.size());
                for (TableInfo tableInfo : tempTableInfos) {
                    if (newTableInfos.contains(tableInfo)) {
                        String column = '` + tableInfo.name + '`;
                        intoColumns.add(column);
                        selectColumns.add(column);
                    }
                }
                // NOT NULL columns list
                for (TableInfo tableInfo : newTableInfos) {
                    if(tableInfo.notnull && ! tempTableInfos.contains(tableInfo)) { String column ='` + tableInfo.name + '`;
                        intoColumns.add(column);

                        String value;
                        if(tableInfo.dfltValue ! = null) { value ="'" + tableInfo.dfltValue + "' AS ";
                        } else {
                            value = "'' AS "; } selectColumns.add(value + column); }}if(intoColumns.size() ! = 0) { StringBuilder insertTableStringBuilder = new StringBuilder(); insertTableStringBuilder.append("REPLACE INTO ").append(tableName).append("(");
                    insertTableStringBuilder.append(TextUtils.join(",", intoColumns));
                    insertTableStringBuilder.append(") SELECT ");
                    insertTableStringBuilder.append(TextUtils.join(",", selectColumns));
                    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);
        }
    }

    private static class TableInfo {
        int cid;
        String name;
        String type;
        boolean notnull;
        String dfltValue;
        boolean pk;

        @Override
        public boolean equals(Object o) {
            returnthis == o || o ! = null && getClass() == o.getClass() && name.equals(((TableInfo) o).name); } @Override public StringtoString() {
            return "TableInfo{" +
                    "cid=" + cid +
                    ", name='" + name + '\'' + ", type='" + type + '\'' + ", notnull=" + notnull + ", dfltValue='" + dfltValue + '\' ' +
                    ", pk=" + pk +
                    '} ';
        }

        private static List<TableInfo> getTableInfo(Database db, String tableName) {
            String sql = "PRAGMA table_info(" + tableName + ")";
            printLog(sql);
            Cursor cursor = db.rawQuery(sql, null);
            if (cursor == null)
                return new ArrayList<>();
            TableInfo tableInfo;
            List<TableInfo> tableInfos = new ArrayList<>();
            while (cursor.moveToNext()) {
                tableInfo = new TableInfo();
                tableInfo.cid = cursor.getInt(0);
                tableInfo.name = cursor.getString(1);
                tableInfo.type = cursor.getString(2);
                tableInfo.notnull = cursor.getInt(3) == 1;
                tableInfo.dfltValue = cursor.getString(4);
                tableInfo.pk = cursor.getInt(5) == 1;
                tableInfos.add(tableInfo);
                // printLog(tableName + ":" + tableInfo);
            }
            cursor.close();
            returntableInfos; }}}Copy the code

8. Encrypt GreenDao database

We can protect the sensitive data stored in the database by encrypting the database. GreenDao can be encrypted using SQLCipher. Let’s briefly explain the encryption process:

Steps:

  1. Import encryption library file:
implementation 'net. After: android - database - sqlcipher: 3.5.6'
Copy the code
  1. Change how DaoSession is generated:
//       MyDaoMaster helper = new MyDaoMaster(this, "aserbaos.db"); DevOpenHelper = new daomaster.devOpenHelper (this,"aserbao.db"); //SQLiteDatabase db = helper.getWritableDatabase(); / / not written encrypted Database db = helper. GetEncryptedWritableDb ("aserbao"); // The database encryption password is aserbao"DaoMaster DaoMaster = new DaoMaster(db); daoSession = daoMaster.newSession();Copy the code

9. Project address

All code in the current article AserbaosAndroid/app/SRC/main/Java/com/aserbao AserbaosAndroid/functions provides/database/greenDao/base directory; (But as far as I am concerned, I may modify the code in the future! However, please rest assured that the modification will be explained on Github)

AserbaosAndroid aserbao personal Android summary project, I hope this project can become the most comprehensive Android development learning project, this is a good vision, there are many not involved in the project, there are many points not mentioned, I hope to see this project friends, If you have any problems during the development and have not found the corresponding solutions in this project, I hope you can raise them, leave a message to me or raise issues at the github address of the project, I will update the parts not involved in the project when I have time! The project will continue to be maintained. Of course, I hope Aserbao ‘sandroid will help all Android developers out there! We also expect more Android developers to participate, as long as you are familiar with a piece of Android, you can pull your code on the branch!

10 summary

This article has spent almost two weeks, from October 8 to today’s official release, which is also a further understanding of GreenDao database! As mentioned at the beginning of the article, I used SQLite to realize database storage at the beginning of Android development, and then I used The third-party storage LitePal. Recently, GreenDao was used in the early stage of the project, so I learned some more GreenDao. For developers, I think mastering one of these three is enough! Of course, if you have the time, you can learn several more, it is no harm to learn more! Finally, as always: if you are an Android developer, you encounter any problems in the development of the road, welcome to my public number to leave a message to me, let’s discuss, join the Android development discussion group, progress together! Article reprint please indicate the source.

The public, Android wechat exchange group expired please clickhere

11. Consult blogs

Android ORM framework: GreenDao use details of Android data storage GreenDao 3.0 details of the Wheel series GreenDao framework principle analysis

12. Modify records

  1. CreditCard cannot be associated with only one useId, because both Teacher and Student have one-to-many relationship with CreditCard, so we need to build two corresponding relationship fields. StudentId and teacherId were added to distinguish between them.
@Entity public class CreditCard { @Id Long id; Long studentId; Long teacherId; String userName; // Holder name String cardNum; / / card number StringwhichBank; // which bank is int cardType; // Card level, classification 0 ~ 5}Copy the code
  1. Student @ToMany (referencedJoinProperty = “ID”) this id corresponds to the studentId in CreditCard, not the increment id. (The question was raised by @shandouji, thanks.) The modified code should be:
  @ToMany(referencedJoinProperty = "studentId")
    List<CreditCard> creditCardsList;
Copy the code