Android: 如何在 Room 的多对多关系中插入数据 (Java)

Android: how to insert data in manyToMany relationship in Room (Java)

伙计们,我正在尝试创建一个具有多对多关系的数据库,我能够创建 2 table 个元素,但我无法填充连接 table .我不知道应该如何插入数据。

这是Card.class:

@Entity
public class Card {

@PrimaryKey(autoGenerate = true)
private Long idCard;

@ColumnInfo(name = "title")
private String title;

@ColumnInfo(name = "taboo_word_1")
private String tabooWord1;

@ColumnInfo(name = "taboo_word_2")
private String tabooWord2;

@ColumnInfo(name = "taboo_word_3")
private String tabooWord3;

@ColumnInfo(name = "taboo_word_4")
private String tabooWord4;

@ColumnInfo(name = "taboo_word_5")
private String tabooWord5;

public Long getIdCard() {
    return idCard;
}

public void setIdCard(Long idCard) {
    this.idCard = idCard;
}

public String getTitle() {
    return title;
}

public void setTitle(String title) {
    this.title = title;
}

public String getTabooWord1() {
    return tabooWord1;
}

public void setTabooWord1(String tabooWord1) {
    this.tabooWord1 = tabooWord1;
}

public String getTabooWord2() {
    return tabooWord2;
}

public void setTabooWord2(String tabooWord2) {
    this.tabooWord2 = tabooWord2;
}

public String getTabooWord3() {
    return tabooWord3;
}

public void setTabooWord3(String tabooWord3) {
    this.tabooWord3 = tabooWord3;
}

public String getTabooWord4() {
    return tabooWord4;
}

public void setTabooWord4(String tabooWord4) {
    this.tabooWord4 = tabooWord4;
}

public String getTabooWord5() {
    return tabooWord5;
}

public void setTabooWord5(String tabooWord5) {
    this.tabooWord5 = tabooWord5;
}


}

标签:

@Entity
public class Tag {

@PrimaryKey(autoGenerate = true)
private long idTag;

@ColumnInfo(name = "tag")
private String tag;

public Tag(String tag) {
    this.tag = tag;
}

public long getIdTag() {
    return idTag;
}

public void setIdTag(long idTag) {
    this.idTag = idTag;
}

public String getTag() {
    return tag;
}

public void setTag(String tag) {
    this.tag = tag;
}


@Override
public String toString() {
    return getTag();
}
}

这是DatabaseTaboom.class:

@Database(entities = {Card.class, Tag.class, CardTagCrossRef.class},
      version = 1)
public abstract class DatabaseTaboom extends RoomDatabase {

public static final String DATABASE_NAME = "db_taboom-1";

public abstract CardDAO cardDao();

public static DatabaseTaboom db;

// Singleton pattern
public static DatabaseTaboom getDatabase(Context applicationContext) {
    if (db == null) {
        db = Room.databaseBuilder(applicationContext, DatabaseTaboom.class, DATABASE_NAME)
                //.allowMainThreadQueries()
                .build();
    }
    return db;
}
}

这是CardDAO.class:

@Dao
public interface CardDAO {

@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertCard(Card card);

@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertTag(Tag tag);

@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertCardWithTags(CardTagCrossRef cardTagCrossRef);

// If called on an item not present in the DB it won't do anything
@Update
public void updateCard(Card card);

@Delete
public void deleteCard(Card card);

// With a query method you can also perform complex inserts/updates/deletes
// Transaction needed for relational classes
@Transaction
@Query("SELECT * FROM Card")
LiveData<List<CardWithTags>> getAllCards();
}

这是CardTagCrossRef.class:

@Entity(primaryKeys = {"idCard", "idTag"})
public class CardTagCrossRef {

public long idCard;
public long idTag;
}

CardWithTags:

public class CardWithTags {

@Embedded private Card card;
@Relation(
        parentColumn = "idCard",
        entityColumn = "idTag",
        associateBy = @Junction(CardTagCrossRef.class)
)

private List<Tag> tagList;

public CardWithTags() {

}

public CardWithTags(Card card, List<Tag> tagList) {
    this.card = card;
    this.tagList = tagList;
}

public Card getCard() {
    return card;
}

public void setCard(Card card) {
    this.card = card;
}

public List<Tag> getTagList() {
    return tagList;
}

public void setTagList(List<Tag> tagList) {
    this.tagList = tagList;
}

@Override
public String toString() {

    String s = getCard().toString();
    s += ", TAG[";
    for (Tag t: getTagList()) {
        s += t + "";
    }
    s+="]";

    return s;
}
}

这是我写的插卡方法:

