如何使用 Room 创建多个表?

How can I create several tables, using Room?

我有一些问题。在我的应用程序中,我使用名为“Templates”的 Sqlite 数据库,我想创建几个 table,例如 peopleTemplatesanimalsTemplates,它们由列 iddescriptionimage_link。还有特别的tablefavourites,我会把用户喜欢的模板都加进去。我将使用 RoomDatabase。这是我的 TemplatesClass:

@Entity(tableName = "my_templates")
public class MyTemplate implements Serializable {

    @PrimaryKey(autoGenerate = true)
    public int id;

    @ColumnInfo(name = "description")
    public String titleTemplate;

    @ColumnInfo(name = "image_link")
    public String imageLink;

    public String getTitleTemplate() {
        return titleTemplate;
    }

    public void setTitleTemplate(String titleTemplate) {
        this.titleTemplate = titleTemplate;
    }

    public String getImageLink() {
        return imageLink;
    }

    public void setDrawableID(String imageLink) {
        this.imageLink = imageLink;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }


    public MyTemplate(String titleTemplate, String imageLink){
        this.titleTemplate = titleTemplate;
        this.imageLink = imageLink;
    }

    public MyTemplate(){}
}

我的数据库类:


@Database(entities = {MyTemplate.class}, version = 1, exportSchema = false)
public abstract class RoomDB extends RoomDatabase {
   private static RoomDB database;

   private final static String DATABASE_NAME = "Templates";

   public synchronized static RoomDB getInstance(Context context){
       if(database == null){
           database = Room.databaseBuilder(context.getApplicationContext(),
                   RoomDB.class, DATABASE_NAME)
                   .allowMainThreadQueries()
                   .fallbackToDestructiveMigration()
                   .build();
       }
       return  database;
   }

   public abstract TemplateDao templateDao();
}

还有我的道:


@Dao
public interface TemplateDao {
    @Insert(onConflict = REPLACE)
    void insert(MyTemplate myTemplate);

    @Delete
    void delete(MyTemplate myTemplate);

    @Delete
    void reset(ArrayList<MyTemplate> myTemplates);

    @Query("UPDATE my_templates SET description = :sDescription WHERE id = :sID")
    void update(int sID, String sDescription);

    @Query("SELECT * FROM my_templates")
    List<MyTemplate> getAll();
}

  1. 使用 @Entity 创建额外的 table 实体。

  2. 创建适当的 Dao(根据您的选择全部合一或每个实体一个)

  3. 修改@Database (RoomDB class) entities = 以将其他实体 class 包含在实体数组中。如果使用多个 Dao,则添加用于检索 Dao 的抽象方法(即类似于 public abstract TemplateDao templateDao();)。

至少对于模板 table 来说,如果您添加一个表示类型(人、动物等)的列,那么单个 table 似乎就足以满足所有模板

示例 1 - 附加 tables

1. 创建新实体

@Entity(tableName = "people_templates")
public class PeopleTemplate implements Serializable {

    @PrimaryKey(autoGenerate = true)
    public long id; //<<<<<<<<< Should really be long not int

    @ColumnInfo(name = "description")
    public String titleTemplate;

    @ColumnInfo(name = "image_link")
    public String imageLink;

    public String getTitleTemplate() {
        return titleTemplate;
    }

    public void setTitleTemplate(String titleTemplate) {
        this.titleTemplate = titleTemplate;
    }

    public String getImageLink() {
        return imageLink;
    }

    public void setDrawableID(String imageLink) {
        this.imageLink = imageLink;
    }

    public long getId() { // long
        return id;
    }

    public void setId(long id) { // long
        this.id = id;
    }

    public PeopleTemplate(String titleTemplate, String imageLink){
        this.titleTemplate = titleTemplate;
        this.imageLink = imageLink;
    }

    public PeopleTemplate(){}
}
  • 请注意,id 列的 int 已更改为 long。理论上 id 可以很长,Room 在使用 @Insert 时期望 return 长。

2.修道

@Dao
public interface TemplateDao {
    @Insert(onConflict = REPLACE)
    void insert(MyTemplate myTemplate);
    @Insert(onConflict = REPLACE)
    long insert(PeopleTemplate peopleTemplate); // long will hold the id of the inserted row
    @Insert(onConflict = REPLACE)
    long[] insert(PeopleTemplate...peopleTemplates); // allows many (e.g. ArrayList to be added), returns long[] of id's inserted

