深度揭秘:Android GreenDao 数据库操作模块使用原理大剖析(2)

70 阅读32分钟

深度揭秘:Android GreenDao 数据库操作模块使用原理大剖析

一、引言

在 Android 开发的广阔天地中,数据存储与管理始终是至关重要的环节。数据库作为数据存储的核心载体,其高效操作直接影响着应用的性能和用户体验。GreenDao 作为一款轻量级且性能卓越的 ORM(对象关系映射)框架,在 Android 数据库操作领域占据着重要地位。它能够将 Java 对象与数据库表进行无缝映射,极大地简化了数据库操作的复杂度,让开发者可以专注于业务逻辑的实现。

本文将聚焦于 Android GreenDao 数据库操作模块,深入探究其使用原理。我们将从源码级别出发,详细剖析每一个操作步骤背后的逻辑,为你呈现一个全面而深入的 GreenDao 数据库操作画卷。通过本文的学习,你将对 GreenDao 的工作机制有更深刻的理解,从而在实际开发中更加得心应手地运用它。

二、GreenDao 数据库操作模块概述

2.1 GreenDao 简介

GreenDao 是一个专门为 Android 平台设计的轻量级 ORM 框架,它通过将 Java 对象映射到数据库表,实现了数据库操作的自动化和简化。与传统的 SQLite 操作相比,GreenDao 具有更高的性能和更简洁的代码结构。它采用了代码生成的方式,根据开发者定义的实体类自动生成数据库操作所需的代码,大大减少了手动编写 SQL 语句的工作量。

2.2 数据库操作模块的作用

GreenDao 的数据库操作模块是整个框架的核心部分,它负责处理与数据库的交互,包括数据库的创建、表的创建、数据的增删改查等操作。通过该模块,开发者可以使用简单的 Java 代码来完成复杂的数据库操作,无需关心底层的 SQL 语句和数据库连接细节。

2.3 数据库操作模块的基本使用流程

一般来说,使用 GreenDao 数据库操作模块的基本流程如下:

  1. 初始化数据库:创建数据库实例,通常使用 DaoMasterDaoSession 来管理数据库连接和会话。
  2. 获取 DAO 对象:通过 DaoSession 获取具体实体类的 DAO(数据访问对象)对象,用于执行数据库操作。
  3. 执行数据库操作:使用 DAO 对象执行增删改查等操作。
  4. 关闭数据库连接:在不需要使用数据库时,关闭数据库连接,释放资源。

三、数据库初始化

3.1 数据库创建与管理

在 GreenDao 中,DaoMaster 类负责数据库的创建和管理。以下是一个简单的数据库初始化示例:

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

// 假设这是你的 Application 类
import android.app.Application;

// 定义一个继承自 Application 的类
public class MyApplication extends Application {
    // 定义一个静态的 DaoSession 对象,用于全局访问数据库会话
    private static DaoSession daoSession;

    @Override
    public void onCreate() {
        super.onCreate();
        // 创建一个 OpenHelper 对象,用于管理数据库的打开和升级
        DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "my-database-name");
        // 通过 OpenHelper 获取一个可写的数据库实例
        Database db = helper.getWritableDb();
        // 创建一个 DaoMaster 对象,用于管理数据库的创建和版本控制
        DaoMaster daoMaster = new DaoMaster(db);
        // 通过 DaoMaster 创建一个 DaoSession 对象,用于获取 DAO 对象
        daoSession = daoMaster.newSession();
    }

    // 提供一个静态方法,用于获取全局的 DaoSession 对象
    public static DaoSession getDaoSession() {
        return daoSession;
    }
}

3.2 源码分析

3.2.1 DaoMaster.DevOpenHelper

DaoMaster.DevOpenHelper 是一个继承自 DatabaseOpenHelper 的辅助类,用于管理数据库的打开和升级。以下是其部分源码:

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

// 继承自 DatabaseOpenHelper,用于开发环境下的数据库操作
public static class DevOpenHelper extends DatabaseOpenHelper {
    // 构造函数,接收上下文和数据库名称作为参数
    public DevOpenHelper(Context context, String name) {
        // 调用父类的构造函数,传入上下文、数据库名称和游标工厂(这里为 null)
        super(context, name, null);
    }

    @Override
    public void onCreate(Database db) {
        // 调用 DaoMaster 的 createAllTables 方法,创建所有的数据库表
        DaoMaster.createAllTables(db, false);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        // 调用 DaoMaster 的 dropAllTables 方法,删除所有的数据库表
        DaoMaster.dropAllTables(db, true);
        // 再次调用 onCreate 方法,重新创建所有的数据库表
        onCreate(db);
    }
}
3.2.2 DaoMaster

DaoMaster 类负责数据库的创建和版本控制。以下是其部分源码:

import org.greenrobot.greendao.database.Database;

// 用于管理数据库的创建和版本控制
public class DaoMaster extends AbstractDaoMaster {
    // 定义数据库版本号
    public static final int SCHEMA_VERSION = 1;

    // 构造函数,接收一个数据库实例作为参数
    public DaoMaster(Database db) {
        // 调用父类的构造函数,传入数据库实例和版本号
        super(db, SCHEMA_VERSION);
        // 注册所有的 DAO 类
        registerDaoClass(UserDao.class);
    }

    // 创建所有的数据库表
    public static void createAllTables(Database db, boolean ifNotExists) {
        // 调用 UserDao 的 createTable 方法,创建 User 表
        UserDao.createTable(db, ifNotExists);
    }

    // 删除所有的数据库表
    public static void dropAllTables(Database db, boolean ifExists) {
        // 调用 UserDao 的 dropTable 方法,删除 User 表
        UserDao.dropTable(db, ifExists);
    }

    // 创建一个新的 DaoSession 对象
    @Override
    public DaoSession newSession() {
        // 创建一个新的 DaoSession 对象,传入数据库实例和所有的 DAO 对象
        return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);
    }
}
3.2.3 DaoSession

DaoSession 类是数据库操作的核心类,它负责管理数据库会话和获取 DAO 对象。以下是其部分源码:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.identityscope.IdentityScopeType;

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

// 用于管理数据库会话和获取 DAO 对象
public class DaoSession extends AbstractDaoSession {
    // 存储所有的 DAO 对象
    private final Map<Class<? extends AbstractDao<?, ?>>, AbstractDao<?, ?>> daoMap;
    // UserDao 对象
    private final UserDao userDao;

    // 构造函数,接收数据库实例、标识范围类型和 DAO 配置映射作为参数
    public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) {
        super(db);
        // 初始化 DAO 映射
        daoMap = new HashMap<>();
        // 创建 UserDao 对象
        userDao = new UserDao(daoConfigMap.get(UserDao.class), this);
        // 将 UserDao 对象添加到 DAO 映射中
        daoMap.put(UserDao.class, userDao);
    }

    // 获取 UserDao 对象
    public UserDao getUserDao() {
        return userDao;
    }

    // 根据 DAO 类获取对应的 DAO 对象
    @Override
    public <T extends AbstractDao<?, ?>> T getDao(Class<T> daoClass) {
        // 从 DAO 映射中获取对应的 DAO 对象
        @SuppressWarnings("unchecked")
        T dao = (T) daoMap.get(daoClass);
        if (dao == null) {
            throw new DaoException("No DAO registered for " + daoClass);
        }
        return dao;
    }
}

3.3 数据库初始化流程总结

  1. 创建 DaoMaster.DevOpenHelper 对象,用于管理数据库的打开和升级。
  2. 通过 DevOpenHelper 获取可写的数据库实例。
  3. 创建 DaoMaster 对象,用于管理数据库的创建和版本控制。
  4. 通过 DaoMaster 创建 DaoSession 对象,用于获取 DAO 对象。
  5. 在需要使用数据库操作时,通过 DaoSession 获取具体的 DAO 对象。

四、数据插入操作

4.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);
    }
}

4.2 源码分析

4.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();
        }
    }
}
4.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;
    }
}

4.3 插入单条数据流程总结

  1. 创建实体类对象,并设置其属性值。
  2. 通过 DaoSession 获取对应的 DAO 对象。
  3. 调用 DAO 对象的 insert() 方法,该方法会开启一个事务。
  4. 在事务中,获取插入语句的 DatabaseStatement 对象,并绑定参数。
  5. 执行插入操作,返回插入记录的 ID。
  6. 设置事务成功,结束事务。

4.4 插入多条数据

如果需要插入多条数据,可以使用 insertInTx() 方法或 insertOrReplaceInTx() 方法。以下是一个插入多条数据的示例:

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.ArrayList;
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();

        // 创建一个 User 列表
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setName("User " + i);
            user.setAge(20 + i);
            userList.add(user);
        }

        // 调用 UserDao 的 insertInTx 方法插入多条数据
        userDao.insertInTx(userList);
    }
}

4.5 源码分析

4.5.1 UserDao 类的 insertInTx() 方法