public void insertCard(CardWithTags card) {

    Log.d(TAG, ">>insertCard(): " + card);
    executor.execute(() -> {

        cardDAO.insertCard(card.getCard());
        for (Tag t: card.getTagList()) {
            cardDAO.insertTag(t);
            CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
            cardTagCrossRef.idCard = card.getCard().getIdCard();
            cardTagCrossRef.idTag = t.getIdTag();
            Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
            cardDAO.insertCardWithTags(cardTagCrossRef);
        }

        // Check if tags already exists
        cardListIsUpdatedWithDb = false;
    });

}

首先,您应该修改 Dao,使它们 return 插入行的 ID 使您能够确定插入行的实际 ID。所以 :-

@Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertCard(Card card);

@Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertTag(Tag tag);

@Insert(onConflict = OnConflictStrategy.REPLACE)
public long insertCardWithTags(CardTagCrossRef cardTagCrossRef);

这允许您在插入卡片或标签时检索相应的 ID(请注意,对于 CardTagCrossRef 插入,这将是 rowid,通常是隐藏的行)。

因此您可以使用 long cardId = cardDAO.insertCard(card.getCard()); 而无需尝试使用 cardTagCrossRef.idCard = card.getCard().getIdCard(); 卡没有插入卡的 ID(您面临的问题的一部分) .

标签也是如此。

所以你可以使用 :-

    long cardId = cardDAO.insertCard(card.getCard());
    for (Tag t: card.getTagList()) {
        long tagId = cardDAO.insertTag(t);
        CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
        cardTagCrossRef.idCard = cardId;
        cardTagCrossRef.idTag = tagId;
        Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
        cardDAO.insertCardWithTags(cardTagCrossRef);
    }

但是,通过一些更改,我相信这可以使事情变得更加灵活,并且有一个插入可以有效地在 Dao 中执行您想要的操作。

SO 或许可以考虑以下内容,最终在工作中达到高潮 DEMO

卡片

@Entity
public class Card {

    @PrimaryKey/*(autoGenerate = true) SUGGESTED suppression of autogenerate as will still autogenerate but more efficiently */
    private Long idCard;

    @ColumnInfo(name = "title")
    private String title;

    @ColumnInfo(name = "taboo_word_1")
    private String tabooWord1;

    @ColumnInfo(name = "taboo_word_2")
    private String tabooWord2;

    @ColumnInfo(name = "taboo_word_3")
    private String tabooWord3;

    @ColumnInfo(name = "taboo_word_4")
    private String tabooWord4;

    @ColumnInfo(name = "taboo_word_5")
    private String tabooWord5;

    /* Constructors added */
    public Card(){}

    @Ignore
    public Card(Long idCard,String title, String tabooWord1, String tabooWord2, String tabooWord3, String tabooWord4, String tabooWord5) {
        this.idCard = idCard;
        this.title = title;
        this.tabooWord1 = tabooWord1;
        this.tabooWord2 = tabooWord2;
        this.tabooWord3 = tabooWord3;
        this.tabooWord4 = tabooWord4;
        this.tabooWord5 = tabooWord5;
    }

    public Long getIdCard() {
        return idCard;
    }

    public void setIdCard(Long idCard) {
        this.idCard = idCard;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTabooWord1() {
        return tabooWord1;
    }

    public void setTabooWord1(String tabooWord1) {
        this.tabooWord1 = tabooWord1;
    }

    public String getTabooWord2() {
        return tabooWord2;
    }

    public void setTabooWord2(String tabooWord2) {
        this.tabooWord2 = tabooWord2;
    }

    public String getTabooWord3() {
        return tabooWord3;
    }

    public void setTabooWord3(String tabooWord3) {
        this.tabooWord3 = tabooWord3;
    }

    public String getTabooWord4() {
        return tabooWord4;
    }

    public void setTabooWord4(String tabooWord4) {
        this.tabooWord4 = tabooWord4;
    }

    public String getTabooWord5() {
        return tabooWord5;
    }

    public void setTabooWord5(String tabooWord5) {
        this.tabooWord5 = tabooWord5;
    }
}
  • 2 更改了一个更灵活的构造函数,而不是使用 autogenerate = true(但这会自动生成 id,但没有 SQLite AUTOINCREMENT 的开销,这是 autogenerate = true 添加的内容)。

标签(类似的变化)

@Entity
public class Tag {

    @PrimaryKey/*(autoGenerate = true) SUGGESTED suppression of autogenerate*/
    private Long idTag;

    @ColumnInfo(name = "tag")
    private String tag;

    public Tag(){}

    @Ignore
    public Tag(Long idTag, String tag) {
        this.idTag = idTag;
        this.tag = tag;
    }