    @Delete
    void delete(MyTemplate myTemplate);
    @Delete
    int delete(PeopleTemplate...peopleTemplate); // delete 1 or many, returns number deleted
    @Delete
    void reset(ArrayList<MyTemplate> myTemplates);

    @Query("UPDATE my_templates SET description = :sDescription WHERE id = :sID")
    void update(int sID, String sDescription);
    @Query("UPDATE people_templates SET description = :sDescription WHERE id = :sID")
    int update(long sID, String sDescription); // returns number of updates

    @Query("SELECT * FROM my_templates")
    List<MyTemplate> getAll();
    @Query("SELECT * FROM people_templates")
    List<PeopleTemplate> getAllPeopleTemplates();
}
  • 注意评论

3.合并到@Databaseclass

//@Database(entities = {MyTemplate.class}, version = 1, exportSchema = false) <<<<<<<<<< WAS THIS
@Database(entities = {MyTemplate.class,PeopleTemplate.class}, version = 1, exportSchema = false) //<<<<<<<<<< CHANGED
public abstract class RoomDB extends RoomDatabase {
    private static RoomDB database;

    private final static String DATABASE_NAME = "Templates";

    public synchronized static RoomDB getInstance(Context context){
        if(database == null){
            database = Room.databaseBuilder(context.getApplicationContext(),
                    RoomDB.class, DATABASE_NAME)
                    .allowMainThreadQueries()
                    .fallbackToDestructiveMigration()
                    .build();
        }
        return  database;
    }

    public abstract TemplateDao templateDao();
}

示例 2 - 单个 Table

这是一个适用于多种模板类型的单一模板的工作示例table

SingleTemplate实体

@Entity(tableName = "templates")
public class SingleTemplate implements Serializable {

    public static final int MY_TEMPLATE = 0;
    public static final int PEOPLE_TEMPLATE = 1;
    public static final int ANIMAL_TEMPLATE = 2;

    /* suggest not using AUTOGENERATE as it has overheads
        using Long and passing null has the same result without overheads
     */
    @PrimaryKey
    Long id;
    @ColumnInfo(name = "description")
    String title;
    @ColumnInfo(name = "image_link")
    String imageLink;
    @ColumnInfo(name = "template_type")
    int templateType;

    public SingleTemplate(){}

    // Use @Ignore to suppress warning "There are multiple good constructors and Room will pick the no-arg constructor...."
    @Ignore
    public SingleTemplate(String title, String imageLink, int templateType) {
        this.title = title;
        this.imageLink = imageLink;
        this.templateType = templateType;
    }

    /* Method to allow conversion of type to a user readable String */
    public static String getType(int templateType) {
        switch (templateType) {
            case MY_TEMPLATE:
                return "MY";
            case PEOPLE_TEMPLATE:
                return "PEOPLE";
            case ANIMAL_TEMPLATE:
                return "ANIMAL";
            default:
                return "UNKNOWN";
        }
    }
}

The Dao SingleTemplateDao

@Dao
interface SingleTemplateDao {

    @Insert(onConflict = REPLACE)
    long insert(SingleTemplate singleTemplate);
    @Insert(onConflict = REPLACE)
    long[] insert(SingleTemplate...singleTemplates);
    @Update
    int update(SingleTemplate...singleTemplate);
    @Query("SELECT * FROM templates WHERE template_type IN (:arrayOfTypes)")
    List<SingleTemplate> getTemplatesByTypes(int[] arrayOfTypes);
}

@Database class RoomDBSingleTemplate

@Database(entities = {SingleTemplate.class}, version = 1, exportSchema = false) //<<<<<<<<<< CHANGED
public abstract class RoomDBSingleTemplate extends RoomDatabase {
    private static RoomDBSingleTemplate database;

    private final static String DATABASE_NAME = "Templates";

    public synchronized static RoomDBSingleTemplate getInstance(Context context){
        if(database == null){
            database = Room.databaseBuilder(context.getApplicationContext(),
                    RoomDBSingleTemplate.class, DATABASE_NAME)
                    .allowMainThreadQueries()
                    .fallbackToDestructiveMigration()
                    .build();
        }
        return  database;
    }

    public abstract SingleTemplateDao getSingleTemplateDao();
}

Invoking/Demo MainActivity

中的代码
public class MainActivity extends AppCompatActivity {

    RoomDBSingleTemplate db;
    SingleTemplateDao dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        db = RoomDBSingleTemplate.getInstance(this);
        dao = db.getSingleTemplateDao();

