揭秘 Android GreenDao 实体类模块:从源码到实战的深度剖析(3)

106 阅读40分钟

揭秘 Android GreenDao 实体类模块:从源码到实战的深度剖析

一、引言

在 Android 开发的广阔领域中,数据持久化是一个至关重要的环节。数据库作为数据存储的核心,其高效管理和操作直接影响着应用的性能和用户体验。GreenDao 作为一款轻量级且高性能的 Android ORM(对象关系映射)框架,在数据持久化方面表现出色。它通过将 Java 对象与数据库表进行映射,极大地简化了数据库操作,使开发者能够以面向对象的方式进行数据的增删改查。

实体类模块作为 GreenDao 的核心组成部分,承担着对象与数据库表之间的桥梁作用。理解实体类模块的使用原理,对于开发者充分发挥 GreenDao 的优势至关重要。本文将深入剖析 Android GreenDao 实体类模块的使用原理,从源码级别进行详细分析,帮助开发者全面掌握这一强大工具。

二、GreenDao 实体类概述

2.1 实体类的定义

在 GreenDao 中,实体类是用于映射数据库表的 Java 类。每个实体类对应数据库中的一张表,实体类的属性对应表中的列。通过定义实体类,GreenDao 可以自动生成相应的数据库表结构和操作代码。

以下是一个简单的实体类示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Generated;

// 使用 @Entity 注解标记该类为 GreenDao 实体类
@Entity
public class User {
    // 使用 @Id 注解标记该属性为主键,autoincrement 表示自增
    @Id(autoincrement = true)
    private Long id;
    // 普通属性,对应数据库表中的列
    private String name;
    private int age;

    // GreenDao 自动生成的构造函数
    @Generated(hash = 123456789)
    public User(Long id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    // GreenDao 自动生成的无参构造函数
    @Generated(hash = 987654321)
    public User() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

2.2 实体类的作用

实体类在 GreenDao 中具有以下重要作用:

  • 数据映射:将 Java 对象的属性映射到数据库表的列,实现对象与数据库之间的数据转换。
  • 数据库表生成:GreenDao 根据实体类的定义自动生成数据库表结构,无需开发者手动编写 SQL 语句。
  • 数据操作:通过实体类对象,开发者可以方便地进行数据的增删改查操作,无需关心底层的数据库操作细节。

2.3 实体类与数据库表的关系

实体类与数据库表之间存在一一对应的关系。具体如下:

  • 类名与表名:实体类的类名对应数据库表的表名,默认情况下,GreenDao 会将类名转换为大写的蛇形命名法作为表名。例如,User 类对应的表名默认为 USER
  • 属性与列:实体类的属性对应数据库表的列,属性的类型决定了列的数据类型。例如,Long 类型的属性对应数据库中的 INTEGER 类型,String 类型的属性对应数据库中的 TEXT 类型。

三、实体类注解详解

3.1 @Entity 注解

@Entity 注解是 GreenDao 中最重要的注解之一,用于标记一个类为实体类。以下是 @Entity 注解的常用属性:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Generated;

// 使用 @Entity 注解标记该类为 GreenDao 实体类
@Entity(
        // 指定数据库表名,若不指定,默认使用类名的大写蛇形命名法
        nameInDb = "MY_USER",
        // 是否创建所有的默认索引,默认为 true
        createInDb = true,
        // 是否生成所有的默认 DAO 类,默认为 true
        generateDaoClass = true,
        // 是否允许外部关联,默认为 true
        active = true
)
public class User {
    // ... 类的其他部分
}
  • nameInDb:指定实体类对应的数据库表名。如果不指定,GreenDao 会使用类名的大写蛇形命名法作为表名。
  • createInDb:是否在数据库中创建对应的表,默认为 true
  • generateDaoClass:是否生成对应的 DAO(数据访问对象)类,默认为 true
  • active:是否允许外部关联,默认为 true。如果设置为 false,则该实体类不能被其他实体类关联。

3.2 @Id 注解

@Id 注解用于标记实体类中的主键属性。以下是 @Id 注解的常用属性:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Generated;

@Entity
public class User {
    // 使用 @Id 注解标记该属性为主键,autoincrement 表示自增
    @Id(autoincrement = true)
    private Long id;
    // ... 类的其他部分
}
  • autoincrement:是否使用自增主键,默认为 false。如果设置为 true,则数据库会自动为该主键生成唯一的自增数值。

3.3 @Property 注解

@Property 注解用于指定实体类属性对应的数据库列名。以下是 @Property 注解的使用示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Property;
import org.greenrobot.greendao.annotation.Generated;

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    // 使用 @Property 注解指定属性对应的数据库列名
    @Property(nameInDb = "USER_NAME")
    private String name;
    // ... 类的其他部分
}
  • nameInDb:指定属性对应的数据库列名。如果不指定,GreenDao 会使用属性名的大写蛇形命名法作为列名。

3.4 @NotNull 注解

@NotNull 注解用于标记实体类属性不能为空。以下是 @NotNull 注解的使用示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.NotNull;
import org.greenrobot.greendao.annotation.Generated;

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    // 使用 @NotNull 注解标记该属性不能为空
    @NotNull
    private String name;
    // ... 类的其他部分
}

当向数据库插入数据时,如果该属性为空,GreenDao 会抛出异常。

3.5 @Unique 注解

@Unique 注解用于标记实体类属性的值必须唯一。以下是 @Unique 注解的使用示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Unique;
import org.greenrobot.greendao.annotation.Generated;

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    // 使用 @Unique 注解标记该属性的值必须唯一
    @Unique
    private String email;
    // ... 类的其他部分
}

当向数据库插入数据时,如果该属性的值已经存在,GreenDao 会抛出异常。

3.6 @Index 注解

@Index 注解用于为实体类的属性创建索引。以下是 @Index 注解的使用示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;
import org.greenrobot.greendao.annotation.Generated;

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    // 使用 @Index 注解为该属性创建索引
    @Index
    private String name;
    // ... 类的其他部分
}

索引可以提高数据库查询的效率,特别是在经常进行查询的列上创建索引。

3.7 @ToMany 注解

@ToMany 注解用于定义实体类之间的一对多关系。以下是 @ToMany 注解的使用示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.ToMany;
import org.greenrobot.greendao.annotation.Generated;
import java.util.List;

@Entity
public class Order {
    @Id(autoincrement = true)
    private Long id;
    private String orderNumber;
    // 使用 @ToMany 注解定义一对多关系,targetEntity 指定关联的实体类,joinProperty 指定关联的属性
    @ToMany(targetEntity = OrderItem.class, joinProperty = "orderId")
    private List<OrderItem> orderItems;

    @Generated(hash = 123456789)
    public Order(Long id, String orderNumber) {
        this.id = id;
        this.orderNumber = orderNumber;
    }

    @Generated(hash = 987654321)
    public Order() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOrderNumber() {
        return this.orderNumber;
    }

    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public List<OrderItem> getOrderItems() {
        return this.orderItems;
    }

    public void setOrderItems(List<OrderItem> orderItems) {
        this.orderItems = orderItems;
    }
}

@Entity
public class OrderItem {
    @Id(autoincrement = true)
    private Long id;
    private String itemName;
    private Long orderId;

    @Generated(hash = 123456789)
    public OrderItem(Long id, String itemName, Long orderId) {
        this.id = id;
        this.itemName = itemName;
        this.orderId = orderId;
    }

    @Generated(hash = 987654321)
    public OrderItem() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getItemName() {
        return this.itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public Long getOrderId() {
        return this.orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }
}
  • targetEntity:指定关联的实体类。
  • joinProperty:指定关联的属性,用于建立一对多关系。

3.8 @ToOne 注解

@ToOne 注解用于定义实体类之间的一对一关系。以下是 @ToOne 注解的使用示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.ToOne;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.DaoException;

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    private String name;
    private Long addressId;
    // 使用 @ToOne 注解定义一对一关系,targetIdProperty 指定关联的属性
    @ToOne(joinProperty = "addressId")
    private Address address;

    /** Used to resolve relations */
    @Generated(hash = 204004002)
    private transient DaoSession daoSession;

    /** Used for active entity operations. */
    @Generated(hash = 174607749)
    private transient UserDao myDao;

    @Generated(hash = 123456789)
    public User(Long id, String name, Long addressId) {
        this.id = id;
        this.name = name;
        this.addressId = addressId;
    }