    @Ignore
    public Tag(String tag) {
        this.tag = tag;
    }

    public Long getIdTag() {
        return idTag;
    }

    public void setIdTag(Long idTag) {
        this.idTag = idTag;
    }

    public String getTag() {
        return tag;
    }

    public void setTag(String tag) {
        this.tag = tag;
    }

    @Override
    public String toString() {
        return getTag();
    }
}

CardTagCrossRef(为 enforce/manage 引用完整性添加了 ForeignKey 约束)

@Entity(
        primaryKeys = {"idCard", "idTag"}
        /* SUGGESTED */
        , foreignKeys = {
                @ForeignKey(
                        entity = Card.class,
                        parentColumns = "idCard",
                        childColumns = "idCard",
                        /* SUGGESTED with ForeignKey */
                        onDelete = CASCADE,
                        onUpdate = CASCADE
                ),
                @ForeignKey(
                        entity = Tag.class,
                        parentColumns = "idTag",
                        childColumns = "idTag",
                        /* SUGGESTED with ForeignKey */
                        onDelete = CASCADE,
                        onUpdate = CASCADE
                )
        }
        )
public class CardTagCrossRef {

    public long idCard;
    @ColumnInfo(index = true) /* SUGGESTED */
    public long idTag;

    public CardTagCrossRef(){}
    @Ignore
    public CardTagCrossRef(long idCard, long idTag) {
        this.idCard = idCard;
        this.idTag = idTag;
    }
}

CardWithTags

除了 CardWithTags(Card card, List<Tag> tagList) 构造函数上的 @Ignore 注释外,其他相同,以抑制有关多个良好构造函数的警告。

....
@Ignore /*<<<<< SUGGESTED */
public CardWithTags(Card card, List<Tag> tagList) {
    this.card = card;
    this.tagList = tagList;
}
....

CardDAO(新插入 + return 值)

@Dao
abstract class CardDAO {
/* public interface CardDAO {  CHANGED TO abstract class to allow functions with bodies */

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract long insertCard(Card card); /* Returns long (inserted row id) */

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract long insertTag(Tag tag); /* Returns long (inserted row id) */

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract long insertCardWithTags(CardTagCrossRef cardTagCrossRef); /* Returns long (inserted row id) */

    /* NEW INSERT */
    @Query("")
    @Transaction
    long[] insert(Card card, List<Tag> tags) {
        long[] rv = new long[tags.size() + 1];
        int ix = 0;
        rv[ix++] = insertCard(card);
        if (rv[ix-1] > -1) {
            for (Tag t : tags) {
                rv[ix++] = insertTag(t);
                if (rv[ix-1] > -1) {
                    insertCardWithTags(new CardTagCrossRef(rv[0],rv[ix-1]));
                }
            }

        }
        return rv;
    }

    // If called on an item not present in the DB it won't do anything
    @Update
    abstract int updateCard(Card card); /* returns number of updated rows */

    @Delete
    abstract int deleteCard(Card card); /* returns number of deleted rows */



    // With a query method you can also perform complex inserts/updates/deletes
// Transaction needed for relational classes
    @Transaction
    @Query("SELECT * FROM Card")
    /* abstract LiveData<List<CardWithTags>> getAllCards(); COMMENTED OUT to allow demo to run on main thread */
    abstract List<CardWithTags> getAllCards(); /* same but not with LiveData */
}

DatabaseTaboom(允许主线程+exportSchema = false抑制警告)

@Database(entities = {Card.class, Tag.class, CardTagCrossRef.class},
        version = 1/* SUGGESTED */ , exportSchema = false)
public abstract class DatabaseTaboom extends RoomDatabase {


    public static final String DATABASE_NAME = "db_taboom-1";
    abstract CardDAO cardDao();
    public static DatabaseTaboom db;

    // Singleton pattern
    public static DatabaseTaboom getDatabase(Context applicationContext) {
        if (db == null) {
            db = Room.databaseBuilder(applicationContext, DatabaseTaboom.class, DATABASE_NAME)
                    .allowMainThreadQueries() /* uncommented for testing */
                    .build();
        }
        return db;
    }
}

最后是 DEMO MainActivity,其中包含一些插入卡片、标签和 CardTagCrossRef 的示例,然后提取它们并将结果输出到日志。

public class MainActivity extends AppCompatActivity {

    DatabaseTaboom db;
    CardDAO dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        db = DatabaseTaboom.getDatabase(this);
        dao = db.cardDao();