        // Add one of each type individually
        long id = dao.insert(new SingleTemplate("Template1","Link1",SingleTemplate.MY_TEMPLATE));
        dao.insert(new SingleTemplate("Template2","Link2", ANIMAL_TEMPLATE));
        dao.insert(new SingleTemplate("Template3","Link3",SingleTemplate.PEOPLE_TEMPLATE));
        // Add an unknown type
        dao.insert(new SingleTemplate("Template4","LINK4",99999));

        // Add many templates type determined according to iteration
        int limit = 10;
        SingleTemplate[] stList = new SingleTemplate[limit];
        for(int i=0; i < limit; i++) {
            stList[i] = new SingleTemplate(
                    "Template" + (i+100),
                    "Link" + (i+100),
                    i % 3
            );
        }
        dao.insert(stList);

        // Get just Animal templates and update the Link by prefixing with ANIMAL
        List<SingleTemplate> animalTemplates =  dao.getTemplatesByTypes(new int[]{ANIMAL_TEMPLATE});
        stList = new SingleTemplate[animalTemplates.size()];
        int i=0;
        for (SingleTemplate s: animalTemplates) {
            s.imageLink = "ANIMAL" + s.imageLink;
            stList[i++] = s;
        }
        dao.update(stList);

        // Get all known types and output to the log
        List<SingleTemplate> allTemplates = dao.getTemplatesByTypes(new int[]{MY_TEMPLATE,ANIMAL_TEMPLATE,PEOPLE_TEMPLATE});
        for(SingleTemplate s: allTemplates) {
            Log.d("TEMPLATEINFO",
                    "Title is " + s.title +
                            " Link is " + s.imageLink +
                            " Type is " + SingleTemplate.getType(s.templateType) +
                            " ID is " + s.id
            );
        }
    }
}

结果 - 在日志中输出。

2021-06-09 07:44:18.163 D/TEMPLATEINFO: Title is Template1 Link is Link1 Type is MY ID is 1
2021-06-09 07:44:18.163 D/TEMPLATEINFO: Title is Template2 Link is ANIMALLink2 Type is ANIMAL ID is 2
2021-06-09 07:44:18.163 D/TEMPLATEINFO: Title is Template3 Link is Link3 Type is PEOPLE ID is 3
2021-06-09 07:44:18.163 D/TEMPLATEINFO: Title is Template100 Link is Link100 Type is MY ID is 5
2021-06-09 07:44:18.163 D/TEMPLATEINFO: Title is Template101 Link is Link101 Type is PEOPLE ID is 6
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template102 Link is ANIMALLink102 Type is ANIMAL ID is 7
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template103 Link is Link103 Type is MY ID is 8
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template104 Link is Link104 Type is PEOPLE ID is 9
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template105 Link is ANIMALLink105 Type is ANIMAL ID is 10
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template106 Link is Link106 Type is MY ID is 11
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template107 Link is Link107 Type is PEOPLE ID is 12
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template108 Link is ANIMALLink108 Type is ANIMAL ID is 13
2021-06-09 07:44:18.164 D/TEMPLATEINFO: Title is Template109 Link is Link109 Type is MY ID is 14

可见

  • 已添加 14 行(最后一个 ID 是 14)
  • 所有类型为 ANIMAL 的行都已更新 link
  • 输出中省略了 UNKNOWN 行(ID 为 4 的 Template4)

更进一步

你说:-

Also there will be special table favourites, I will add there all templates user likes. I am going to use RoomDatabase.

假设用户有多个用户(如果只有 1 个用户也无妨,但如果只有 1 个用户则不需要那么复杂)。

您可能有一个用户 table,例如 :-

@Entity(tableName = "user")
public class User {

    @PrimaryKey
    Long id;
    String userName;
    String userEmail;
    String userPassword;

    public User(){};

    @Ignore
    public User(String userName, String userEmail, String userPassword) {
        this.userName = userName;
        this.userEmail = userEmail;
        this.userPassword = userPassword;
    }
}

link 用户使用他们最喜欢的模板并允许用户使用其他用户使用的模板,则需要 many-many 关系。因此,收藏夹 table 可以是用户和他们喜欢的模板之间的映射。要映射,您需要的只是用户的唯一标识符以及模板的唯一标识符(例如,每个模板的 id 列)。因此,收藏夹 table 可能是:-

/*
    Mapping Table for many-many relationship between users and templates
 */