UserDao 类的 insertInTx() 方法用于在事务中插入多条数据。以下是其部分源码:

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.Collection;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 在事务中插入多条数据
    public void insertInTx(Collection<User> entities) {
        // 获取数据库实例
        Database db = getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 遍历实体列表
            for (User entity : entities) {
                // 调用 insert 方法插入单条数据
                insert(entity);
            }
            // 设置事务成功
            db.setTransactionSuccessful();
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}

4.6 插入多条数据流程总结

  1. 创建一个实体类对象列表,并设置其属性值。
  2. 通过 DaoSession 获取对应的 DAO 对象。
  3. 调用 DAO 对象的 insertInTx() 方法,该方法会开启一个事务。
  4. 在事务中,遍历实体列表,调用 insert() 方法插入单条数据。
  5. 设置事务成功,结束事务。

五、数据查询操作

5.1 简单查询

GreenDao 提供了简单的查询方法,如 load()loadAll() 等。以下是一个简单查询的示例:

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.2 源码分析

5.2.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.2.2 QueryBuilder 类的 list() 方法

QueryBuilder 类用于构建查询语句。以下是其 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 List<T> list() {
        // 获取数据库实例
        Database db = dao.getDatabase();
        // 构建查询语句
        String sql = buildQueryString();
        // 执行查询,获取游标
        Cursor cursor = db.rawQuery(sql, null);
        try {
            // 创建一个结果列表
            List<T> result = new ArrayList<>();
            // 遍历游标
            while (cursor.moveToNext()) {
                // 从游标中读取记录,创建实体对象
                T entity = dao.readEntity(cursor, 0);
                // 将实体对象添加到结果列表中
                result.add(entity);
            }
            return result;
        } finally {
            // 关闭游标
            cursor.close();
        }
    }
}
5.2.3 UserDao 类的 load() 方法

UserDao 类的 load() 方法用于根据 ID 查询记录。以下是其部分源码:

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> {
    // 根据 ID 查询记录
    @Override
    public User load(Long key) {
        // 创建一个查询构建器
        QueryBuilder<User> queryBuilder = queryBuilder();
        // 添加查询条件,根据 ID 进行查询
        queryBuilder.where(Properties.Id.eq(key));
        // 执行查询,返回第一条记录
        return queryBuilder.unique();
    }
}

5.3 简单查询流程总结

  1. 通过 DaoSession 获取对应的 DAO 对象。
  2. 调用 DAO 对象的 loadAll() 方法查询所有记录,该方法会创建一个查询构建器,执行查询并返回结果列表。
  3. 调用 DAO 对象的 load() 方法根据 ID 查询记录,该方法会创建一个查询构建器,添加查询条件,执行查询并返回第一条记录。

5.4 条件查询

除了简单查询,GreenDao 还支持条件查询。可以使用 QueryBuilder 类来构建复杂的查询条件。以下是一个条件查询的示例:

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();

        // 创建一个查询构建器
        QueryBuilder<User> queryBuilder = userDao.queryBuilder();
        // 添加查询条件,查询年龄大于 20 的用户
        queryBuilder.where(UserDao.Properties.Age.gt(20));
        // 执行查询,返回结果列表
        List<User> userList = queryBuilder.list();
        for (User user : userList) {
            System.out.println("User: " + user.getName() + ", Age: " + user.getAge());
        }
    }
}

5.5 源码分析

5.5.1 QueryBuilder 类的 where() 方法

QueryBuilder 类的 where() 方法用于添加查询条件。以下是其部分源码:

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 condition : condMore) {
                whereConditions.add(condition);
            }
        }
        return this;
    }
}
5.5.2 WhereCondition 类及其子类

WhereCondition 类是所有查询条件的基类,它有多个子类,如 PropertyCondition 用于表示属性条件。以下是 PropertyCondition 类的部分源码:

import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.Property;

// 表示属性条件
public class PropertyCondition implements WhereCondition {
    // 属性对象
    private final Property property;
    // 操作符
    private final String op;
    // 参数
    private final Object value;

    // 构造函数,接收属性对象、操作符和参数作为参数
    public PropertyCondition(Property property, String op, Object value) {
        this.property = property;
        this.op = op;
        this.value = value;
    }

    @Override
    public void appendTo(StringBuilder builder, String tablePrefix) {
        // 将属性名和操作符添加到查询语句构建器中
        builder.append(tablePrefix).append('.').append(property.columnName).append(' ').append(op);
        if (value != null) {
            // 添加参数占位符
            builder.append(" ?");
        }
    }