    @Generated(hash = 987654321)
    public User() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getAddressId() {
        return this.addressId;
    }

    public void setAddressId(Long addressId) {
        this.addressId = addressId;
    }

    /** To-one relationship, resolved on first access. */
    @Generated(hash = 103202020)
    public Address getAddress() {
        Long __key = this.addressId;
        if (address__resolvedKey == null || !address__resolvedKey.equals(__key)) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            AddressDao targetDao = daoSession.getAddressDao();
            Address addressNew = targetDao.load(__key);
            synchronized (this) {
                address = addressNew;
                address__resolvedKey = __key;
            }
        }
        return address;
    }

    /** called by internal mechanisms, do not call yourself. */
    @Generated(hash = 163844887)
    public void setAddress(Address address) {
        synchronized (this) {
            this.address = address;
            addressId = address == null ? null : address.getId();
            address__resolvedKey = addressId;
        }
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 128553479)
    public void delete() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.delete(this);
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 194239201)
    public void refresh() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.refresh(this);
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 713229351)
    public void update() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.update(this);
    }
}

@Entity
public class Address {
    @Id(autoincrement = true)
    private Long id;
    private String street;
    private String city;

    @Generated(hash = 123456789)
    public Address(Long id, String street, String city) {
        this.id = id;
        this.street = street;
        this.city = city;
    }

    @Generated(hash = 987654321)
    public Address() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getStreet() {
        return this.street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return this.city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}
  • joinProperty:指定关联的属性,用于建立一对一关系。

四、实体类的生成过程

4.1 代码生成器的配置

GreenDao 使用代码生成器来根据实体类的定义生成数据库表结构和操作代码。以下是一个简单的代码生成器配置示例:

import org.greenrobot.greendao.generator.DaoGenerator;
import org.greenrobot.greendao.generator.Entity;
import org.greenrobot.greendao.generator.Schema;

public class GreenDaoGenerator {
    public static void main(String[] args) throws Exception {
        // 创建一个 Schema 对象,指定数据库版本和生成代码的包名
        Schema schema = new Schema(1, "com.example.greendaoexample");

        // 创建一个实体类
        Entity user = schema.addEntity("User");
        // 添加主键属性
        user.addIdProperty();
        // 添加普通属性
        user.addStringProperty("name");
        user.addIntProperty("age");

        // 生成代码,指定生成代码的目录
        new DaoGenerator().generateAll(schema, "../app/src/main/java");
    }
}
  • Schema:用于定义数据库的版本和生成代码的包名。
  • Entity:用于创建实体类,通过 addEntity() 方法添加实体类。
  • addIdProperty():添加主键属性。
  • addStringProperty():添加 String 类型的属性。
  • addIntProperty():添加 int 类型的属性。
  • generateAll():生成所有的代码,指定生成代码的目录。

4.2 代码生成的源码分析

4.2.1 DaoGenerator

DaoGenerator 类是 GreenDao 代码生成器的核心类,负责根据 Schema 对象生成数据库表结构和操作代码。以下是 DaoGenerator 类的部分源码:

import java.io.File;
import java.io.IOException;

import org.greenrobot.greendao.generator.DaoGenerator;
import org.greenrobot.greendao.generator.Entity;
import org.greenrobot.greendao.generator.Schema;

public class DaoGenerator {
    /**
     * Generates all DAO classes and related code for the given schema.
     *
     * @param schema the schema to generate code for
     * @param targetDir the directory where the generated code will be placed
     * @throws IOException if an I/O error occurs
     */
    public void generateAll(Schema schema, String targetDir) throws IOException {
        // 创建目标目录
        File dir = new File(targetDir);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
                throw new IOException("Could not create directory: " + targetDir);
            }
        }

        // 生成实体类代码
        for (Entity entity : schema.getEntities()) {
            entity.generateCode(targetDir);
        }

        // 生成 DAO 类代码
        schema.generateAllDaoClasses(targetDir);
    }
}
  • generateAll():根据 Schema 对象生成所有的代码,包括实体类代码和 DAO 类代码。
  • entity.generateCode():生成实体类的代码。
  • schema.generateAllDaoClasses():生成所有的 DAO 类代码。
4.2.2 Entity

Entity 类用于表示实体类,负责生成实体类的代码。以下是 Entity 类的部分源码:

import java.io.File;
import java.io.IOException;

import org.greenrobot.greendao.generator.Entity;
import org.greenrobot.greendao.generator.Property;
import org.greenrobot.greendao.generator.Schema;

public class Entity {
    private final Schema schema;
    private final String className;
    private final String tableName;
    private final Property idProperty;
    private final List<Property> properties;

    // 构造函数
    public Entity(Schema schema, String className) {
        this.schema = schema;
        this.className = className;
        this.tableName = toDbName(className);
        this.idProperty = null;
        this.properties = new ArrayList<>();
    }

    /**
     * Generates the code for this entity.
     *
     * @param targetDir the directory where the generated code will be placed
     * @throws IOException if an I/O error occurs
     */
    public void generateCode(String targetDir) throws IOException {
        // 创建实体类的文件
        File file = new File(targetDir, getJavaPackage() + "/" + className + ".java");
        if (!file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                throw new IOException("Could not create directory: " + file.getParent());
            }
        }

        // 生成实体类的代码
        StringBuilder builder = new StringBuilder();
        builder.append("package ").append(getJavaPackage()).append(";\n\n");
        builder.append("import org.greenrobot.greendao.annotation.Entity;\n");
        if (idProperty != null) {
            builder.append("import org.greenrobot.greendao.annotation.Id;\n");
        }
        builder.append("import org.greenrobot.greendao.annotation.Generated;\n\n");
        builder.append("@Entity\n");
        builder.append("public class ").append(className).append(" {\n");

        // 生成属性代码
        if (idProperty != null) {
            builder.append("    @Id(autoincrement = true)\n");
            builder.append("    private ").append(idProperty.getType().getJavaType()).append(" ").append(idProperty.getName()).append(";\n");
        }
        for (Property property : properties) {
            builder.append("    private ").append(property.getType().getJavaType()).append(" ").append(property.getName()).append(";\n");
        }

        // 生成构造函数代码
        builder.append("\n    @Generated(hash = 123456789)\n");
        builder.append("    public ").append(className).append("(");
        if (idProperty != null) {
            builder.append(idProperty.getType().getJavaType()).append(" ").append(idProperty.getName());
            if (!properties.isEmpty()) {
                builder.append(", ");
            }
        }
        for (int i = 0; i < properties.size(); i++) {
            Property property = properties.get(i);
            builder.append(property.getType().getJavaType()).append(" ").append(property.getName());
            if (i < properties.size() - 1) {
                builder.append(", ");
            }
        }
        builder.append(") {\n");
        if (idProperty != null) {
            builder.append("        this.").append(idProperty.getName()).append(" = ").append(idProperty.getName()).append(";\n");
        }
        for (Property property : properties) {
            builder.append("        this.").append(property.getName()).append(" = ").append(property.getName()).append(";\n");
        }
        builder.append("    }\n");

        builder.append("\n    @Generated(hash = 987654321)\n");
        builder.append("    public ").append(className).append("() {\n");
        builder.append("    }\n");

        // 生成 Getter 和 Setter 代码
        if (idProperty != null) {
            builder.append("\n    public ").append(idProperty.getType().getJavaType()).append(" ").append(getGetterMethodName(idProperty.getName())).append("() {\n");
            builder.append("        return this.").append(idProperty.getName()).append(";\n");
            builder.append("    }\n");

            builder.append("\n    public void ").append(getSetterMethodName(idProperty.getName())).append("(").append(idProperty.getType().getJavaType()).append(" ").append(idProperty.getName()).append(") {\n");
            builder.append("        this.").append(idProperty.getName()).append(" = ").append(idProperty.getName()).append(";\n");
            builder.append("    }\n");
        }
        for (Property property : properties) {
            builder.append("\n    public ").append(property.getType().getJavaType()).append(" ").append(getGetterMethodName(property.getName())).append("() {\n");
            builder.append("        return this.").append(property.getName()).append(";\n");
            builder.append("    }\n");

            builder.append("\n    public void ").append(getSetterMethodName(property.getName())).append("(").append(property.getType().getJavaType()).append(" ").append(property.getName()).append(") {\n");
            builder.append("        this.").append(property.getName()).append(" = ").append(property.getName()).append(";\n");
            builder.append("    }\n");
        }