@Entity(
        tableName = "favourite",
        foreignKeys = {
                @ForeignKey(
                        entity = User.class,
                        parentColumns = {"id"},
                        childColumns = {"userId"},
                        onDelete = CASCADE,
                        onUpdate = CASCADE
                        ),
                @ForeignKey(
                        entity = SingleTemplate.class,
                        parentColumns = {"id"},
                        childColumns = {"templateId"},
                        onDelete = CASCADE,
                        onUpdate = CASCADE
                        )
        },
        /* composite Primary Key */
        primaryKeys = {"userId","templateId"},
        indices = {@Index("templateId")}
)
public class Favourites {

    long userId;
    long templateId;

    public Favourites(){}

    @Ignore
    public Favourites(long userId,long templateId) {
        this.userId = userId;
        this.templateId = templateId;
    }
}
  • 这种 table 有很多术语,例如关联 table、引用 table、映射 table ....
  • ForeignKey 定义定义了 managing/asserting 参照完整性的规则,它们不是必需的,但它们很有用
    • 它们可以防止添加孤儿 children。也就是说,如果尝试在相应 parent 中不存在列值的行中添加行,则会发生外键冲突(参见 Dao)。
    • onDelete = CASCADE 将级联删除 parent,以便删除 child 中引用 parent 的所有行。
    • onUpdate 类似,但它只会导致在 children 中更改引用的列,并且仅当引用的列发生更改时(这种情况很少发生)。
  • 有两个外键定义,一个用于 reference/relationship 到用户 table,另一个用于 refrence/relationship 到 SingleTemplates table。
  • templateId 列上的索引不是必需的,但如果省略 Room 会发出警告。

在此阶段可以添加用于插入和检索用户和收藏夹的 Dao。然而,检索收藏夹并不是用户友好的,它们只是没有什么意义的数字。例如

  • 1,13 确实意味着 ID 为 1 的用户对 ID 为 13 的模板具有 link ....

因此,您可能希望获取用户详细信息以及模板详细信息。 Room 使这很容易做到你创建一个 POJO class (不是实体),你 Embed 一个实体然后定义一个与另一个class的关系 (嵌入用户模板)。但是,由于映射 table 在您 之间(仅嵌入和关系满足 one-many 关系) 那么您需要一个 Association 定义了三个 tables.

之间的 Junction

这样一个 suitable POJO 可以是 UserWithFavourites:-

public class UserWithFavourites {

    @Embedded
    User user; /* One User */
    @Relation(entity = SingleTemplate.class, parentColumn = "id",entityColumn = "id",
    associateBy = @Junction(value = Favourites.class,parentColumn = "userId",entityColumn = "templateId"))
    List<SingleTemplate> singleTemplateList; /* A list of Templates for the User */
}

因此需要 Dao 来检索(查询)数据库。所以 SingleTemplateDao 现在可以是 :-

@Dao
interface SingleTemplateDao {

    @Insert(onConflict = REPLACE)
    long insert(SingleTemplate singleTemplate);
    @Insert(onConflict = REPLACE)
    long[] insert(SingleTemplate...singleTemplates);
    @Insert(onConflict = REPLACE)
    long[] insert(User...users);
    @Insert(onConflict = IGNORE)
    long[] insert(Favourites...favourites);
    @Update
    int update(SingleTemplate...singleTemplate);
    @Query("SELECT * FROM templates WHERE template_type IN (:arrayOfTypes)")
    List<SingleTemplate> getTemplatesByTypes(int[] arrayOfTypes);
    @Query("SELECT * FROM user")
    List<User> getAllUsers();
    @Query("SELECT * FROM user WHERE id=:userId")
    User getUserById(long userId);
    @Query("SELECT * FROM User")
    List<UserWithFavourites> getAllUsersWithFavourites();

}