    @Override
    public void appendValuesTo(List<Object> values) {
        if (value != null) {
            // 将参数添加到参数列表中
            values.add(value);
        }
    }
}

5.6 条件查询流程总结

  1. 通过 DaoSession 获取对应的 DAO 对象。
  2. 创建一个 QueryBuilder 对象。
  3. 调用 QueryBuilder 对象的 where() 方法添加查询条件,where() 方法会将查询条件添加到条件列表中。
  4. 调用 QueryBuilder 对象的 list() 方法执行查询,返回结果列表。

六、数据更新操作

6.1 更新单条数据

使用 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("New Name");
            user.setAge(30);
            // 调用 UserDao 的 update 方法更新数据
            userDao.update(user);
        }
    }
}

6.2 源码分析

6.2.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();
        }
    }
}
6.2.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;
    }
}

6.3 更新单条数据流程总结

  1. 通过 DaoSession 获取对应的 DAO 对象。
  2. 根据 ID 查询需要更新的记录,创建实体类对象。
  3. 设置实体类对象的属性值。
  4. 调用 DAO 对象的 update() 方法,该方法会开启一个事务。
  5. 在事务中,获取更新语句的 DatabaseStatement 对象,并绑定参数。
  6. 执行更新操作。
  7. 设置事务成功,结束事务。

6.4 更新多条数据

如果需要更新多条数据,可以使用 updateInTx() 方法。以下是一个更新多条数据的示例:

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.ArrayList;
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) {
            // 更新用户信息
            user.setName("Updated Name");
            user.setAge(user.getAge() + 1);
        }

        // 调用 UserDao 的 updateInTx 方法更新多条数据
        userDao.updateInTx(userList);
    }
}

6.5 源码分析

6.5.1 UserDao 类的 updateInTx() 方法

UserDao 类的 updateInTx() 方法用于在事务中更新多条数据。以下是其部分源码:

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.Collection;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 在事务中更新多条数据
    public void updateInTx(Collection<User> entities) {
        // 获取数据库实例
        Database db = getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 遍历实体列表
            for (User entity : entities) {
                // 调用 update 方法更新单条数据
                update(entity);
            }
            // 设置事务成功
            db.setTransactionSuccessful();
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}

6.6 更新多条数据流程总结

  1. 通过 DaoSession 获取对应的 DAO 对象。
  2. 查询需要更新的记录,创建实体类对象列表。
  3. 设置实体类对象列表中每个对象的属性值。
  4. 调用 DAO 对象的 updateInTx() 方法,该方法会开启一个事务。
  5. 在事务中,遍历实体列表,调用 update() 方法更新单条数据。
  6. 设置事务成功,结束事务。

七、数据删除操作

7.1 删除单条数据

使用 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);
        }
    }
}

7.2 源码分析

7.2.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();
            // 绑定参数
1. 方法概述

UserDao 类继承自 AbstractDao,它重写了 delete() 方法,用于删除单条记录。在删除操作中,数据库事务的使用是为了保证数据操作的原子性,即要么全部删除成功,要么全部失败,避免出现部分删除的不一致情况。

2. 代码详细解释
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();
        }
    }
}
  • SQL_DELETE 常量:这是一个静态常量,存储了用于删除记录的 SQL 语句。TABLENAME 代表数据库表名,UserDao.Properties.Id.columnName 表示主键列名,? 是占位符,用于后续绑定具体的主键值。
  • delete(User entity) 方法
    • Database db = getDatabase();:调用 getDatabase() 方法从 AbstractDao 基类中获取当前操作的数据库实例,后续的操作都基于这个数据库实例进行。
    • db.beginTransaction();:开启一个数据库事务。事务是一组不可分割的数据库操作序列,要么全部执行成功,要么全部失败回滚。
    • DatabaseStatement stmt = getDeleteStatement();:调用 getDeleteStatement() 方法获取一个预编译的删除语句的 DatabaseStatement 对象。这个对象可以用来执行 SQL 语句,并且可以重复使用,提高性能。
    • stmt.clearBindings();:清除之前绑定在 DatabaseStatement 对象上的所有参数,确保本次绑定的参数是新的。
    • stmt.bindLong(1, entity.getId());:将 User 实体对象的主键值绑定到 SQL 语句中的占位符 ? 上。这里的 1 表示第一个占位符,entity.getId() 是要删除记录的主键值。
    • stmt.executeUpdateDelete();:执行预编译的删除 SQL 语句,删除数据库中对应的记录。
    • db.setTransactionSuccessful();:如果删除操作执行成功,将事务标记为成功。
    • db.endTransaction();:无论删除操作是否成功,最终都会结束事务。如果事务标记为成功,则提交事务,将删除操作持久化到数据库;如果没有标记为成功,则回滚事务,撤销本次删除操作。