        builder.append("}\n");

        // 将代码写入文件
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            writer.write(builder.toString());
        }
    }

    // 其他方法...
}
  • generateCode():生成实体类的代码,包括包声明、导入语句、注解、属性、构造函数、Getter 和 Setter 方法等。
4.2.3 Schema

Schema 类用于表示数据库的架构,负责生成所有的 DAO 类代码。以下是 Schema 类的部分源码:

import java.io.File;
import java.io.IOException;

import org.greenrobot.greendao.generator.DaoGenerator;
import org.greenrobot.greendao.generator.Entity;
import org.greenrobot.greendao.generator.Schema;

public class Schema {
    private final int version;
    private final String defaultJavaPackage;
    private final List<Entity> entities;

    // 构造函数
    public Schema(int version, String defaultJavaPackage) {
        this.version = version;
        this.defaultJavaPackage = defaultJavaPackage;
        this.entities = new ArrayList<>();
    }

    /**
     * Adds an entity to this schema.
     *
     * @param className the name of the entity class
     * @return the newly created entity
     */
    public Entity addEntity(String className) {
        Entity entity = new Entity(this, className);
        entities.add(entity);
        return entity;
    }

    /**
     * Generates all DAO classes for this schema.
     *
     * @param targetDir the directory where the generated code will be placed
     * @throws IOException if an I/O error occurs
     */
    public void generateAllDaoClasses(String targetDir) throws IOException {
        for (Entity entity : entities) {
            entity.generateDaoClass(targetDir);
        }
    }

    // 其他方法...
}
  • addEntity():向 Schema 中添加一个实体类。
  • generateAllDaoClasses():生成所有的 DAO 类代码。

4.3 生成代码的结构

生成的代码主要包括实体类代码和 DAO 类代码。以下是生成代码的结构示例:

com.example.greendaoexample
├── User.java
├── UserDao.java
├── DaoMaster.java
├── DaoSession.java
  • User.java:实体类代码,包含实体类的属性、构造函数、Getter 和 Setter 方法等。
  • UserDao.java:DAO 类代码,包含对 User 表的增删改查操作方法。
  • DaoMaster.java:数据库管理类,负责数据库的创建和版本管理。
  • DaoSession.java:数据库会话类,负责获取 DAO 类的实例。

五、实体类与数据库的交互

5.1 插入数据

使用 GreenDao 插入数据非常简单,只需要创建实体类对象,然后调用 DAO 类的 insert() 方法即可。以下是一个插入数据的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;

// 假设这是在某个 Activity 中
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

// 定义一个继承自 AppCompatActivity 的类
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取全局的 DaoSession 对象
        DaoSession daoSession = MyApplication.getDaoSession();
        // 通过 DaoSession 获取 UserDao 对象
        UserDao userDao = daoSession.getUserDao();

        // 创建一个 User 对象
        User user = new User();
        user.setName("John");
        user.setAge(25);

        // 调用 UserDao 的 insert 方法插入数据
        long id = userDao.insert(user);
    }
}

5.2 源码分析

5.2.1 UserDao 类的 insert() 方法

UserDao 类是由 GreenDao 自动生成的,用于对 User 表进行操作。以下是其 insert() 方法的部分源码:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import org.greenrobot.greendao.query.QueryBuilder;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 插入语句
    private static final String SQL_INSERT = "INSERT INTO " + TABLENAME + " (" +
            UserDao.Properties.Name.columnName + ", " +
            UserDao.Properties.Age.columnName + ") VALUES (?, ?);";

    // 插入单条数据
    @Override
    public long insert(User entity) {
        // 获取数据库实例
        Database db = getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 获取插入语句的 DatabaseStatement 对象
            DatabaseStatement stmt = getInsertStatement();
            // 绑定参数
            stmt.clearBindings();
            stmt.bindString(1, entity.getName());
            stmt.bindLong(2, entity.getAge());
            // 执行插入操作,返回插入记录的 ID
            long rowId = stmt.executeInsert();
            // 设置事务成功
            db.setTransactionSuccessful();
            return rowId;
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}
5.2.2 AbstractDao 类的 getInsertStatement() 方法

AbstractDao 类是所有 DAO 类的基类,它提供了一些通用的数据库操作方法。以下是其 getInsertStatement() 方法的部分源码:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;

// 所有 DAO 类的基类
public abstract class AbstractDao<T, K> {
    // 存储插入语句的 DatabaseStatement 对象
    private DatabaseStatement insertStatement;

    // 获取插入语句的 DatabaseStatement 对象
    public DatabaseStatement getInsertStatement() {
        if (insertStatement == null) {
            // 获取数据库实例
            Database db = getDatabase();
            // 编译插入语句
            insertStatement = db.compileStatement(SQL_INSERT);
        }
        return insertStatement;
    }
}

5.3 查询数据

使用 GreenDao 查询数据可以通过 DAO 类的各种查询方法实现。以下是一个简单查询的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;

// 假设这是在某个 Activity 中
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.util.List;

// 定义一个继承自 AppCompatActivity 的类
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取全局的 DaoSession 对象
        DaoSession daoSession = MyApplication.getDaoSession();
        // 通过 DaoSession 获取 UserDao 对象
        UserDao userDao = daoSession.getUserDao();

        // 查询所有用户
        List<User> userList = userDao.loadAll();
        for (User user : userList) {
            System.out.println("User: " + user.getName() + ", Age: " + user.getAge());
        }

        // 根据 ID 查询用户
        User user = userDao.load(1L);
        if (user != null) {
            System.out.println("User with ID 1: " + user.getName() + ", Age: " + user.getAge());
        }
    }
}

5.4 源码分析

5.4.1 UserDao 类的 loadAll() 方法

UserDao 类的 loadAll() 方法用于查询所有记录。以下是其部分源码:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import org.greenrobot.greendao.query.QueryBuilder;

import java.util.List;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 查询所有记录
    @Override
    public List<User> loadAll() {
        // 创建一个查询构建器
        QueryBuilder<User> queryBuilder = queryBuilder();
        // 执行查询,返回结果列表
        return queryBuilder.list();
    }
}
5.4.2 QueryBuilder 类的 list() 方法

QueryBuilder 类用于构建查询语句。以下是其 list() 方法的部分源码: QueryBuilder 类是 GreenDao 中用于构建复杂查询语句的核心类。list() 方法的作用是执行查询操作并将结果以列表的形式返回。以下是其部分源码及详细分析:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.Cursor;
import org.greenrobot.greendao.query.WhereCondition;

import java.util.ArrayList;
import java.util.List;

// 用于构建查询语句
public class QueryBuilder<T> {
    // 所属的 DAO 对象
    private final AbstractDao<T, ?> dao;
    // 存储查询条件
    private final List<WhereCondition> whereConditions;

    // 构造函数,接收一个 DAO 对象作为参数
    public QueryBuilder(AbstractDao<T, ?> dao) {
        this.dao = dao;
        this.whereConditions = new ArrayList<>();
    }

    // 构建查询条件
    public QueryBuilder<T> where(WhereCondition cond, WhereCondition... condMore) {
        whereConditions.add(cond);
        if (condMore != null) {
            for (WhereCondition cond2 : condMore) {
                whereConditions.add(cond2);
            }
        }
        return this;
    }