        /* Simple but long winded */
        long c1id = dao.insertCard(new Card(null,"Card1","tw1","tw2","tw3","tw4","tw5"));
        long t1id = dao.insertTag(new Tag(null,"TAG1"));
        CardTagCrossRef ctcr1 = new CardTagCrossRef();
        ctcr1.idCard = c1id;
        ctcr1.idTag = t1id ;
        dao.insertCardWithTags(ctcr1);
        /* Using additional constructor for CardTagCrossRef */
        long t2id = dao.insertTag(new Tag("TAG2"));
        dao.insertCardWithTags(new CardTagCrossRef(c1id,t2id));
        /* More dynamic  BUT don't know the actual inserted id's of the Card and Tag */
        dao.insertCardWithTags(
                new CardTagCrossRef(dao.insertCard(new Card(100l,"Card2","c2tw1","c2tw2","c2tw3","c2tw4","c2tw5")),dao.insertTag(new Tag(null,"TAG3"))));

        CardWithTags cwt = new CardWithTags(
                new Card(null,"CARD3","c3tw1","c3tw2","c3tw3","c3tw4","c3tw5"),
                Arrays.asList(
                        new Tag(null,"TAG4"), new Tag("TAG5"), new Tag("TAG6")
                )
        );

        /* Amended insert function */
        insertCard(cwt,dao);

        /* Using new insert funciotn */
        dao.insert(
                new Card(1000l,"CARD4","c4tw1","c4tw2","c4tw3","c4tw4","c4tw5"),
                Arrays.asList(
                        new Tag(null,"TAG7"), new Tag(500l,"TAG8"),new Tag(null,"TAG9")
                )
        );
         /* Extract the results and output to the log */
        for(CardWithTags cwtlist: dao.getAllCards()) {
            Log.d("CWTINFO","Card is " + cwtlist.getCard().getTitle() + " TabooWord1 is " + cwtlist.getCard().getTabooWord1() + " it has " + cwtlist.getTagList().size() + " tags. They are:-");
            for(Tag t: cwtlist.getTagList()) {
                Log.d("CWTINFO_TAG","\tTAG is " + t.getTag());
            }
        }
    }
    public void insertCard(CardWithTags card, CardDAO cardDAO) {
        final String TAG = "INSERTCARDINFO";

        Log.d(TAG, ">>insertCard(): " + card);
        /*
        executor.execute(() -> {
         */

            long currentCardId = cardDAO.insertCard(card.getCard());
            for (Tag t: card.getTagList()) {
                long currentTagId = cardDAO.insertTag(t);
                CardTagCrossRef cardTagCrossRef = new CardTagCrossRef();
                cardDAO.insertCardWithTags(new CardTagCrossRef(currentCardId,currentTagId));

                /*
                cardTagCrossRef.idCard = card.getCard().getIdCard();
                cardTagCrossRef.idTag = t.getIdTag();
                 */
                /*
                 OR with new Contsructor

                CardTagCrossRef ctcr = new CardTagCrossRef(currentCardId,currentTagId);
                */
                /* AND THEN cardDAO.insertCardWithTags(cardTagCrossRef); */
                Log.d(TAG, "CardCrossRef:" + cardTagCrossRef.idCard + cardTagCrossRef.idTag);
            }

            /*
            // Check if tags already exists
            cardListIsUpdatedWithDb = false;
             */
        /*})*/;
    }
}

当运行(新安装后只设计为运行一次)日志包括:-

2022-02-04 13:29:10.569D/INSERTCARDINFO: >>insertCard(): a.a.so70979022javaroom.Card@d751e5e, TAG[TAG4TAG5TAG6]
2022-02-04 13:29:10.573D/INSERTCARDINFO: CardCrossRef:00
2022-02-04 13:29:10.578I/chatty: uid=10194(a.a.so70979022javaroom) identical 1 line
2022-02-04 13:29:10.581D/INSERTCARDINFO: CardCrossRef:00
2022-02-04 13:29:10.600D/CWTINFO: Card is Card1 TabooWord1 is tw1 it has 2 tags. They are:-
2022-02-04 13:29:10.600D/CWTINFO_TAG:   TAG is TAG1
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG2
2022-02-04 13:29:10.601D/CWTINFO: Card is Card2 TabooWord1 is c2tw1 it has 1 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG3
2022-02-04 13:29:10.601D/CWTINFO: Card is CARD3 TabooWord1 is c3tw1 it has 3 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG4
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG5
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG6
2022-02-04 13:29:10.601D/CWTINFO: Card is CARD4 TabooWord1 is c4tw1 it has 3 tags. They are:-
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG7
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG8
2022-02-04 13:29:10.601D/CWTINFO_TAG:   TAG is TAG9

然后通过 App Inspection :-

和:-

和:-