3. 异常处理与事务保障

delete() 方法中使用了 try-finally 块,确保无论删除操作是否成功,事务都会被正确结束。如果在删除过程中发生异常,事务不会被标记为成功,最终会回滚,保证数据库的一致性。例如,如果在绑定参数或者执行删除语句时出现异常,db.setTransactionSuccessful() 不会被执行,事务会回滚,数据库不会被修改。

4. 性能优化点
  • 预编译 SQL 语句:通过 getDeleteStatement() 方法获取预编译的 DatabaseStatement 对象,避免了每次删除操作都重新编译 SQL 语句,提高了性能。
  • 事务操作:将删除操作放在事务中执行,减少了数据库的提交次数,提高了操作效率。如果需要删除多条记录,可以在一个事务中批量执行,进一步提升性能。
7.2.2 AbstractDao 类的 getDeleteStatement() 方法深入分析
1. 方法概述

AbstractDao 类是所有 DAO 类的基类,getDeleteStatement() 方法用于获取删除语句的 DatabaseStatement 对象。这个方法的作用是确保在需要执行删除操作时,能够高效地获取预编译的 SQL 语句对象。

2. 代码详细解释
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;
    }
}
  • deleteStatement 成员变量:用于存储预编译的删除语句的 DatabaseStatement 对象。使用成员变量存储可以避免重复编译 SQL 语句,提高性能。
  • getDeleteStatement() 方法
    • if (deleteStatement == null):检查 deleteStatement 是否已经被初始化。如果为 null,说明还没有编译过删除语句,需要进行编译。
    • Database db = getDatabase();:获取当前操作的数据库实例。
    • deleteStatement = db.compileStatement(SQL_DELETE);:调用数据库实例的 compileStatement() 方法,将 SQL_DELETE 常量表示的 SQL 语句进行预编译,得到一个 DatabaseStatement 对象,并将其赋值给 deleteStatement 成员变量。
    • return deleteStatement;:返回预编译好的 DatabaseStatement 对象,供后续的删除操作使用。
3. 单例模式的应用

getDeleteStatement() 方法实际上采用了单例模式的思想。在整个 DAO 类的生命周期内,deleteStatement 只会被编译一次。每次调用 getDeleteStatement() 方法时,首先检查 deleteStatement 是否已经存在,如果存在则直接返回,避免了重复编译 SQL 语句,提高了性能。

4. 线程安全考虑

在多线程环境下,如果多个线程同时调用 getDeleteStatement() 方法,可能会出现竞态条件,导致 deleteStatement 被多次编译。为了避免这种情况,可以考虑使用同步机制,例如在方法上加 synchronized 关键字或者使用双重检查锁定等方式来保证线程安全。不过,在 GreenDao 的实现中,由于数据库操作通常是在主线程或者单线程环境下进行的,所以这种情况一般不会出现。

7.3 删除单条数据流程总结

1. 整体流程

删除单条数据的操作主要涉及到从 DaoSession 获取对应的 DAO 对象,根据主键查询到要删除的记录,然后调用 DAO 对象的 delete() 方法进行删除。具体步骤如下:

2. 详细步骤
  1. 获取 DAO 对象:通过 DaoSession 获取对应的 DAO 对象,例如 UserDao。这一步是为了后续能够调用 UserDao 中封装的删除方法。
DaoSession daoSession = MyApplication.getDaoSession();
UserDao userDao = daoSession.getUserDao();
  1. 查询要删除的记录:根据主键(如 id)查询到要删除的 User 实体对象。这一步可以使用 UserDaoload() 方法,根据主键值加载对应的记录。
User user = userDao.load(1L);
  1. 调用 delete() 方法:将查询到的 User 实体对象作为参数传递给 UserDaodelete() 方法。在 delete() 方法中,会开启一个数据库事务,获取预编译的删除语句的 DatabaseStatement 对象,绑定要删除记录的主键值,执行删除操作,最后根据操作结果标记事务是否成功并结束事务。
if (user != null) {
    userDao.delete(user);
}
3. 事务的重要性

在删除操作中使用事务可以保证数据的一致性。如果在删除过程中出现异常,事务会回滚,数据库不会被修改,避免了数据不一致的问题。例如,如果在执行删除语句时数据库连接中断,事务会自动回滚,保证数据库的完整性。