    // 执行查询并返回结果列表
    public List<T> list() {
        // 构建查询的 SQL 语句
        String sql = buildQueryString();
        // 获取查询参数
        List<Object> values = buildValues();
        // 获取数据库实例
        Database db = dao.getDatabase();
        // 执行查询操作,获取游标
        Cursor cursor = db.rawQuery(sql, values.toArray(new String[values.size()]));
        try {
            // 从游标中解析数据并构建实体对象列表
            return dao.loadAllAndCloseCursor(cursor);
        } finally {
            // 确保游标被关闭
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    // 构建查询的 SQL 语句
    private String buildQueryString() {
        StringBuilder builder = new StringBuilder("SELECT ");
        builder.append(dao.getAllColumnsAsString());
        builder.append(" FROM ").append(dao.getTablename());
        if (!whereConditions.isEmpty()) {
            builder.append(" WHERE ");
            boolean first = true;
            for (WhereCondition condition : whereConditions) {
                if (!first) {
                    builder.append(" AND ");
                }
                builder.append(condition.toString());
                first = false;
            }
        }
        return builder.toString();
    }

    // 获取查询参数
    private List<Object> buildValues() {
        List<Object> values = new ArrayList<>();
        for (WhereCondition condition : whereConditions) {
            condition.appendValuesTo(values);
        }
        return values;
    }
}
  • list() 方法流程
    1. 构建查询 SQL 语句:调用 buildQueryString() 方法,根据存储的查询条件构建完整的 SQL 查询语句。该方法会将所有的查询条件拼接成一个合法的 SQL WHERE 子句。
    2. 获取查询参数:调用 buildValues() 方法,将查询条件中的参数提取出来,存储在一个列表中。这些参数将用于后续的 rawQuery 方法。
    3. 执行查询操作:通过 dao.getDatabase() 获取数据库实例,然后调用 db.rawQuery() 方法执行查询操作,返回一个 Cursor 对象。Cursor 是 Android 中用于遍历查询结果的对象。
    4. 解析查询结果:调用 dao.loadAllAndCloseCursor(cursor) 方法,从 Cursor 中解析数据并构建实体对象列表。该方法会自动将数据库中的每一行数据转换为对应的实体类对象。
    5. 关闭游标:在 finally 块中,确保 Cursor 被关闭,以避免资源泄漏。
5.4.3 AbstractDao 类的 loadAllAndCloseCursor() 方法

AbstractDao 类的 loadAllAndCloseCursor() 方法用于从 Cursor 中解析数据并构建实体对象列表,同时关闭 Cursor。以下是其部分源码及详细分析:

import org.greenrobot.greendao.database.Cursor;

import java.util.ArrayList;
import java.util.List;

// 所有 DAO 类的基类
public abstract class AbstractDao<T, K> {
    // 从游标中解析数据并构建实体对象列表,同时关闭游标
    public List<T> loadAllAndCloseCursor(Cursor cursor) {
        try {
            // 创建一个空的实体对象列表
            List<T> list = new ArrayList<>();
            // 检查游标是否有数据
            if (cursor.moveToFirst()) {
                do {
                    // 从当前游标位置解析数据并构建实体对象
                    T entity = readEntity(cursor, 0);
                    // 将实体对象添加到列表中
                    list.add(entity);
                } while (cursor.moveToNext());
            }
            return list;
        } finally {
            // 确保游标被关闭
            if (cursor != null) {
                cursor.close();
            }
        }
    }

    // 从游标中读取一行数据并构建实体对象
    protected abstract T readEntity(Cursor cursor, int offset);
}
  • loadAllAndCloseCursor() 方法流程
    1. 创建实体对象列表:创建一个空的 ArrayList 用于存储解析后的实体对象。
    2. 遍历游标:使用 cursor.moveToFirst() 方法将游标移动到第一行,如果有数据,则进入循环。在循环中,使用 cursor.moveToNext() 方法将游标移动到下一行,直到没有更多数据为止。
    3. 解析数据并构建实体对象:在每次循环中,调用 readEntity(cursor, 0) 方法从当前游标位置解析数据并构建实体对象。readEntity() 方法是一个抽象方法,具体的实现由子类完成。
    4. 添加实体对象到列表:将解析后的实体对象添加到列表中。
    5. 关闭游标:在 finally 块中,确保 Cursor 被关闭,以避免资源泄漏。

5.5 更新数据

使用 GreenDao 更新数据可以通过 DAO 类的 update() 方法实现。以下是一个更新数据的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;

// 假设这是在某个 Activity 中
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

// 定义一个继承自 AppCompatActivity 的类
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取全局的 DaoSession 对象
        DaoSession daoSession = MyApplication.getDaoSession();
        // 通过 DaoSession 获取 UserDao 对象
        UserDao userDao = daoSession.getUserDao();

        // 根据 ID 查询用户
        User user = userDao.load(1L);
        if (user != null) {
            // 更新用户信息
            user.setName("Updated Name");
            user.setAge(30);
            // 调用 UserDao 的 update 方法更新数据
            userDao.update(user);
        }
    }
}

5.6 源码分析

5.6.1 UserDao 类的 update() 方法

UserDao 类的 update() 方法用于更新单条记录。以下是其部分源码及详细分析:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import org.greenrobot.greendao.query.QueryBuilder;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 更新语句
    private static final String SQL_UPDATE = "UPDATE " + TABLENAME + " SET " +
            UserDao.Properties.Name.columnName + " = ?, " +
            UserDao.Properties.Age.columnName + " = ? WHERE " +
            UserDao.Properties.Id.columnName + " = ?;";

    // 更新单条数据
    @Override
    public void update(User entity) {
        // 获取数据库实例
        Database db = getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 获取更新语句的 DatabaseStatement 对象
            DatabaseStatement stmt = getUpdateStatement();
            // 绑定参数
            stmt.clearBindings();
            stmt.bindString(1, entity.getName());
            stmt.bindLong(2, entity.getAge());
            stmt.bindLong(3, entity.getId());
            // 执行更新操作
            stmt.executeUpdateDelete();
            // 设置事务成功
            db.setTransactionSuccessful();
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}
  • update() 方法流程
    1. 获取数据库实例:通过 getDatabase() 方法获取当前操作的数据库实例。
    2. 开启事务:调用 db.beginTransaction() 方法开启一个数据库事务,确保更新操作的原子性。
    3. 获取更新语句的 DatabaseStatement 对象:调用 getUpdateStatement() 方法获取预编译的更新语句的 DatabaseStatement 对象。
    4. 绑定参数:使用 stmt.bindString()stmt.bindLong() 方法将实体对象的属性值绑定到更新语句的参数占位符上。
    5. 执行更新操作:调用 stmt.executeUpdateDelete() 方法执行更新操作。
    6. 设置事务成功:如果更新操作执行成功,调用 db.setTransactionSuccessful() 方法将事务标记为成功。
    7. 结束事务:在 finally 块中,调用 db.endTransaction() 方法结束事务。如果事务被标记为成功,则提交事务;否则,回滚事务。
5.6.2 AbstractDao 类的 getUpdateStatement() 方法

AbstractDao 类的 getUpdateStatement() 方法用于获取更新语句的 DatabaseStatement 对象。以下是其部分源码及详细分析:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;

// 所有 DAO 类的基类
public abstract class AbstractDao<T, K> {
    // 存储更新语句的 DatabaseStatement 对象
    private DatabaseStatement updateStatement;

    // 获取更新语句的 DatabaseStatement 对象
    public DatabaseStatement getUpdateStatement() {
        if (updateStatement == null) {
            // 获取数据库实例
            Database db = getDatabase();
            // 编译更新语句
            updateStatement = db.compileStatement(SQL_UPDATE);
        }
        return updateStatement;
    }
}
  • getUpdateStatement() 方法流程
    1. 检查 updateStatement 是否为空:如果 updateStatementnull,说明还没有编译过更新语句,需要进行编译。
    2. 获取数据库实例:通过 getDatabase() 方法获取当前操作的数据库实例。
    3. 编译更新语句:调用 db.compileStatement(SQL_UPDATE) 方法编译更新语句,得到一个 DatabaseStatement 对象,并将其赋值给 updateStatement 成员变量。
    4. 返回 DatabaseStatement 对象:返回预编译好的 DatabaseStatement 对象,供后续的更新操作使用。

5.7 删除数据

使用 GreenDao 删除数据可以通过 DAO 类的 delete() 方法实现。以下是一个删除数据的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;

// 假设这是在某个 Activity 中
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

// 定义一个继承自 AppCompatActivity 的类
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取全局的 DaoSession 对象
        DaoSession daoSession = MyApplication.getDaoSession();
        // 通过 DaoSession 获取 UserDao 对象
        UserDao userDao = daoSession.getUserDao();

        // 根据 ID 查询用户
        User user = userDao.load(1L);
        if (user != null) {
            // 调用 UserDao 的 delete 方法删除数据
            userDao.delete(user);
        }
    }
}

5.8 源码分析

5.8.1 UserDao 类的 delete() 方法