示例 3

  • 请注意,上面的 insert/updating 和提取代码已移至方法 na编辑示例 2。已创建新方法 example3 来演示添加用户和收藏夹 table,因此 activity 代码现在为:-

    public class MainActivity 扩展 AppCompatActivity {

      RoomDBSingleTemplate db;
      SingleTemplateDao dao;
    
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          db = RoomDBSingleTemplate.getInstance(this);
          dao = db.getSingleTemplateDao();
          example2();
          example3();
      }
    
      private void example3() {
    
          /* Add some Users */
          dao.insert(new User("Fred","Fred@email.com","password"), /*ID will be 1 */
                  new User("Mary","Mary@email.com","password"), /* ID will be 2 */
                  new User("Sarah","Sarah@email.com","password") /* ID will be 3 */);
          /* Using the existing templates map/associate/relate users to templates */
          dao.insert(new Favourites(1,1),
                  new Favourites(1,3),
                  new Favourites(1,4) /* UNKNOWN TYPE */,
                  new Favourites(1,10),
                  new Favourites(2,5),
                  new Favourites(2,6),
                  new Favourites(2,7),
                  new Favourites(2,8),
                  new Favourites(3,9),
                  new Favourites(3,11),
                  new Favourites(3,12),
                  new Favourites(3,13),
                  new Favourites(3,14));
    
          /* extract all users with thier templates and then output the results to the log */
          List<UserWithFavourites> userWithFavourites = dao.getAllUsersWithFavourites();
          for (UserWithFavourites uwf: userWithFavourites) {
              Log.d("FAVOURITESINFO",
                      "User is " + uwf.user.userName +
                              " email is " + uwf.user.userEmail +
                              " password is " + uwf.user.userPassword +
                              " USERID = " + uwf.user.id +
                              " # of Templates = " + uwf.singleTemplateList.size()
              );
              for (SingleTemplate st: uwf.singleTemplateList) {
                  Log.d("FAVOURITESINFO","Template is " + st.title + " Link is " + st.imageLink + " Type is " + getType(st.templateType) + " ID is " + st.id);
              }
          }
      }
    
      private void example2() {
    
          // Add one of each type individually
          long id = dao.insert(new SingleTemplate("Template1","Link1",SingleTemplate.MY_TEMPLATE));
          dao.insert(new SingleTemplate("Template2","Link2", ANIMAL_TEMPLATE));
          dao.insert(new SingleTemplate("Template3","Link3",SingleTemplate.PEOPLE_TEMPLATE));
          // Add an unknown type
          dao.insert(new SingleTemplate("Template4","LINK4",99999));
    
          // Add many templates type determined according to iteration
          int limit = 10;
          SingleTemplate[] stList = new SingleTemplate[limit];
          for(int i=0; i < limit; i++) {
              stList[i] = new SingleTemplate(
                      "Template" + (i+100),
                      "Link" + (i+100),
                      i % 3
              );
          }
          dao.insert(stList);
    
          // Get just Animal templates and update the Link by prefixing with ANIMAL
          List<SingleTemplate> animalTemplates =  dao.getTemplatesByTypes(new int[]{ANIMAL_TEMPLATE});
          stList = new SingleTemplate[animalTemplates.size()];
          int i=0;
          for (SingleTemplate s: animalTemplates) {
              s.imageLink = "ANIMAL" + s.imageLink;
              stList[i++] = s;
          }
          dao.update(stList);
    
          // Get all known types and output to the log
          List<SingleTemplate> allTemplates = dao.getTemplatesByTypes(new int[]{MY_TEMPLATE,ANIMAL_TEMPLATE,PEOPLE_TEMPLATE});
          for(SingleTemplate s: allTemplates) {
              Log.d("TEMPLATEINFO",
                      "Title is " + s.title +
                              " Link is " + s.imageLink +
                              " Type is " + SingleTemplate.getType(s.templateType) +
                              " ID is " + s.id
              );
          }
      }
    

    }

示例3结果当运行(第一次)日志包括:-

2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template1 Link is Link1 Type is MY ID is 1
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template3 Link is Link3 Type is PEOPLE ID is 3
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template4 Link is LINK4 Type is UNKNOWN ID is 4
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template105 Link is ANIMALANIMALLink105 Type is ANIMAL ID is 10
2021-06-09 11:50:11.477 D/FAVOURITESINFO: User is Mary email is Mary@email.com password is password USERID = 2 # of Templates = 4
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template100 Link is Link100 Type is MY ID is 5
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template101 Link is Link101 Type is PEOPLE ID is 6
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template102 Link is ANIMALANIMALLink102 Type is ANIMAL ID is 7
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template103 Link is Link103 Type is MY ID is 8
2021-06-09 11:50:11.477 D/FAVOURITESINFO: User is Sarah email is Sarah@email.com password is password USERID = 3 # of Templates = 5
2021-06-09 11:50:11.477 D/FAVOURITESINFO: Template is Template104 Link is Link104 Type is PEOPLE ID is 9
2021-06-09 11:50:11.478 D/FAVOURITESINFO: Template is Template106 Link is Link106 Type is MY ID is 11
2021-06-09 11:50:11.478 D/FAVOURITESINFO: Template is Template107 Link is Link107 Type is PEOPLE ID is 12
2021-06-09 11:50:11.478 D/FAVOURITESINFO: Template is Template108 Link is ANIMALANIMALLink108 Type is ANIMAL ID is 13
2021-06-09 11:50:11.478 D/FAVOURITESINFO: Template is Template109 Link is Link109 Type is MY ID is 14