【问题标题】:Saving Multiple Arraylist in Room Database - Best way of doing it?在房间数据库中保存多个 Arraylist - 最好的方法是什么?
【发布时间】:2018-11-24 14:47:11
【问题描述】:

背景: 我正在尝试为 Cardgame 构建 Deckbuilder。不同的套牌是基于 我想在本地保护的一个 Arraylists。这意味着你可以创建你的套牌,而不是我希望你能够保护它,然后创建一个新套牌,能够再次保存它...... -> 这会产生多个我想要保护的 Arraylists同一个班级。

问题: 在知道我想要的不仅仅是一个 Arraylist 的情况下,将 Arraylist 存储在 Room 中的最佳方法是什么?

据我所知,我必须创建一个实体类,它基本上会为我创建一个(?)表,我必须在其中一个接一个地保存 Arraylist?

有没有更好的方法。

奖励: 我也希望有一个基本示例来说明如何做到这一点,因为我自己似乎很难做到这一点。

非常感谢!

编辑:

代码示例

所以我使用您的代码示例中的基线实现的内容如下:

  1. 我创建了 SaveDeck 类,它应该能够使用给定的甲板名称保存甲板: :-

    @实体 公共类 SaveDeck 实现 Serializable { @PrimaryKey(autoGenerate = true) 私有 int _id;

    public SaveDeck(int _id, String deckName, int cardImage, int typeImage, Integer cardCost, String cardName, Integer cardNumber) {
        this._id = _id;
        DeckName = deckName;
        CardImage = cardImage;
        TypeImage = typeImage;
        CardCost = cardCost;
        CardName = cardName;
        CardNumber = cardNumber;
    }
    
    @ColumnInfo(name = "DeckName")
    private String DeckName;
    
    @ColumnInfo(name = "CardImage")
    private int CardImage;
    
    @ColumnInfo(name = "TypeImage")
    private int TypeImage;
    
    @ColumnInfo(name = "CardCost")
    private Integer CardCost;
    
    @ColumnInfo(name = "CardName")
    private String CardName;
    
    @ColumnInfo(name = "CardNumber")
    private Integer CardNumber;
    

    }

  2. 我创建的道类如下: :-

    @道 公共接口 DeckBuilderDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public long[] insertCards(SaveDeck... saveDecks);
    
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    public long insertCard(SaveDeck saveDecks);
    
    @Update
    public int updateCardBaseEntries(SaveDeck... saveDecks);
    
    @Update
    public int updateCardBaseEntry(SaveDeck saveDecks);
    
    @Delete
    public int deleteCardBaseEntried(SaveDeck... saveDecks);
    
    @Delete
    public int deleteCardBaseEntry(SaveDeck saveDecks);
    
    @Query("SELECT * FROM SaveDeck")
    public SaveDeck[] getAllDecks();
    
    //probably I do not need the getAllDecks Query. Right now I only need the following one:
    @Query("SELECT * FROM SaveDeck WHERE DeckName = :NameOfDeck ORDER  BY DeckName, CardName")
    public SaveDeck getOneDeck(String NameOfDeck);
    

    }

  3. 进一步创建了数据库类:

    @Database(entities = {SaveDeck.class}, 版本 = 1) 公共抽象类 SaveDecksDataBase 扩展 RoomDatabase { 公共抽象 DeckBuilderDao deckBuilderDao(); }

  4. 最后尝试在我各自的片段中创建设置,这是我挣扎的地方: :-

    公共类 review_fragment 扩展 Fragment {

    private List<TransferDeck> mTransferDeck = DataHolder.getInstance().savedDecklistTransfer;
    SaveDecksDataBase mSavedDecksDB;
    Cursor mCursor;
    
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    //return super.onCreateView(inflater, container, savedInstanceState);
        View view = inflater.inflate(R.layout.review_fragment, container, false);
    
        /*Introduce Cards Recycler*/
    
        RecyclerView rvCards = view.findViewById(R.id.rv_review_cardlist);
        rvCards.setLayoutManager(new GridLayoutManager(getActivity(), 5));
        review_RViewAdapter_Cards adapterCards = new review_RViewAdapter_Cards(getContext(), mTransferDeck);
        rvCards.setAdapter(adapterCards);
    
    
    
        /*Init Room database*/
        mSavedDecksDB = Room.databaseBuilder(getActivity(),SaveDecksDataBase.class,"SavedDecksDB.db").build();
        populateDB(mTransferDeck);
    
    
    
    
    
        return view;
    }
    
    private void populateDB(final List<TransferDeck> mTransferDeck) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                List<SaveDeck> mSaveDeck = new ArrayList<>();
                for(int i = 0; i<mTransferDeck.size(); i++){
                    mSaveDeck.add(new SaveDeck(i, "FirstSavedDeck", mTransferDeck.get(i).getCardImage() ,mTransferDeck.get(i).getTypeImage(), mTransferDeck.get(i).getCost(), mTransferDeck.get(i).getName(), mTransferDeck.get(i).getNumber()));
                }
                mSavedDecksDB.deckBuilderDao().insertCards(mSaveDeck);
    
            }
        }).start();
    }
    

    }