UserDao 类的 delete() 方法用于删除单条记录。以下是其部分源码及详细分析:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import org.greenrobot.greendao.query.QueryBuilder;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 删除语句
    private static final String SQL_DELETE = "DELETE FROM " + TABLENAME + " WHERE " +
            UserDao.Properties.Id.columnName + " = ?;";

    // 删除单条数据
    @Override
    public void delete(User entity) {
        // 获取数据库实例
        Database db = getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 获取删除语句的 DatabaseStatement 对象
            DatabaseStatement stmt = getDeleteStatement();
            // 绑定参数
            stmt.clearBindings();
            stmt.bindLong(1, entity.getId());
            // 执行删除操作
            stmt.executeUpdateDelete();
            // 设置事务成功
            db.setTransactionSuccessful();
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}
  • delete() 方法流程
    1. 获取数据库实例:通过 getDatabase() 方法获取当前操作的数据库实例。
    2. 开启事务:调用 db.beginTransaction() 方法开启一个数据库事务,确保删除操作的原子性。
    3. 获取删除语句的 DatabaseStatement 对象:调用 getDeleteStatement() 方法获取预编译的删除语句的 DatabaseStatement 对象。
    4. 绑定参数:使用 stmt.bindLong() 方法将实体对象的主键值绑定到删除语句的参数占位符上。
    5. 执行删除操作:调用 stmt.executeUpdateDelete() 方法执行删除操作。
    6. 设置事务成功:如果删除操作执行成功,调用 db.setTransactionSuccessful() 方法将事务标记为成功。
    7. 结束事务:在 finally 块中,调用 db.endTransaction() 方法结束事务。如果事务被标记为成功,则提交事务;否则,回滚事务。
5.8.2 AbstractDao 类的 getDeleteStatement() 方法

AbstractDao 类的 getDeleteStatement() 方法用于获取删除语句的 DatabaseStatement 对象。以下是其部分源码及详细分析:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;

// 所有 DAO 类的基类
public abstract class AbstractDao<T, K> {
    // 存储删除语句的 DatabaseStatement 对象
    private DatabaseStatement deleteStatement;

    // 获取删除语句的 DatabaseStatement 对象
    public DatabaseStatement getDeleteStatement() {
        if (deleteStatement == null) {
            // 获取数据库实例
            Database db = getDatabase();
            // 编译删除语句
            deleteStatement = db.compileStatement(SQL_DELETE);
        }
        return deleteStatement;
    }
}
  • getDeleteStatement() 方法流程
    1. 检查 deleteStatement 是否为空:如果 deleteStatementnull,说明还没有编译过删除语句,需要进行编译。
    2. 获取数据库实例:通过 getDatabase() 方法获取当前操作的数据库实例。
    3. 编译删除语句:调用 db.compileStatement(SQL_DELETE) 方法编译删除语句,得到一个 DatabaseStatement 对象,并将其赋值给 deleteStatement 成员变量。
    4. 返回 DatabaseStatement 对象:返回预编译好的 DatabaseStatement 对象,供后续的删除操作使用。

六、实体类的关联关系

6.1 一对一关系

在 GreenDao 中,一对一关系可以通过 @ToOne 注解来实现。以下是一个一对一关系的示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.ToOne;
import org.greenrobot.greendao.annotation.Generated;
import org.greenrobot.greendao.DaoException;

@Entity
public class User {
    @Id(autoincrement = true)
    private Long id;
    private String name;
    private Long addressId;
    // 使用 @ToOne 注解定义一对一关系,targetIdProperty 指定关联的属性
    @ToOne(joinProperty = "addressId")
    private Address address;

    /** Used to resolve relations */
    @Generated(hash = 204004002)
    private transient DaoSession daoSession;

    /** Used for active entity operations. */
    @Generated(hash = 174607749)
    private transient UserDao myDao;

    @Generated(hash = 123456789)
    public User(Long id, String name, Long addressId) {
        this.id = id;
        this.name = name;
        this.addressId = addressId;
    }

    @Generated(hash = 987654321)
    public User() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Long getAddressId() {
        return this.addressId;
    }

    public void setAddressId(Long addressId) {
        this.addressId = addressId;
    }

    /** To-one relationship, resolved on first access. */
    @Generated(hash = 103202020)
    public Address getAddress() {
        Long __key = this.addressId;
        if (address__resolvedKey == null || !address__resolvedKey.equals(__key)) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            AddressDao targetDao = daoSession.getAddressDao();
            Address addressNew = targetDao.load(__key);
            synchronized (this) {
                address = addressNew;
                address__resolvedKey = __key;
            }
        }
        return address;
    }

    /** called by internal mechanisms, do not call yourself. */
    @Generated(hash = 163844887)
    public void setAddress(Address address) {
        synchronized (this) {
            this.address = address;
            addressId = address == null ? null : address.getId();
            address__resolvedKey = addressId;
        }
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 128553479)
    public void delete() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.delete(this);
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 194239201)
    public void refresh() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.refresh(this);
    }

    /**
     * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
     * Entity must attached to an entity context.
     */
    @Generated(hash = 713229351)
    public void update() {
        if (myDao == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        myDao.update(this);
    }
}

@Entity
public class Address {
    @Id(autoincrement = true)
    private Long id;
    private String street;
    private String city;

    @Generated(hash = 123456789)
    public Address(Long id, String street, String city) {
        this.id = id;
        this.street = street;
        this.city = city;
    }

    @Generated(hash = 987654321)
    public Address() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getStreet() {
        return this.street;
    }

    public void setStreet(String street) {
        this.street = street;
    }

    public String getCity() {
        return this.city;
    }

    public void setCity(String city) {
        this.city = city;
    }
}

6.2 源码分析

6.2.1 User 类的 getAddress() 方法

User 类的 getAddress() 方法用于获取关联的 Address 对象。以下是其部分源码及详细分析:

/** To-one relationship, resolved on first access. */
@Generated(hash = 103202020)
public Address getAddress() {
    // 获取关联的 Address 对象的主键值
    Long __key = this.addressId;
    // 检查是否已经解析过关联对象
    if (address__resolvedKey == null || !address__resolvedKey.equals(__key)) {
        // 获取 DaoSession 对象
        final DaoSession daoSession = this.daoSession;
        if (daoSession == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        // 获取 AddressDao 对象
        AddressDao targetDao = daoSession.getAddressDao();
        // 根据主键值加载关联的 Address 对象
        Address addressNew = targetDao.load(__key);
        synchronized (this) {
            // 更新关联对象和解析标志
            address = addressNew;
            address__resolvedKey = __key;
        }
    }
    return address;
}
  • getAddress() 方法流程
    1. 获取关联对象的主键值:从 User 对象中获取 addressId 属性的值,作为关联的 Address 对象的主键值。
    2. 检查是否已经解析过关联对象:通过比较 address__resolvedKey__key 的值,判断是否已经解析过关联对象。如果没有解析过或者主键值发生了变化,则需要重新解析。
    3. 获取 DaoSession 对象:从 User 对象中获取 daoSession 对象,如果 daoSessionnull,则抛出 DaoException 异常。
    4. 获取 AddressDao 对象:通过 daoSession.getAddressDao() 方法获取 AddressDao 对象,用于加载关联的 Address 对象。
    5. 加载关联对象:调用 targetDao.load(__key) 方法,根据主键值加载关联的 Address 对象。
    6. 更新关联对象和解析标志:使用 synchronized 块确保线程安全,更新 addressaddress__resolvedKey 的值。
    7. 返回关联对象:返回加载的 Address 对象。
6.2.2 User 类的 setAddress() 方法

User 类的 setAddress() 方法用于设置关联的 Address 对象。以下是其部分源码及详细分析:

/** called by internal mechanisms, do not call yourself. */
@Generated(hash = 163844887)
public void setAddress(Address address) {
    synchronized (this) {
        // 更新关联对象
        this.address = address;
        // 更新关联对象的主键值
        addressId = address == null ? null : address.getId();
        // 更新解析标志
        address__resolvedKey = addressId;
    }
}
  • setAddress() 方法流程
    1. 使用 synchronized 块确保线程安全:在 synchronized 块中执行更新操作,避免多线程环境下的数据不一致问题。
    2. 更新关联对象:将传入的 Address 对象赋值给 this.address
    3. 更新关联对象的主键值:如果传入的 Address 对象不为 null,则将其主键值赋值给 addressId;否则,将 addressId 设置为 null
    4. 更新解析标志:将 address__resolvedKey 设置为 addressId,表示关联对象已经解析过。

6.3 一对多关系

在 GreenDao 中,一对多关系可以通过 @ToMany 注解来实现。以下是一个一对多关系的示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.ToMany;
import org.greenrobot.greendao.annotation.Generated;
import java.util.List;

@Entity
public class Order {
    @Id(autoincrement = true)
    private Long id;
    private String orderNumber;
    // 使用 @ToMany 注解定义一对多关系,targetEntity 指定关联的实体类,joinProperty 指定关联的属性
    @ToMany(targetEntity = OrderItem.class, joinProperty = "orderId")
    private List<OrderItem> orderItems;

    @Generated(hash = 123456789)
    public Order(Long id, String orderNumber) {
        this.id = id;
        this.orderNumber = orderNumber;
    }

    @Generated(hash = 987654321)
    public Order() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOrderNumber() {
        return this.orderNumber;
    }

    public void setOrderNumber(String orderNumber) {
        this.orderNumber = orderNumber;
    }

    public List<OrderItem> getOrderItems() {
        if (orderItems == null) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            OrderItemDao targetDao = daoSession.getOrderItemDao();
            orderItems = targetDao._queryOrder_OrderItems(id);
        }
        return orderItems;
    }

    public void setOrderItems(List<OrderItem> orderItems) {
        this.orderItems = orderItems;
    }
}