4. 性能优化建议
  • 批量删除:如果需要删除多条记录,建议使用批量删除的方式,例如使用 deleteInTx() 方法,将多条删除操作放在一个事务中执行,减少数据库的提交次数,提高性能。
  • 避免频繁创建和销毁 DAO 对象DaoSession 和 DAO 对象的创建和销毁会有一定的开销,建议在应用中复用这些对象,避免频繁创建和销毁。

7.4 删除多条数据

1. 批量删除的实现方式

如果需要删除多条数据,可以使用 deleteInTx() 方法或者根据条件删除。以下是几种常见的删除多条数据的方式。

2. 使用 deleteInTx() 方法

这种方式适用于已经获取到要删除的实体对象列表的情况。以下是示例代码:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;
import java.util.ArrayList;
import java.util.List;

// 假设这是在某个 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();

        // 查询要删除的用户列表
        QueryBuilder<User> queryBuilder = userDao.queryBuilder();
        queryBuilder.where(UserDao.Properties.Age.gt(30)); // 例如,删除年龄大于 30 的用户
        List<User> usersToDelete = queryBuilder.list();

        // 调用 UserDao 的 deleteInTx 方法批量删除数据
        userDao.deleteInTx(usersToDelete);
    }
}
  • 查询要删除的记录:使用 QueryBuilder 构建查询条件,查询出要删除的用户列表。
  • 调用 deleteInTx() 方法:将查询到的用户列表作为参数传递给 deleteInTx() 方法,该方法会在一个事务中批量删除这些记录。
3. 根据条件删除

如果需要根据某个条件删除多条记录,可以使用 QueryBuilder 构建删除查询。以下是示例代码:

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();

        // 创建一个查询构建器
        QueryBuilder<User> queryBuilder = userDao.queryBuilder();
        // 添加查询条件,删除年龄大于 30 的用户
        queryBuilder.where(UserDao.Properties.Age.gt(30));
        // 执行删除操作
        queryBuilder.buildDelete().executeDeleteWithoutDetachingEntities();
    }
}
  • 构建查询条件:使用 QueryBuilderwhere() 方法添加删除条件,例如删除年龄大于 30 的用户。
  • 执行删除操作:调用 buildDelete() 方法构建删除查询,然后调用 executeDeleteWithoutDetachingEntities() 方法执行删除操作。
4. 源码分析
4.1 UserDao 类的 deleteInTx() 方法
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.Collection;

// 继承自 AbstractDao,用于对 User 表进行操作
public class UserDao extends AbstractDao<User, Long> {
    // 在事务中批量删除数据
    public void deleteInTx(Collection<User> entities) {
        // 获取数据库实例
        Database db = getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 遍历实体列表
            for (User entity : entities) {
                // 调用 delete 方法删除单条数据
                delete(entity);
            }
            // 设置事务成功
            db.setTransactionSuccessful();
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}
  • 开启事务:在 deleteInTx() 方法中,首先开启一个数据库事务,确保所有的删除操作要么全部成功,要么全部失败。
  • 遍历实体列表:遍历传入的实体对象列表,调用 delete() 方法删除单条记录。
  • 提交事务:如果所有的删除操作都成功,将事务标记为成功并提交;如果出现异常,事务会自动回滚。
4.2 QueryBuilder 类的 buildDelete() 方法
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 DeleteQuery<T> buildDelete() {
        // 构建删除语句
        String sql = buildDeleteString();
        // 获取查询参数
        List<Object> values = buildValues();
        // 创建 DeleteQuery 对象
        return new DeleteQuery<>(dao, sql, values.toArray());
    }
}
  • 构建删除语句buildDeleteString() 方法根据查询条件构建删除的 SQL 语句。
  • 获取查询参数buildValues() 方法获取查询条件中的参数值。
  • 创建 DeleteQuery 对象:将构建好的 SQL 语句和参数值传递给 DeleteQuery 对象,用于后续的删除操作。
4.3 DeleteQuery 类的 executeDeleteWithoutDetachingEntities() 方法
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;

// 用于执行删除查询
public class DeleteQuery<T> extends AbstractQuery<T> {
    // 构造函数,接收 DAO 对象、SQL 语句和参数数组作为参数
    public DeleteQuery(AbstractDao<T, ?> dao, String sql, Object[] initialValues) {
        super(dao, sql, initialValues);
    }

