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

如何使用Room的@Relation注解结合复合键构建实体模型?

处理Room中复合主键/外键的@Relation关联问题

确实,早期版本的Room的@Relation注解看起来只支持单键关联,但其实从Room 2.2.0版本开始,官方已经支持通过字符串数组来指定复合主键/外键的关联条件,正好匹配你想要的写法!下面分两种情况给你具体解决方案:

方案1:使用Room 2.2.0+版本(推荐)

如果你的项目已经升级到Room 2.2.0或更高版本,直接就能用你预想的方式构建模型类,只需要注意注解参数的语法(Java/Kotlin略有不同):

第一步:定义实体类

先确保你的复合主键实体和带复合外键的子实体定义正确:

父实体(复合主键)

@Entity(primaryKeys = {"id_part_1", "id_part_2"})
public class ParentEntity {
    public String id_part_1;
    public String id_part_2;
    // 其他业务字段...
}

子实体(复合外键)

@Entity(
    foreignKeys = {
        @ForeignKey(
            entity = ParentEntity.class,
            parentColumns = {"id_part_1", "id_part_2"},
            childColumns = {"compose_key_part_1", "compose_key_part_2"},
            onDelete = ForeignKey.CASCADE
        )
    }
)
public class ItemEntity {
    @PrimaryKey(autoGenerate = true)
    public long itemId;
    public String compose_key_part_1;
    public String compose_key_part_2;
    // 其他业务字段...
}

第二步:构建模型类

在包含关联列表的模型类中,直接使用@Relation的数组参数指定复合键关联:

public class ParentModel {
    @Embedded
    public ParentEntity parent;

    @Relation(
        parentColumns = {"id_part_1", "id_part_2"}, // 父实体的复合主键列
        entityColumns = {"compose_key_part_1", "compose_key_part_2"}, // 子实体的复合外键列
        entity = ItemEntity.class
    )
    public List<ItemModel> actions; // 这里如果不需要额外转换,直接用List<ItemEntity>也可以
}

第三步:DAO层查询

只需要在DAO中写一个带@Transaction注解的查询方法,Room会自动帮你关联查询并组装模型:

@Dao
public interface ParentDao {
    @Transaction
    @Query("SELECT * FROM ParentEntity WHERE id_part_1 = :id1 AND id_part_2 = :id2")
    LiveData<ParentModel> getParentWithItems(String id1, String id2);
}

方案2:旧版本Room(低于2.2.0)的兼容方案

如果因为项目限制无法升级Room版本,你可以通过自定义JOIN查询+手动映射或者**@Transaction分两次查询**来实现:

方式A:自定义JOIN查询

直接在DAO中写JOIN语句,然后通过Room的POJO映射来组装数据:

@Dao
public interface ParentDao {
    @Query("""
        SELECT p.*, i.* 
        FROM ParentEntity p
        LEFT JOIN ItemEntity i 
        ON p.id_part_1 = i.compose_key_part_1 AND p.id_part_2 = i.compose_key_part_2
        WHERE p.id_part_1 = :id1 AND p.id_part_2 = :id2
    """)
    List<ParentWithItemRaw> getParentWithItemsRaw(String id1, String id2);
}

然后定义一个中间POJO接收原始查询结果,再手动转换成你需要的ParentModel(可以写一个工具类来分组子实体):

public class ParentWithItemRaw {
    @Embedded
    public ParentEntity parent;
    @Embedded(prefix = "item_")
    public ItemEntity item;
}

方式B:@Transaction分两次查询

在DAO中用@Transaction注解包裹两个查询,先查父实体,再根据复合主键查对应的子实体:

@Dao
public interface ParentDao {
    @Transaction
    default ParentModel getParentWithItems(String id1, String id2) {
        ParentEntity parent = getParent(id1, id2);
        List<ItemEntity> items = getItemsByParent(id1, id2);
        ParentModel model = new ParentModel();
        model.parent = parent;
        model.actions = convertToItemModel(items); // 转换为ItemModel的逻辑
        return model;
    }

    @Query("SELECT * FROM ParentEntity WHERE id_part_1 = :id1 AND id_part_2 = :id2")
    ParentEntity getParent(String id1, String id2);

    @Query("SELECT * FROM ItemEntity WHERE compose_key_part_1 = :id1 AND compose_key_part_2 = :id2")
    List<ItemEntity> getItemsByParent(String id1, String id2);
}

注意事项

  • 不管用哪种方案,都要确保复合主键和外键的列名对应关系正确,避免关联错误。
  • 使用@Transaction注解可以保证查询的原子性,避免数据不一致的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 10:00:22