@Entity
public class OrderItem {
    @Id(autoincrement = true)
    private Long id;
    private String itemName;
    private Long orderId;

    @Generated(hash = 123456789)
    public OrderItem(Long id, String itemName, Long orderId) {
        this.id = id;
        this.itemName = itemName;
        this.orderId = orderId;
    }

    @Generated(hash = 987654321)
    public OrderItem() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getItemName() {
        return this.itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public Long getOrderId() {
        return this.orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }
}

6.4 源码分析

6.4.1 Order 类的 getOrderItems() 方法

Order 类的 getOrderItems() 方法用于获取关联的 OrderItem 对象列表。以下是其部分源码及详细分析:

public List<OrderItem> getOrderItems() {
    // 检查关联的 OrderItem 列表是否为空
    if (orderItems == null) {
        // 获取 DaoSession 对象
        final DaoSession daoSession = this.daoSession;
        if (daoSession == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        // 获取 OrderItemDao 对象
        OrderItemDao targetDao = daoSession.getOrderItemDao();
        // 根据 Order 的主键值查询关联的 OrderItem 列表
        orderItems = targetDao._queryOrder_OrderItems(id);
    }
    return orderItems;
}
  • getOrderItems() 方法流程
    1. 检查关联的 OrderItem 列表是否为空:如果 orderItemsnull,说明还没有加载关联的 OrderItem 列表,需要进行加载。
    2. 获取 DaoSession 对象:从 Order 对象中获取 daoSession 对象,如果 daoSessionnull,则抛出 DaoException 异常。
    3. 获取 OrderItemDao 对象:通过 daoSession.getOrderItemDao() 方法获取 OrderItemDao 对象,用于查询关联的 OrderItem 列表。
    4. 查询关联的 OrderItem 列表:调用 targetDao._queryOrder_OrderItems(id) 方法,根据 Order 的主键值查询关联的 OrderItem 列表。
    5. 返回关联的 OrderItem 列表:返回加载的 OrderItem 列表。
6.4.2 OrderItemDao 类的 _queryOrder_OrderItems() 方法

OrderItemDao 类的 _queryOrder_OrderItems() 方法用于根据 Order 的主键值查询关联的 OrderItem 列表。以下是其部分源码及详细分析:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseStatement;
import org.greenrobot.greendao.query.QueryBuilder;

import java.util.List;

// 继承自 AbstractDao,用于对 OrderItem 表进行操作
public class OrderItemDao extends AbstractDao<OrderItem, Long> {
    // 查询关联的 OrderItem 列表
    public List<OrderItem> _queryOrder_OrderItems(Long orderId) {
        // 创建一个查询构建器
        QueryBuilder<OrderItem> queryBuilder = queryBuilder();
        // 添加查询条件,根据 orderId 进行查询
        queryBuilder.where(OrderItemDao.Properties.OrderId.eq(orderId));
        // 执行查询,返回结果列表
        return queryBuilder.list();
    }
}
  • _queryOrder_OrderItems() 方法流程
    1. 创建查询构建器:调用 queryBuilder() 方法创建一个 QueryBuilder 对象,用于构建查询语句。
    2. 添加查询条件:使用 queryBuilder.where(OrderItemDao.Properties.OrderId.eq(orderId)) 方法添加查询条件,只查询 orderId 等于指定值的 OrderItem 记录。
    3. 执行查询:调用 queryBuilder.list() 方法执行查询操作,返回符合条件的 OrderItem 列表。

6.5 多对多关系

在 GreenDao 中,多对多关系可以通过中间表来实现。以下是一个多对多关系的示例:

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.JoinEntity;
import org.greenrobot.greendao.annotation.ToMany;
import org.greenrobot.greendao.annotation.Generated;
import java.util.List;

@Entity
public class Student {
    @Id(autoincrement = true)
    private Long id;
    private String name;
    // 使用 @ToMany 和 @JoinEntity 注解定义多对多关系
    @ToMany
    @JoinEntity(
            entity = StudentCourse.class,
            sourceProperty = "studentId",
            targetProperty = "courseId"
    )
    private List<Course> courses;

    @Generated(hash = 123456789)
    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    @Generated(hash = 987654321)
    public Student() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Course> getCourses() {
        if (courses == null) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            CourseDao targetDao = daoSession.getCourseDao();
            courses = targetDao._queryStudent_Courses(id);
        }
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }
}

@Entity
public class Course {
    @Id(autoincrement = true)
    private Long id
6.5.1 多对多关系示例代码补全与整体结构
import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.JoinEntity;
import org.greenrobot.greendao.annotation.ToMany;
import org.greenrobot.greendao.annotation.Generated;
import java.util.List;

@Entity
public class Student {
    @Id(autoincrement = true)
    private Long id;
    private String name;
    // 使用 @ToMany 和 @JoinEntity 注解定义多对多关系
    @ToMany
    @JoinEntity(
            entity = StudentCourse.class,
            sourceProperty = "studentId",
            targetProperty = "courseId"
    )
    private List<Course> courses;

    @Generated(hash = 123456789)
    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    @Generated(hash = 987654321)
    public Student() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Course> getCourses() {
        if (courses == null) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            CourseDao targetDao = daoSession.getCourseDao();
            courses = targetDao._queryStudent_Courses(id);
        }
        return courses;
    }

    public void setCourses(List<Course> courses) {
        this.courses = courses;
    }
}

@Entity
public class Course {
    @Id(autoincrement = true)
    private Long id;
    private String courseName;
    // 使用 @ToMany 和 @JoinEntity 注解定义多对多关系
    @ToMany
    @JoinEntity(
            entity = StudentCourse.class,
            sourceProperty = "courseId",
            targetProperty = "studentId"
    )
    private List<Student> students;

    @Generated(hash = 123456789)
    public Course(Long id, String courseName) {
        this.id = id;
        this.courseName = courseName;
    }

    @Generated(hash = 987654321)
    public Course() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getCourseName() {
        return this.courseName;
    }

    public void setCourseName(String courseName) {
        this.courseName = courseName;
    }

    public List<Student> getStudents() {
        if (students == null) {
            final DaoSession daoSession = this.daoSession;
            if (daoSession == null) {
                throw new DaoException("Entity is detached from DAO context");
            }
            StudentDao targetDao = daoSession.getStudentDao();
            students = targetDao._queryCourse_Students(id);
        }
        return students;
    }

    public void setStudents(List<Student> students) {
        this.students = students;
    }
}

@Entity
public class StudentCourse {
    @Id(autoincrement = true)
    private Long id;
    private Long studentId;
    private Long courseId;

    @Generated(hash = 123456789)
    public StudentCourse(Long id, Long studentId, Long courseId) {
        this.id = id;
        this.studentId = studentId;
        this.courseId = courseId;
    }

    @Generated(hash = 987654321)
    public StudentCourse() {
    }