    // 执行删除操作,不分离实体
    public int executeDeleteWithoutDetachingEntities() {
        // 获取数据库实例
        Database db = dao.getDatabase();
        // 开启事务
        db.beginTransaction();
        try {
            // 执行删除语句
            int count = db.delete(dao.getTablename(), buildWhereClause(), buildWhereArgs());
            // 设置事务成功
            db.setTransactionSuccessful();
            return count;
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}
  • 开启事务:在执行删除操作前,开启一个数据库事务,确保删除操作的原子性。
  • 执行删除语句:调用数据库实例的 delete() 方法,根据构建好的删除语句和参数执行删除操作。
  • 提交事务:如果删除操作成功,将事务标记为成功并提交;如果出现异常,事务会自动回滚。
5. 删除多条数据流程总结
  • 使用 deleteInTx() 方法
    1. 查询要删除的实体对象列表。
    2. 调用 deleteInTx() 方法,在一个事务中遍历实体对象列表,调用 delete() 方法删除单条记录。
    3. 根据操作结果提交或回滚事务。
  • 根据条件删除
    1. 使用 QueryBuilder 构建删除条件。
    2. 调用 buildDelete() 方法构建删除查询。
    3. 调用 executeDeleteWithoutDetachingEntities() 方法执行删除操作,在事务中执行删除语句,根据操作结果提交或回滚事务。

八、数据库事务管理

8.1 事务的概念与作用

在数据库操作中,事务是一组不可分割的操作序列,要么全部执行成功,要么全部失败回滚。事务的主要作用是保证数据的一致性和完整性。例如,在进行转账操作时,需要同时减少转出账户的余额和增加转入账户的余额,这两个操作必须作为一个事务来执行,否则可能会出现数据不一致的情况。

8.2 GreenDao 中的事务管理

GreenDao 提供了简单而强大的事务管理功能,可以通过 DaoSession 来管理事务。以下是一个使用事务进行批量插入的示例:

import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.query.QueryBuilder;
import java.util.ArrayList;
import java.util.List;

// 假设这是在某个 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();

        // 开启事务
        daoSession.runInTx(new Runnable() {
            @Override
            public void run() {
                // 创建要插入的用户列表
                List<User> userList = new ArrayList<>();
                for (int i = 0; i < 10; i++) {
                    User user = new User();
                    user.setName("User " + i);
                    user.setAge(20 + i);
                    userList.add(user);
                }

                // 批量插入用户数据
                userDao.insertInTx(userList);
            }
        });
    }
}
  • 开启事务:调用 DaoSessionrunInTx() 方法,传入一个 Runnable 对象。runInTx() 方法会在一个事务中执行 Runnable 对象的 run() 方法。
  • 执行数据库操作:在 run() 方法中,创建要插入的用户列表,调用 UserDaoinsertInTx() 方法批量插入数据。
  • 提交或回滚事务:如果 run() 方法执行过程中没有抛出异常,事务会自动提交;如果抛出异常,事务会自动回滚。
8.3 源码分析
8.3.1 DaoSession 类的 runInTx() 方法
import org.greenrobot.greendao.AbstractDao;
import org.greenrobot.greendao.database.Database;
import org.greenrobot.greendao.identityscope.IdentityScopeType;
import java.util.HashMap;
import java.util.Map;

// 用于管理数据库会话和获取 DAO 对象
public class DaoSession extends AbstractDaoSession {
    // 存储所有的 DAO 对象
    private final Map<Class<? extends AbstractDao<?, ?>>, AbstractDao<?, ?>> daoMap;
    // 数据库实例
    private final Database db;

    // 构造函数,接收数据库实例、标识范围类型和 DAO 配置映射作为参数
    public DaoSession(Database db, IdentityScopeType type, Map<Class<? extends AbstractDao<?, ?>>, DaoConfig> daoConfigMap) {
        super(db);
        this.db = db;
        // 初始化 DAO 映射
        daoMap = new HashMap<>();
        // 注册所有的 DAO 类
        registerDaoClasses(daoConfigMap);
    }

    // 在事务中执行操作
    public void runInTx(Runnable runnable) {
        // 开启事务
        db.beginTransaction();
        try {
            // 执行 Runnable 对象的 run 方法
            runnable.run();
            // 设置事务成功
            db.setTransactionSuccessful();
        } finally {
            // 结束事务
            db.endTransaction();
        }
    }
}
  • 开启事务:调用数据库实例的 beginTransaction() 方法开启一个事务。
  • 执行操作:调用 Runnable 对象的 run() 方法,执行传入的数据库操作。
  • 提交或回滚事务:如果 run() 方法执行过程中没有抛出异常,调用 db.setTransactionSuccessful() 方法将事务标记为成功,然后调用 db.endTransaction() 方法提交事务;如果抛出异常,db.setTransactionSuccessful() 不会被执行,db.endTransaction() 方法会回滚事务。