所以现在我不知道如何正确地将新实例添加到我的类SaveDeck。我习惯于使用带有构造函数的 Arraylists。因此我尝试了这种方式。请您看一下并帮帮我吗?

【问题讨论】:

  • 查看多对多关系联结表,了解如何组织事物。
  • 谢谢兄弟。我在下面查看了正确的答案。你知道我为什么投反对票吗:(

标签: android sqlite arraylist android-room


【解决方案1】:

在知道的情况下将 Arraylist 存储在 Room 中的最佳方法是什么 那我要不是只有一个Arraylist来了?

可以说,没有办法是最好的办法。

ArrayList 不只是出现,数据和元素结构(如果每个元素存在多个值)来自某个地方,即它们只是容器,而不是分组数据的持久容器。由于最终结果似乎是持久的结构化数据集,因此主要利用数据库可能会更简单。

我正在尝试为 Cardgame 构建一个 Deckbuilder。不同的甲板 是基于我想在本地安全的一个 Arraylists。

这听起来像是卡片的基础,即那些可用于游戏的卡片。所以听起来您想要在数据库中为 cards 提供一个表。

这意味着你可以创建你的套牌,而不是我希望你能够 保护好它,然后创建一个新牌组,

听起来您想要在数据库中为 套牌 提供一个表,并且套牌可以有一张卡片列表。那么有多少卡呢? 10、20、3000?好吧,如果您利用 Realtional Database Manager(其中 SQLite 和 Room(因为后者是 SQLite 上的抽象层))的关系功能。因此,很可能是一个被称为映射(同一事物的引用、关系和其他名称)的表。

这主要存储由可以识别关系的一部分的列和可以识别另一部分的另一列组成的关系。将此应用于您的情况,DECK 将与多张卡片有关系,一张卡片可能会出现在许多 DECK 中。这是映射表满足的多对多关系。所以你可能需要一个映射表。

为了进一步解释,假设我们谈论的是纸牌(黑桃 A、红心 Q 等)。

所以我们需要三个表,一个 Card 表、一个 Deck 表和一个将卡牌映射到牌组的表(反之亦然)一个 Card_Deck_Map 表格。

Card 表为简单起见,将有一列用于 cardnameDeck 表将有一列用于 deckname 为了高效起见,将使用作为特殊列 rowid 别名的标识符进行映射。因此,上面的表格每个都有一个额外的列,称为 _id(将列命名为 _id,这对 Android 可能是有益的)。

假设您不希望卡片名称或套牌名称重复,因此将应用 UNIQUE 约束,不允许名称重复。

简而言之,这是在 SQL 中的样子(这最终是大多数数据存储、操作和提取完成的方式):-

-- Delete the tables if they exist (just in case)
DROP TABLE IF EXISTS card_deck_map;
DROP TABLE IF EXISTS card;
DROP TABLE IF EXISTS deck;

-- Create the tables
CREATE TABLE IF NOT EXISTS card (_id INTEGER PRIMARY KEY, cardname UNIQUE);
CREATE TABLE IF NOT EXISTS deck (_id INTEGER PRIMARY KEY, deckname UNIQUE);
CREATE TABLE IF NOT EXISTS card_deck_map (
    card_reference INTEGER REFERENCES card(_id), 
    deck_reference INTEGER REFERENCES deck(_id),
    PRIMARY KEY(card_reference,deck_reference)
);

-- Add 3 cards to the card table
INSERT INTO card (cardname) VALUES ('CARD001'),('CARD002'),('CARD003');
-- Add 3 decks to the deck table
INSERT INTO deck (deckname) VALUES ('DECK001'),('DECK002');
-- Create some mapping entries (aka put some cards into each deck)
INSERT INTO card_deck_map VALUES
    (1,2), -- _id value for CARD001 should be 1, _id value for DECK002 should be 2
    (3,2), -- CARD003 is in DECK002
    (2,1), -- CARD002 is in DECK001
    (1,1) -- CARD001 is also in DECK002
;
-- Have a look at what we have (ignore the id values they mean little to the user)
SELECT deckname, cardname 
FROM deck 
    JOIN card_deck_map ON deck._id = deck_reference
    JOIN card ON card_deck_map.card_reference = card._id
ORDER BY deckname, cardname
;

上面的输出将是:-

所以现在数据库设计看起来很适合,那么现在可以转换它以供 ROOM 使用。

首先是 3 个实体 Defining data using Room entities

Card.java

:-

@Entity (indices = {@Index(value = {"cardname"}, unique = true)})
public class Card {
    @PrimaryKey(autoGenerate = true)
    public long _id;

    @ColumnInfo(name = "cardname")
    public String cardname;

}

Deck.java

:-

@Entity(indices = {@Index(value = "deckname", unique = true)})
public class Deck {

    @PrimaryKey(autoGenerate = true)
    public long _id;

    @ColumnInfo(name = "deckname")
    public String deckname;
}

Card_Deck_Map.java

:-

@Entity(
        primaryKeys = {"card_reference","deck_reference"},
        foreignKeys = {
                @ForeignKey(entity = Card.class,parentColumns = "_id",childColumns = "card_reference"),
                @ForeignKey(entity = Deck.class, parentColumns = "_id",childColumns = "deck_reference")}
                )
public class Card_Deck_Map {

    @ColumnInfo (name="card_reference")
    public long card_reference;

    @ColumnInfo(name="deck_reference")
    public long deck_reference;
}

现在您需要数据访问对象定义Accessing data using Room DAOs

DeckBuildeDao

:-

@道 公共接口 DeckBuilderDao {

@Insert(onConflict = OnConflictStrategy.IGNORE)
public long[] insertCards(Card... cards);

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

@Update
public int updateCardBaseEntries(Card... cards);

@Update
public int updateCardBaseEntry(Card card);

@Delete
public int deleteCardBaseEntried(Card... cards);

@Delete
public int deleteCardBaseEntry(Card card);

@Query("SELECT * FROM card")
public Card[] getAllCards();

@Query("SELECT * FROM card WHERE _id = :id")
public Card getACard(long id);



@Insert(onConflict = OnConflictStrategy.IGNORE)
public long[] insertDecks(Deck... decks);

@Insert(onConflict = OnConflictStrategy.IGNORE)
public long insertDeck(Deck deck);

@Update
public int updateDeckEntries(Deck... decks);

@Update
public int updateDeckEntry(Deck deck);

@Delete
public int deleteDeckEntries(Deck... decks);

@Delete
public int deleteDeckEntry(Deck deck);

@Query("SELECT * FROM deck")
public int getAllDecks();

@Query("SELECT * FROM deck WHERE _id = :id")
public Deck getADeck(long id);


@Insert(onConflict = OnConflictStrategy.IGNORE)
public long[] addCardDeckEntries(Card_Deck_Map... cardDeckMaps);

@Insert(onConflict = OnConflictStrategy.IGNORE)
public long addCardDeckEntry(Card_Deck_Map cardDeckMap);

@Query("SELECT Deck._id,Card.cardname, Deck.deckname " +
        "FROM deck " +
        "JOIN card_deck_map ON deck._id = card_deck_map.deck_reference " +
        "JOIN card ON card_deck_map.card_reference = card._id " +
        "ORDER BY deckname, cardname")
public Cursor getAllDecksWithCards();

}

将实体和 DAO 联系在一起的数据库类

DeckBuilderDatabase.java

:-

@Database(entities = {Card.class, Deck.class, Card_Deck_Map.class}, version = 1)
public abstract class DeckBuilderDatabase extends RoomDatabase {
    public abstract DeckBuilderDao deckBuilderDao();
}

现在是一个使用数据库的活动。

在这个工作示例中;

  1. 数据库将填充 2 副牌(Deck001 和 Deck002),根据一包扑克牌减去 Jokers 的牌库。

    1. 卡片将命名为黑桃 A、红桃 2。
  2. 套牌将加载一些卡片(映射)

    1. Deck002 包含所有 52 张卡片。
    2. Deck001 有 3 张卡片。
  3. 将从数据库中提取牌组和卡片并用于填充 ListView。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    public static final String[] SUITS = new String[]{"Spades","Hearts","Clubs","Diamons"};
    public static final int CARDS_IN_A_SUIT = 13;


    DeckBuilderDatabase mDBDB;
    SimpleCursorAdapter mSCA;
    ListView mDecks_and_Cards_List;
    Cursor mCursor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mDecks_and_Cards_List = this.findViewById(R.id.decksandcards);
        mDBDB = Room.databaseBuilder(this,DeckBuilderDatabase.class,"deckbuilder.db").build();
        populateDB();
    }

    /**
     * Populate the DB with some data, extract the data in the DB and setup the ListView
     */
    private void populateDB() {
        new Thread(new Runnable() {
            @Override
            public void run() {

                Card_Deck_Map currentcdm = new Card_Deck_Map();

                Deck[] decks_to_add = new Deck[]{new Deck(), new Deck()};
                decks_to_add[0].deckname = "DECK001";
                decks_to_add[1].deckname= "DECK002";
                mDBDB.deckBuilderDao().insertDecks(decks_to_add);

                // Build Card base pack of 52 (no Jokers)
                Card[] cardstoadd = new Card[CARDS_IN_A_SUIT * SUITS.length];
                int counter = 0;
                for (int suit = 0; suit < SUITS.length; suit++) {
                    for (int cardval = 0; cardval < CARDS_IN_A_SUIT; cardval++) {
                        Card thiscard = new Card();
                        String thiscardname = generateCardValueDescription(cardval+1,suit);
                        thiscard.cardname = thiscardname;
                        cardstoadd[counter++] = thiscard;
                    }
                }
                mDBDB.deckBuilderDao().insertCards(cardstoadd);

                // Populate the decks with cards Deck002 has full pack of 52 Deck001 has 3 cards
                Card_Deck_Map[] mappings = new Card_Deck_Map[55];
                for (int cardid = 1; cardid < 53; cardid++) {
                    Card_Deck_Map cdm = new Card_Deck_Map();
                    cdm.deck_reference = 2;
                    cdm.card_reference = cardid;
                    mappings[cardid-1] = cdm;
                }
                Card_Deck_Map cdm53 = new Card_Deck_Map();
                cdm53.card_reference = 19;
                cdm53.deck_reference = 1;
                mappings[52] = cdm53;
                Card_Deck_Map cdm54 = new Card_Deck_Map();
                cdm54.card_reference = 10;
                cdm54.deck_reference = 1;
                mappings[53] = cdm54;
                Card_Deck_Map cdm55 = new Card_Deck_Map();
                cdm55.card_reference = 23;
                cdm55.deck_reference = 1;
                mappings[54] = cdm55;
                mDBDB.deckBuilderDao().addCardDeckEntries(mappings);

                // Get the Decks and cards in the decks
                mCursor = mDBDB.deckBuilderDao().getAllDecksWithCards();
                setupOrRefeshListView();
            }
        }).start();
    }


    /**
     * Handles the ListView (also write data to the log for debugging)
     */
    private void setupOrRefeshListView() {
        int rowcount = mCursor.getCount();
        Log.d("ROWS","Number of rows in the Cursor is " + String.valueOf(rowcount));
        while (mCursor.moveToNext()) {
            Log.d(
                    "ROWS",
                    "Row " +
                            String.valueOf(mCursor.getPosition()) +
                            " Has a deck called " +
                            mCursor.getString(mCursor.getColumnIndex("deckname")) +
                            " and a card called " +
                            mCursor.getString(mCursor.getColumnIndex("cardname"))
            );
        }
        if (mSCA == null) {
            mSCA = new SimpleCursorAdapter(
                    this,
                    android.R.layout.simple_list_item_2,
                    mCursor,
                    new String[]{
                            "deckname",
                            "cardname"},
                    new int[]{
                            android.R.id.text1,
                            android.R.id.text2},
                    0
            );
            mDecks_and_Cards_List.setAdapter(mSCA);
        } else {
            mSCA.swapCursor(mCursor);
        }
    }

    /**
     *  Converts numeric cardvalue (1-13) and suit to a decriptive name
     * @param cardvalue
     * @param suit
     * @return
     */
    private String generateCardValueDescription(int cardvalue, int suit) {
        String rv;
        switch (cardvalue) {
            case 1:
                rv = "Ace of " + SUITS[suit];
                break;
            case 11:
                rv = "Jack of " + SUITS[suit];
                break;
            case 12:
                rv = "Queen of " + SUITS[suit];
                break;
            case 13:
                rv = "King of " + SUITS[suit];
                break;
                default:
                    rv = String.valueOf(cardvalue) + " of " + SUITS[suit];
        }
        return rv;
    }
}