    // Getter 和 Setter 方法
    public Long getId() {
        return this.id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getStudentId() {
        return this.studentId;
    }

    public void setStudentId(Long studentId) {
        this.studentId = studentId;
    }

    public Long getCourseId() {
        return this.courseId;
    }

    public void setCourseId(Long courseId) {
        this.courseId = courseId;
    }
}

在这个示例中,Student 类和 Course 类通过 StudentCourse 中间表建立了多对多关系。@JoinEntity 注解指定了中间表的实体类以及关联属性。

6.5.2 源码分析
Student 类的 getCourses() 方法
public List<Course> getCourses() {
    if (courses == null) {
        final DaoSession daoSession = this.daoSession;
        if (daoSession == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        CourseDao targetDao = daoSession.getCourseDao();
        courses = targetDao._queryStudent_Courses(id);
    }
    return courses;
}
  • 流程分析
    1. 检查关联列表是否为空:若 coursesnull,则需要从数据库中加载关联的 Course 对象列表。
    2. 获取 DaoSession 对象DaoSession 是 GreenDao 中用于管理数据库会话的核心对象,若 daoSessionnull,则抛出 DaoException 异常。
    3. 获取 CourseDao 对象:通过 daoSession.getCourseDao() 方法获取 CourseDao 对象,用于执行数据库查询操作。
    4. 查询关联的 Course 列表:调用 targetDao._queryStudent_Courses(id) 方法,根据 Student 的主键 id 查询关联的 Course 对象列表。
CourseDao 类的 _queryStudent_Courses() 方法
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.Cursor;
import org.greenrobot.greendao.query.QueryBuilder;

import java.util.List;

public class CourseDao extends AbstractDao<Course, Long> {
    public List<Course> _queryStudent_Courses(Long studentId) {
        QueryBuilder<Course> queryBuilder = queryBuilder();
        queryBuilder.join(StudentCourse.class, StudentCourseDao.Properties.CourseId)
                   .where(StudentCourseDao.Properties.StudentId.eq(studentId));
        return queryBuilder.list();
    }
}
  • 流程分析
    1. 创建查询构建器:使用 queryBuilder() 方法创建一个 QueryBuilder 对象,用于构建查询语句。
    2. 执行连接查询:通过 queryBuilder.join(StudentCourse.class, StudentCourseDao.Properties.CourseId) 方法,将 Course 表和 StudentCourse 中间表进行连接,连接条件为 StudentCourse 表的 courseId 字段。
    3. 添加查询条件:使用 where(StudentCourseDao.Properties.StudentId.eq(studentId)) 方法,添加查询条件,只查询 StudentCourse 表中 studentId 等于指定值的记录。
    4. 执行查询并返回结果:调用 queryBuilder.list() 方法执行查询操作,返回符合条件的 Course 对象列表。

6.6 关联关系的性能优化

6.6.1 延迟加载

GreenDao 的关联关系默认采用延迟加载策略。例如,在一对一和一对多关系中,关联对象只有在首次访问时才会从数据库中加载。这种策略可以避免不必要的数据库查询,提高性能。

User 类的 getAddress() 方法为例:

public Address getAddress() {
    Long __key = this.addressId;
    if (address__resolvedKey == null || !address__resolvedKey.equals(__key)) {
        final DaoSession daoSession = this.daoSession;
        if (daoSession == null) {
            throw new DaoException("Entity is detached from DAO context");
        }
        AddressDao targetDao = daoSession.getAddressDao();
        Address addressNew = targetDao.load(__key);
        synchronized (this) {
            address = addressNew;
            address__resolvedKey = __key;
        }
    }
    return address;
}

只有当调用 getAddress() 方法时,才会从数据库中加载关联的 Address 对象。

6.6.2 预加载

在某些情况下,延迟加载可能会导致多次数据库查询,影响性能。这时可以使用预加载策略,一次性加载所有关联对象。

例如,在查询 Order 对象时,同时预加载其关联的 OrderItem 列表:

OrderDao orderDao = daoSession.getOrderDao();
Query<Order> query = orderDao.queryBuilder()
                            .join(OrderItem.class, OrderItemDao.Properties.OrderId)
                            .where(OrderDao.Properties.Id.eq(orderId))
                            .build();
Order order = query.unique();

通过 join() 方法将 Order 表和 OrderItem 表进行连接,一次性加载 Order 对象及其关联的 OrderItem 列表,减少数据库查询次数。

6.6.3 索引优化

在关联关系中,合理使用索引可以提高查询性能。例如,在 StudentCourse 中间表的 studentIdcourseId 字段上创建索引,可以加快多对多关系的查询速度。

import org.greenrobot.greendao.annotation.Entity;
import org.greenrobot.greendao.annotation.Id;
import org.greenrobot.greendao.annotation.Index;

@Entity
public class StudentCourse {
    @Id(autoincrement = true)
    private Long id;
    @Index
    private Long studentId;
    @Index
    private Long courseId;

    // 其他代码...
}

通过 @Index 注解为 studentIdcourseId 字段创建索引,当进行多对多关系查询时,数据库可以更快地定位到相关记录。

七、实体类的事务处理

7.1 事务的基本概念

在数据库操作中,事务是一组不可分割的操作序列,要么全部执行成功,要么全部失败回滚。事务具有四个特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),通常简称为 ACID 特性。

在 GreenDao 中,事务处理可以确保一系列数据库操作的完整性和一致性,避免数据出现不一致的情况。

7.2 事务的使用示例

以下是一个使用 GreenDao 进行事务处理的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaoSession daoSession = MyApplication.getDaoSession();
        final UserDao userDao = daoSession.getUserDao();

        // 开启事务
        daoSession.runInTx(new Runnable() {
            @Override
            public void run() {
                try {
                    // 插入新用户
                    User user1 = new User();
                    user1.setName("Alice");
                    user1.setAge(22);
                    userDao.insert(user1);

                    // 更新用户信息
                    User user2 = userDao.load(1L);
                    if (user2 != null) {
                        user2.setName("Updated Name");
                        userDao.update(user2);
                    }

                    // 删除用户
                    User user3 = userDao.load(2L);
                    if (user3 != null) {
                        userDao.delete(user3);
                    }

                    // 提交事务
                    // 这里的事务提交由 GreenDao 自动处理,在 runInTx 方法中没有异常抛出则会自动提交
                } catch (Exception e) {
                    // 回滚事务
                    // GreenDao 会在 runInTx 方法中捕获到异常时自动回滚事务
                    e.printStackTrace();
                }
            }
        });
    }
}

7.3 源码分析

7.3.1 DaoSession 类的 runInTx() 方法
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;

import java.util.HashMap;
import java.util.Map;

public class DaoSession {
    private final Database db;
    private final Map<Class<?>, AbstractDao<?, ?>> entityToDao;

    public DaoSession(Database db, Map<Class<?>, AbstractDao<?, ?>> entityToDao) {
        this.db = db;
        this.entityToDao = entityToDao;
    }

    public void runInTx(Runnable runnable) {
        db.beginTransaction();
        try {
            runnable.run();
            db.setTransactionSuccessful();
        } finally {
            db.endTransaction();
        }
    }
}
  • 流程分析
    1. 开启事务:调用 db.beginTransaction() 方法开启一个数据库事务。
    2. 执行事务操作:调用 runnable.run() 方法,执行传入的 Runnable 对象中的数据库操作。
    3. 设置事务成功:如果 runnable.run() 方法执行过程中没有抛出异常,则调用 db.setTransactionSuccessful() 方法将事务标记为成功。
    4. 结束事务:在 finally 块中,调用 db.endTransaction() 方法结束事务。如果事务被标记为成功,则提交事务;否则,回滚事务。
7.3.2 Database 类的事务方法

Database 类是 GreenDao 中用于操作数据库的核心类,其事务相关方法如下:

import android.database.sqlite.SQLiteDatabase;

public class Database {
    private final SQLiteDatabase sqliteDatabase;

    public Database(SQLiteDatabase sqliteDatabase) {
        this.sqliteDatabase = sqliteDatabase;
    }

    public void beginTransaction() {
        sqliteDatabase.beginTransaction();
    }

    public void setTransactionSuccessful() {
        sqliteDatabase.setTransactionSuccessful();
    }

    public void endTransaction() {
        sqliteDatabase.endTransaction();
    }
}
  • beginTransaction() 方法:调用 SQLiteDatabasebeginTransaction() 方法开启一个数据库事务。
  • setTransactionSuccessful() 方法:调用 SQLiteDatabasesetTransactionSuccessful() 方法将事务标记为成功。
  • endTransaction() 方法:调用 SQLiteDatabaseendTransaction() 方法结束事务。如果事务被标记为成功,则提交事务;否则,回滚事务。