8.4 事务管理的注意事项
  • 异常处理:在事务中执行的操作可能会抛出异常,需要在 run() 方法中进行适当的异常处理,避免事务因异常而回滚。
  • 事务嵌套:虽然 GreenDao 支持事务嵌套,但尽量避免过度嵌套,以免增加事务管理的复杂度。
  • 性能考虑:事务的开启和提交会有一定的开销,对于批量操作,建议使用事务来提高性能;对于单个操作,不需要使用事务。

九、数据库版本升级与迁移

9.1 数据库版本升级的需求

随着应用的不断发展,数据库结构可能会发生变化,例如添加新的表、修改表结构等。为了保证数据的兼容性和完整性,需要对数据库进行版本升级。

9.2 GreenDao 中的数据库版本管理

在 GreenDao 中,数据库版本管理通过 DaoMasterDaoMaster.OpenHelper 来实现。以下是一个数据库版本升级的示例:

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

// 自定义 OpenHelper 类,用于管理数据库的打开和升级
public class MyOpenHelper extends DaoMaster.OpenHelper {
    public MyOpenHelper(Context context, String name) {
        super(context, name);
    }

    @Override
    public void onUpgrade(Database db, int oldVersion, int newVersion) {
        // 根据版本号进行不同的升级操作
        switch (oldVersion) {
            case 1:
                // 从版本 1 升级到版本 2 的操作
                db.execSQL("ALTER TABLE USER ADD COLUMN NEW_COLUMN TEXT");
            case 2:
                // 从版本 2 升级到版本 3 的操作
                db.execSQL("CREATE TABLE NEW_TABLE (ID INTEGER PRIMARY KEY, NAME TEXT)");
            // 可以继续添加更多的版本升级操作
        }
    }
}
  • 自定义 OpenHelper:继承自 DaoMaster.OpenHelper,重写 onUpgrade() 方法。
  • onUpgrade() 方法:根据旧版本号和新版本号,执行相应的数据库升级操作。例如,使用 db.execSQL() 方法执行 SQL 语句来修改表结构或创建新表。
9.3 数据库迁移的注意事项
  • 数据备份:在进行数据库升级前,建议备份重要的数据,以免升级过程中出现数据丢失的情况。
  • 逐步升级:如果数据库版本跨度较大,建议逐步升级,避免一次性进行大量的数据库结构修改。
  • 测试:在发布应用前,需要对数据库升级进行充分的测试,确保升级过程中数据的完整性和兼容性。

十、总结与展望

10.1 总结

通过对 Android GreenDao 数据库操作模块的深入分析,我们详细了解了其数据库操作的原理和实现方式。从数据库的初始化、数据的增删改查操作到事务管理和数据库版本升级,GreenDao 提供了一套简洁而高效的解决方案。

  • 初始化过程:通过 DaoMasterDaoSession 来管理数据库的创建和会话,方便开发者进行数据库操作。
  • 增删改查操作:通过自动生成的 DAO 类,开发者可以使用简单的 Java 代码完成复杂的数据库操作,无需手动编写 SQL 语句。
  • 事务管理:GreenDao 提供了方便的事务管理功能,通过 DaoSessionrunInTx() 方法可以确保一组数据库操作的原子性。
  • 数据库版本升级:通过自定义 OpenHelper 类,重写 onUpgrade() 方法,可以实现数据库的版本升级和迁移。
10.2 展望

随着 Android 应用的不断发展,对数据库操作的性能和易用性要求也越来越高。GreenDao 作为一款优秀的 ORM 框架,未来可能会在以下几个方面进行改进和发展:

  • 性能优化:进一步优化数据库操作的性能,减少内存占用和操作时间,提高应用的响应速度。
  • 兼容性增强:支持更多的数据库类型和版本,提高框架的兼容性和适用性。
  • 功能扩展:提供更多的高级功能,如数据库加密、数据同步等,满足不同应用场景的需求。
  • 开发体验提升:简化开发流程,提供更多的代码生成模板和工具,降低开发者的学习成本和开发难度。

总之,GreenDao 为 Android 开发者提供了一个强大而便捷的数据库操作解决方案,随着技术的不断进步,相信它会在 Android 开发领域发挥更加重要的作用。