You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Android Room中实现可复用的邻接列表数据库?

Android Room 通用邻接列表实现方案

核心思路

针对项目、任务的嵌套结构需求,通过抽象基类+子类继承实现通用邻接列表实体,DAO则通过抽象DAO类+子类重写/泛型动态SQL适配不同表名,既复用通用逻辑,又满足多表独立存储的要求。

实体类实现

先定义抽象基类封装通用字段,子类继承后指定各自表名与外键约束:

// 抽象基类:定义邻接列表通用字段,不添加@Entity注解
public abstract class BaseAdjacencyList {
    @PrimaryKey(autoGenerate = true)
    public long id;
    // 根节点的parentId可设为-1或0,表示无父节点
    public long parentId;
}

// 项目邻接列表实体:指定专属表名与同表外键
@Entity(
    tableName = "project_adjacency",
    foreignKeys = @ForeignKey(
        entity = ProjectAdjacencyList.class,
        parentColumns = "id",
        childColumns = "parentId",
        onDelete = CASCADE,
        onUpdate = CASCADE
    )
)
public class ProjectAdjacencyList extends BaseAdjacencyList {}

// 任务邻接列表实体:同理定义专属表
@Entity(
    tableName = "task_adjacency",
    foreignKeys = @ForeignKey(
        entity = TaskAdjacencyList.class,
        parentColumns = "id",
        childColumns = "parentId",
        onDelete = CASCADE,
        onUpdate = CASCADE
    )
)
public class TaskAdjacencyList extends BaseAdjacencyList {}

DAO实现

方式1:抽象DAO基类+子类重写(推荐,Room兼容性更好)

通过抽象基类封装通用CRUD,子类继承后指定具体表名的查询语句:

// 抽象DAO基类:定义通用操作签名
@Dao
public abstract class BaseAdjacencyDao<T extends BaseAdjacencyList> {
    @Insert
    public abstract long insert(T item);

    @Update
    public abstract int update(T item);

    @Delete
    public abstract int delete(T item);

    // 子类需实现的表专属查询
    public abstract List<T> getAll();
    public abstract List<T> getChildren(long parentId);
}

// 项目邻接列表DAO:绑定project_adjacency表
@Dao
public abstract class ProjectAdjacencyDao extends BaseAdjacencyDao<ProjectAdjacencyList> {
    @Query("SELECT * FROM project_adjacency")
    @Override
    public abstract List<ProjectAdjacencyList> getAll();

    @Query("SELECT * FROM project_adjacency WHERE parentId = :parentId")
    @Override
    public abstract List<ProjectAdjacencyList> getChildren(long parentId);
}

// 任务邻接列表DAO:绑定task_adjacency表
@Dao
public abstract class TaskAdjacencyDao extends BaseAdjacencyDao<TaskAdjacencyList> {
    @Query("SELECT * FROM task_adjacency")
    @Override
    public abstract List<TaskAdjacencyList> getAll();

    @Query("SELECT * FROM task_adjacency WHERE parentId = :parentId")
    @Override
    public abstract List<TaskAdjacencyList> getChildren(long parentId);
}

方式2:泛型+@RawQuery(更灵活,适合复杂动态SQL)

通过反射获取子类表名,结合@RawQuery实现完全通用的查询逻辑:

@Dao
public abstract class BaseAdjacencyDao<T extends BaseAdjacencyList> {
    private final String tableName;

    // 构造时通过反射获取子类的表名
    public BaseAdjacencyDao(Class<T> entityClass) {
        Entity entityAnnotation = entityClass.getAnnotation(Entity.class);
        this.tableName = entityAnnotation.tableName();
    }

    @Insert
    public abstract long insert(T item);

    @Update
    public abstract int update(T item);

    @Delete
    public abstract int delete(T item);

    // 基础RawQuery方法,供上层封装
    @RawQuery
    protected abstract List<T> executeRawQuery(SupportSQLiteQuery query);

    // 通用查询所有节点
    public List<T> getAll() {
        String sql = "SELECT * FROM " + tableName;
        return executeRawQuery(new SimpleSQLiteQuery(sql));
    }

    // 通用查询指定父节点的子节点
    public List<T> getChildren(long parentId) {
        String sql = "SELECT * FROM " + tableName + " WHERE parentId = ?";
        return executeRawQuery(new SimpleSQLiteQuery(sql, new Object[]{parentId}));
    }
}

// 使用时实例化DAO
// ProjectAdjacencyDao projectDao = new BaseAdjacencyDao<>(ProjectAdjacencyList.class) {};

关键注意事项

  1. 外键约束的entity参数必须指向子类自身,因为邻接列表的父节点属于同一张表;
  2. 根节点的parentId建议用特殊值(如-1)标识,避免与正常节点ID冲突;
  3. Room支持实体类继承,父类的字段会自动映射到子类的表结构中,无需重复定义。

内容的提问来源于stack exchange,提问作者ndaniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 00:47:46