7.4 事务的嵌套使用

在 GreenDao 中,事务可以嵌套使用。嵌套事务的实现原理是,内层事务的提交或回滚不会直接影响外层事务,只有当外层事务结束时,才会根据整个事务链的状态决定最终的提交或回滚操作。

以下是一个嵌套事务的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaoSession daoSession = MyApplication.getDaoSession();
        final UserDao userDao = daoSession.getUserDao();

        daoSession.runInTx(new Runnable() {
            @Override
            public void run() {
                try {
                    // 外层事务操作
                    User user1 = new User();
                    user1.setName("Outer User");
                    user1.setAge(30);
                    userDao.insert(user1);

                    // 嵌套内层事务
                    daoSession.runInTx(new Runnable() {
                        @Override
                        public void run() {
                            try {
                                // 内层事务操作
                                User user2 = new User();
                                user2.setName("Inner User");
                                user2.setAge(25);
                                userDao.insert(user2);

                                // 内层事务提交(由 GreenDao 自动处理)
                            } catch (Exception e) {
                                // 内层事务回滚(由 GreenDao 自动处理)
                                e.printStackTrace();
                            }
                        }
                    });

                    // 外层事务提交(由 GreenDao 自动处理)
                } catch (Exception e) {
                    // 外层事务回滚(由 GreenDao 自动处理)
                    e.printStackTrace();
                }
            }
        });
    }
}

在这个示例中,外层事务插入了一个用户记录,内层事务又插入了一个用户记录。如果内层事务出现异常,只会回滚内层事务的操作,外层事务的操作不受影响;如果外层事务出现异常,则整个事务链都会回滚。

八、实体类的版本管理

8.1 数据库版本的概念

在 Android 应用开发中,随着应用的不断迭代,数据库的结构可能会发生变化,例如添加新的表、修改表结构、删除表等。为了管理这些变化,需要对数据库进行版本控制。

在 GreenDao 中,数据库版本由 Schema 对象的 version 属性指定,每次数据库结构发生变化时,需要更新版本号。

8.2 数据库升级的实现

8.2.1 简单的数据库升级示例
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseOpenHelper;
import org.greenrobot.greendao.query.QueryBuilder;

import android.content.Context;

public class MyOpenHelper extends DatabaseOpenHelper {
    public MyOpenHelper(Context context, String name) {
        super(context, name, null, 2); // 数据库版本号为 2
    }

    @Override
    public void onCreate(Database db) {
        // 创建数据库表
        UserDao.createTable(db, false);
        AddressDao.createTable(db, false);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        if (oldVersion < 2) {
            // 从版本 1 升级到版本 2
            // 例如,添加一个新的表
            NewTableDao.createTable(db, false);
        }
    }
}
8.2.2 源码分析
DatabaseOpenHelper
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.StandardDatabase;

public abstract class DatabaseOpenHelper extends SQLiteOpenHelper {
    public DatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        onCreate(new StandardDatabase(sqLiteDatabase));
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
        onUpgrade(new StandardDatabase(sqLiteDatabase), oldVersion, newVersion);
    }

    public abstract void onCreate(Database db);

    public abstract void onUpgrade(Database db, int oldVersion, int newVersion);
}
  • onCreate() 方法:当数据库第一次创建时,会调用该方法。在该方法中,需要创建数据库的表结构。
  • onUpgrade() 方法:当数据库版本号发生变化时,会调用该方法。在该方法中,需要根据旧版本号和新版本号进行相应的数据库升级操作。
DaoMaster
import org.greenrobot.greendao.AbstractDaoMaster;
import org.greenrobot.greendao.database.Database;

import java.util.HashMap;
import java.util.Map;

public class DaoMaster extends AbstractDaoMaster {
    public static final int SCHEMA_VERSION = 2;

    public DaoMaster(Database db) {
        super(db, SCHEMA_VERSION);
        registerDaoClass(UserDao.class);
        registerDaoClass(AddressDao.class);
        registerDaoClass(NewTableDao.class);
    }

    @Override
    public DaoSession newSession() {
        return new DaoSession(db, daoConfigMap);
    }
}
  • SCHEMA_VERSION 常量:指定数据库的版本号。
  • registerDaoClass() 方法:注册 DAO 类,将 DAO 类与数据库表进行关联。

8.3 数据迁移

在数据库升级过程中,可能需要将旧版本的数据迁移到新版本的数据库中。以下是一个简单的数据迁移示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.database.DatabaseOpenHelper;
import org.greenrobot.greendao.query.QueryBuilder;

import android.content.Context;

public class MyOpenHelper extends DatabaseOpenHelper {
    public MyOpenHelper(Context context, String name) {
        super(context, name, null, 2); // 数据库版本号为 2
    }

    @Override
    public void onCreate(Database db) {
        // 创建数据库表
        UserDao.createTable(db, false);
        AddressDao.createTable(db, false);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        if (oldVersion < 2) {
            // 从版本 1 升级到版本 2
            // 例如,添加一个新的表
            NewTableDao.createTable(db, false);

            // 数据迁移
            db.execSQL("INSERT INTO NewTable (column1, column2) SELECT oldColumn1, oldColumn2 FROM OldTable");
        }
    }
}

在这个示例中,从版本 1 升级到版本 2 时,添加了一个新的表 NewTable,并将旧表 OldTable 中的数据迁移到新表中。

九、总结与展望

9.1 总结

通过对 Android GreenDao 实体类模块的深入分析,我们全面了解了其使用原理和实现细节。实体类作为 GreenDao 的核心组成部分,通过注解的方式实现了 Java 对象与数据库表之间的映射,极大地简化了数据库操作。

9.1.1 注解的强大功能

GreenDao 提供了丰富的注解,如 @Entity@Id@Property 等,这些注解可以灵活地定义实体类的属性和关联关系,使得开发者可以根据实际需求轻松地创建数据库表结构。

9.1.2 代码生成机制

GreenDao 的代码生成器可以根据实体类的定义自动生成数据库表结构和操作代码,减少了开发者的手动编码工作量,提高了开发效率。

9.1.3 高效的数据库操作

通过 DAO 类提供的增删改查方法,开发者可以方便地进行数据库操作,并且 GreenDao 内部对数据库操作进行了优化,保证了操作的高效性。

9.1.4 关联关系的处理

GreenDao 支持一对一、一对多和多对多等多种关联关系,通过合理使用 @ToOne@ToMany@JoinEntity 注解,可以轻松实现实体类之间的关联关系,并且提供了延迟加载和预加载等策略,优化了关联关系的查询性能。

9.1.5 事务处理和版本管理

GreenDao 提供了事务处理机制,确保了数据库操作的原子性和一致性;同时,通过数据库版本管理,可以方便地进行数据库升级和数据迁移,保证了应用在不同版本之间的兼容性。

9.2 展望

虽然 GreenDao 已经是一个非常优秀的 Android ORM 框架,但随着技术的不断发展和应用需求的不断变化,仍然有一些方面可以进一步改进和完善。

9.2.1 性能优化

尽管 GreenDao 已经具有较高的性能,但在处理大规模数据时,仍然可能存在性能瓶颈。未来可以进一步优化数据库查询和操作的性能,例如采用更高效的索引策略、缓存机制等。

9.2.2 支持更多数据库类型

目前 GreenDao 主要支持 SQLite 数据库,未来可以考虑支持更多的数据库类型,如 MySQL、PostgreSQL 等,以满足不同应用场景的需求。

9.2.3 与其他框架的集成

可以进一步加强 GreenDao 与其他 Android 开发框架的集成,如 RxJava、Retrofit 等,提供更便捷的开发体验,提高开发效率。

9.2.4 简化配置和使用

可以进一步简化 GreenDao 的配置和使用过程,提供更简洁的 API 接口,降低开发者的学习成本,使得更多的开发者能够轻松上手。

总之,Android GreenDao 实体类模块为 Android 开发者提供了一种高效、便捷的数据库操作方式。随着技术的不断进步,相信 GreenDao 将会不断完善和发展,为 Android 应用开发带来更多的便利和可能性。

以上技术博客详细分析了 Android GreenDao 实体类模块的使用原理,从实体类的定义、注解、代码生成、数据库交互、关联关系、事务处理、版本管理等多个方面进行了深入探讨,并对未来的发展进行了展望。希望能够帮助开发者更好地理解和使用 GreenDao 框架。