【译】使用 Room 定义对象间的关系

译自Define relationships between objects | Android Developers

此前一直困惑于 Room 如何实现多对多关系的问题,奈何当时全网搜不到相关的内容,于时迁移到 Room 的计划便被搁置了。直到我最近从 Google Codelab 的 Android Room with a View 中的链接跳转到了官方文档,看到了官方对于多对多关系的示例代码,我又去搜索了一下这个问题,中文站内已经有几篇相关的问答了,但是掘金似乎还没有相关的文章提到(可能是我搜索的方式不对吧),于是我决定将这段文档翻译分享出来。以下是正文。


由于 SQLite 数据库是一种关系型数据库,你可以指定对象间的关系。然而尽管绝大部分的对象关系映射库允许实体对象相互引用,但是 Room 明确禁止这种做法。想要了解这个决定的技术性原因,参见理解为什么 Room 不允许对象引用

定义一对多关系

尽管你不能使用直接关系,但是 Room 允许你定义外键关联实体类。

例如有一个实体类 User ,另一个实体类 Book ,你可以通过 @ForeignKey 声明,定义 BookUser 的关系。

@Entity
public class User {
    @PrimaryKey
    public int id;

    public String firstName;
    public String lastName;
}

@Entity(foreignKeys = @ForeignKey(entity = User.class,
                                  parentColumns = "id",
                                  childColumns = "user_id"))
public class Book {
    @PrimaryKey public int bookId;

    public String title;

    @ColumnInfo(name = "user_id") public int userId;
}
复制代码

由于 Book 的 0 或多个实例可以通过外键 user_id 关联到 User 的多个实例上,因此上面的代码模拟了 BookUser 之间的一对多关系。

外键非常的强大,甚至允许你指定引用的实体更新后执行的操作。例如,通过在 @ForeignKey 的声明中加入 onDelete = CASCADE ,你可以设置 SQLite 在一个 User 的实例被删除时,删除该实例关联的所有书。

注意: SQLite 通过一系列的 删除替换 操作处理 @Insert(onConflict = REPLACE) 而非单一的 更新 操作。这种替换冲突值得方法可能会影响你的外键约束。多于更多细节,参见 SQLite 文档ON_CONFLICT 部分。

创建嵌套对象

有时,你希望在数据库逻辑中将实体或数据对象表达为一个整体,即使该对象包含多个字段。在这些情况下,你可以使用 @Embedded 注解来表示表中要分解到子成员变量的对象。 然后,你可以像查询其他单个列一样查询嵌入的成员变量。

例如,您的User类可以包含Address类型的字段,该字段表示名为street,city,state和postCode的字段的组合。 要将组合列分别存储在表中,在User类中使用@Embedded注解修饰的Address字段,如以下代码段所示:

public class Address {
    public String street;
    public String state;
    public String city;

    @ColumnInfo(name = "post_code") public int postCode;
}

@Entity
public class User {
    @PrimaryKey public int id;

    public String firstName;

    @Embedded public Address address;
}
复制代码

这张表表示包含具有以下名称的列: idfirstNamestreetstatecitypost_codeUser 对象。

注意: 嵌套字段也可以包含其他嵌套字段。

如果实体具有多个相同类型的嵌入字段,则可以通过设置 prefix 属性使每个列保持唯一。然后,Room将提供的值添加到嵌入对象中每个列名称的开头。

定义多对多关系

PS:终于到重点了,嘿嘿?

你经常要在关系数据库中建模另一种关系:两个实体之间的多对多关系,其中每个实体可以关联到另一个实体的0或多个实例。例如,考虑一个音乐串流应用程序,用户可以将他们喜欢的歌曲组成播放列表。每个播放列表可以具有任意数量的歌曲,并且每首歌曲可以包括在任意数量的播放列表中。

要模拟此关系,你需要创建三个对象:

  1. 播放列表的实体类。
  2. 歌曲的实体类。
  3. 一个中间类,用于保存每个播放列表中有哪些歌曲的信息。

你可以将实体类定义为独立部分:

@Entity
public class Playlist {
    @PrimaryKey public int id;

    public String name;
    public String description;
}

@Entity
public class Song {
    @PrimaryKey public int id;

    public String songName;
    public String artistName;
}
复制代码

然后,将中间类定义为包含对 SongPlaylist 的外键引用的实体:

@Entity(tableName = "playlist_song_join",
        primaryKeys = { "playlistId", "songId" },
        foreignKeys = {
                @ForeignKey(entity = Playlist.class,
                            parentColumns = "id",
                            childColumns = "playlistId"),
                @ForeignKey(entity = Song.class,
                            parentColumns = "id",
                            childColumns = "songId")
                })
public class PlaylistSongJoin {
    public int playlistId;
    public int songId;
}
复制代码

这会生成一个多对多关系模型,允许你使用 DAO 按播放列表按歌曲和歌曲查询播放列表:

@Dao
public interface PlaylistSongJoinDao {
    @Insert
    void insert(PlaylistSongJoin playlistSongJoin);

    @Query("SELECT * FROM playlist " +
           "INNER JOIN playlist_song_join " +
           "ON playlist.id=playlist_song_join.playlistId " +
           "WHERE playlist_song_join.songId=:songId")
    List<Playlist> getPlaylistsForSong(final int songId);

    @Query("SELECT * FROM song " +
           "INNER JOIN playlist_song_join " +
           "ON song.id=playlist_song_join.songId " +
           "WHERE playlist_song_join.playlistId=:playlistId")
    List<Song> getSongsForPlaylist(final int playlistId);
}
复制代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值