结果迷你应用:-

【讨论】:

  • 感谢 MikeT。我会尽快查看您的答案,可能是明天。我很乐意给你点我可以的!
  • 嘿迈克,我的设置有点不同。我会尝试仍然使用它。我已经构建了一个片段,它使我能够基本上构建你的套牌。我正在 Recyclerview 中加载所有卡片的预览。所有卡片都已存储在(SQlite)数据库中的不同表中。如果您为您的套牌选择一张卡片,则会在 Arraylist 中创建一个新条目。 我的计划是保存这个 Arraylist。 所以基本上,我不需要为我的卡片建立一个新的数据库,它已经存在。我只是想存储我的Arraylist,所以我认为不需要映射表。 :)
  • 嘿,迈克,我尝试实施您的解决方案。在我的帖子结束时,我编辑了我尝试更新 popluateDB 方法的尝试。你能告诉我这样做的正确方法吗? :)
  • @Christian.gruener 给分 = 勾选答案。您不能直接存储数组列表,它只是一个数据容器,可以归结为要存储的许多数据项(可能是卡片)。将项目列表(例如卡片)存储为单个列既高效又笨拙。如果您也花一些时间查看并理解代码。你可能会注意到我已经包含了一些关于如何存储数组的文件(尽管 Room 可以接受它们,请参阅复数名称的方法)。
  • @Christian.gruener 否决票,可能是因为问题的广泛性,可能是因为有些人认为它不代表研究,也许是因为有些人认为它缺乏细节,也许是因为有些人认为它作为征求要写的解决方案。有些是因为 BEST 是固执己见的,有些可能是因为没有最小完整和可验证的问题。我的建议,不要把选票记在心里。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-21
  • 1970-01-01
  • 2011-04-04
  • 2016-09-02
  • 2016-05-19
  • 2021-10-31
相关资源
最近更新 更多