简体   繁体   中英

Android: Many to Many relation in Room library(sqllite)

I have a media player app which has "Song" as entity and "Playlist" as entity. each song can be in multiple playlists and each playlist holds many songs. I'm using the Room library as my sqlite database manager. any suggestions how to implement this Many to Many relation ? I haven't found in the docs an example of how to implement Many to Many relation on Room.

Implementing a many-to-many relation in Room is no different than is implementing a many-to-many relation with any SQL database: have a join table. In the case of Room, that join table is created via a join @Entity .

This sample project from this book illustrates the technique.

I have a Category class, that happens to implement a tree structure (1:N relation between parent and child categories):

@Entity(
  tableName="categories",
  foreignKeys=@ForeignKey(
    entity=Category.class,
    parentColumns="id",
    childColumns="parentId",
    onDelete=CASCADE),
  indices=@Index(value="parentId"))
public class Category {
  @PrimaryKey
  @NonNull
  public final String id;
  public final String title;
  public final String parentId;

  @Ignore
  public Category(String title) {
    this(title, null);
  }

  @Ignore
  public Category(String title, String parentId) {
    this(UUID.randomUUID().toString(), title, parentId);
  }

  public Category(String id, String title, String parentId) {
    this.id=id;
    this.title=title;
    this.parentId=parentId;
  }
}

And, I have a Customer , with a nested CategoryJoin to represent a M:N relation between customers and categories:

@Entity(indices={@Index(value="postalCode", unique=true)})
class Customer {
  @PrimaryKey
  @NonNull
  public final String id;

  public final String postalCode;
  public final String displayName;
  public final Date creationDate;

  @Embedded
  public final LocationColumns officeLocation;

  public final Set<String> tags;

  @Ignore
  Customer(String postalCode, String displayName, LocationColumns officeLocation,
           Set<String> tags) {
    this(UUID.randomUUID().toString(), postalCode, displayName, new Date(),
      officeLocation, tags);
  }

  Customer(String id, String postalCode, String displayName, Date creationDate,
           LocationColumns officeLocation, Set<String> tags) {
    this.id=id;
    this.postalCode=postalCode;
    this.displayName=displayName;
    this.creationDate=creationDate;
    this.officeLocation=officeLocation;
    this.tags=tags;
  }

  @Entity(
    tableName="customer_category_join",
    primaryKeys={"categoryId", "customerId"},
    foreignKeys={
      @ForeignKey(
        entity=Category.class,
        parentColumns="id",
        childColumns="categoryId",
        onDelete=CASCADE),
      @ForeignKey(
        entity=Customer.class,
        parentColumns="id",
        childColumns="customerId",
        onDelete=CASCADE)},
    indices={
      @Index(value="categoryId"),
      @Index(value="customerId")
    }
  )
  public static class CategoryJoin {
    @NonNull public final String categoryId;
    @NonNull public final String customerId;

    public CategoryJoin(String categoryId, String customerId) {
      this.categoryId=categoryId;
      this.customerId=customerId;
    }
  }
}

Your @Dao then has methods to retrieve objects based on the relation, such as:

  @Query("SELECT categories.* FROM categories\n"+
    "INNER JOIN customer_category_join ON categories.id=customer_category_join.categoryId\n"+
    "WHERE customer_category_join.customerId=:customerId")
  List<Category> categoriesForCustomer(String customerId);

  @Query("SELECT Customer.* FROM Customer\n"+
    "INNER JOIN customer_category_join ON Customer.id=customer_category_join.customerId\n"+
    "WHERE customer_category_join.categoryId=:categoryId")
  List<Customer> customersForCategory(String categoryId);

You need to create 3 tables:

  1. Song (song_id,song_name,artist)

  2. Playlist (playlist_id,playlist_name)

  3. A junction table (playlist_id ,song_id )

Reference Links:

Many-to-Many relation exmples

You'll need to create a new "junction" class which represents a "belongs to" relation. In Room, it'd be something like this:

@Entity(tableName = "playlist_entries",
    primaryKeys = { "songId", "playlistId" },
    foreignKeys = {
            @ForeignKey(entity = Song.class,
                        parentColumns = "id",
                        childColumns = "songId"),
            @ForeignKey(entity = Playlist.class,
                        parentColumns = "id",
                        childColumns = "playlistId")
            })
public class PlaylistEntry {
    public final int songId;
    public final int playlistId;

    public PlaylistEntry(final int songId, final int playlistId {
        this.songId = songId;
        this.playlistId = playlistId;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM