Saving Multiple Arraylist in Room Database - Best way of doing it?
Background:
I am trying to build a Deckbuilder for a Cardgame. The different decks are based on
one Arraylists that I want to safe locally. That means you can create your deck and than I want you to be able to safe it, and then create a new deck, be able to save it again... -> This yields multiple Arraylists that I want to safe in the same class.
Question:
What is the best way of storing the Arraylists in Room while knowing that I want to there is not only one Arraylist coming?
From what I know now is that I have to create a entity class which would basically create me one(?) table, where I would have to save the Arraylists after each other?
Is there a better way of doing it.
Bonus:
I would love an basic example of how to do it aswell, since this seems like a super hard to achieve by myself.
Thanks very much!
Edit:
Code Sample
So what I implemented with the baseline from your codesample is the following:
I created the class SaveDeck which should be able to Save a Deck with a given Deckname:
:-
@Entity
public class SaveDeck implements Serializable {
@PrimaryKey(autoGenerate = true)
private 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;
}
I created the Dao Class as follows:
:-
@Dao
public interface 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);
}
Furthermore created the DataBase Class:
@Database(entities = {SaveDeck.class}, version = 1)
public abstract class SaveDecksDataBase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Finally tried to create the setup in my respective fragment, this is where I struggle:
:-
public class review_fragment extends 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();
}
}
So right now I do not know how to correctly add new instances to my Class SaveDeck
. I am used to use Arraylists, with constructors. Therefore I tried it that way. Could you maybe have a look on it and give me a helping hand please?
android sqlite arraylist android-room
add a comment |
Background:
I am trying to build a Deckbuilder for a Cardgame. The different decks are based on
one Arraylists that I want to safe locally. That means you can create your deck and than I want you to be able to safe it, and then create a new deck, be able to save it again... -> This yields multiple Arraylists that I want to safe in the same class.
Question:
What is the best way of storing the Arraylists in Room while knowing that I want to there is not only one Arraylist coming?
From what I know now is that I have to create a entity class which would basically create me one(?) table, where I would have to save the Arraylists after each other?
Is there a better way of doing it.
Bonus:
I would love an basic example of how to do it aswell, since this seems like a super hard to achieve by myself.
Thanks very much!
Edit:
Code Sample
So what I implemented with the baseline from your codesample is the following:
I created the class SaveDeck which should be able to Save a Deck with a given Deckname:
:-
@Entity
public class SaveDeck implements Serializable {
@PrimaryKey(autoGenerate = true)
private 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;
}
I created the Dao Class as follows:
:-
@Dao
public interface 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);
}
Furthermore created the DataBase Class:
@Database(entities = {SaveDeck.class}, version = 1)
public abstract class SaveDecksDataBase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Finally tried to create the setup in my respective fragment, this is where I struggle:
:-
public class review_fragment extends 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();
}
}
So right now I do not know how to correctly add new instances to my Class SaveDeck
. I am used to use Arraylists, with constructors. Therefore I tried it that way. Could you maybe have a look on it and give me a helping hand please?
android sqlite arraylist android-room
Look up many to many relationships and junction tables to get an idea of how to organize things.
– Shawn
Nov 24 '18 at 16:49
Thanks bro. I check out the decend answer below. Do you know why I got downvotes :(
– Christian.gruener
Nov 25 '18 at 15:23
add a comment |
Background:
I am trying to build a Deckbuilder for a Cardgame. The different decks are based on
one Arraylists that I want to safe locally. That means you can create your deck and than I want you to be able to safe it, and then create a new deck, be able to save it again... -> This yields multiple Arraylists that I want to safe in the same class.
Question:
What is the best way of storing the Arraylists in Room while knowing that I want to there is not only one Arraylist coming?
From what I know now is that I have to create a entity class which would basically create me one(?) table, where I would have to save the Arraylists after each other?
Is there a better way of doing it.
Bonus:
I would love an basic example of how to do it aswell, since this seems like a super hard to achieve by myself.
Thanks very much!
Edit:
Code Sample
So what I implemented with the baseline from your codesample is the following:
I created the class SaveDeck which should be able to Save a Deck with a given Deckname:
:-
@Entity
public class SaveDeck implements Serializable {
@PrimaryKey(autoGenerate = true)
private 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;
}
I created the Dao Class as follows:
:-
@Dao
public interface 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);
}
Furthermore created the DataBase Class:
@Database(entities = {SaveDeck.class}, version = 1)
public abstract class SaveDecksDataBase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Finally tried to create the setup in my respective fragment, this is where I struggle:
:-
public class review_fragment extends 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();
}
}
So right now I do not know how to correctly add new instances to my Class SaveDeck
. I am used to use Arraylists, with constructors. Therefore I tried it that way. Could you maybe have a look on it and give me a helping hand please?
android sqlite arraylist android-room
Background:
I am trying to build a Deckbuilder for a Cardgame. The different decks are based on
one Arraylists that I want to safe locally. That means you can create your deck and than I want you to be able to safe it, and then create a new deck, be able to save it again... -> This yields multiple Arraylists that I want to safe in the same class.
Question:
What is the best way of storing the Arraylists in Room while knowing that I want to there is not only one Arraylist coming?
From what I know now is that I have to create a entity class which would basically create me one(?) table, where I would have to save the Arraylists after each other?
Is there a better way of doing it.
Bonus:
I would love an basic example of how to do it aswell, since this seems like a super hard to achieve by myself.
Thanks very much!
Edit:
Code Sample
So what I implemented with the baseline from your codesample is the following:
I created the class SaveDeck which should be able to Save a Deck with a given Deckname:
:-
@Entity
public class SaveDeck implements Serializable {
@PrimaryKey(autoGenerate = true)
private 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;
}
I created the Dao Class as follows:
:-
@Dao
public interface 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);
}
Furthermore created the DataBase Class:
@Database(entities = {SaveDeck.class}, version = 1)
public abstract class SaveDecksDataBase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Finally tried to create the setup in my respective fragment, this is where I struggle:
:-
public class review_fragment extends 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();
}
}
So right now I do not know how to correctly add new instances to my Class SaveDeck
. I am used to use Arraylists, with constructors. Therefore I tried it that way. Could you maybe have a look on it and give me a helping hand please?
android sqlite arraylist android-room
android sqlite arraylist android-room
edited Nov 25 '18 at 16:37
Christian.gruener
asked Nov 24 '18 at 14:47
Christian.gruenerChristian.gruener
438
438
Look up many to many relationships and junction tables to get an idea of how to organize things.
– Shawn
Nov 24 '18 at 16:49
Thanks bro. I check out the decend answer below. Do you know why I got downvotes :(
– Christian.gruener
Nov 25 '18 at 15:23
add a comment |
Look up many to many relationships and junction tables to get an idea of how to organize things.
– Shawn
Nov 24 '18 at 16:49
Thanks bro. I check out the decend answer below. Do you know why I got downvotes :(
– Christian.gruener
Nov 25 '18 at 15:23
Look up many to many relationships and junction tables to get an idea of how to organize things.
– Shawn
Nov 24 '18 at 16:49
Look up many to many relationships and junction tables to get an idea of how to organize things.
– Shawn
Nov 24 '18 at 16:49
Thanks bro. I check out the decend answer below. Do you know why I got downvotes :(
– Christian.gruener
Nov 25 '18 at 15:23
Thanks bro. I check out the decend answer below. Do you know why I got downvotes :(
– Christian.gruener
Nov 25 '18 at 15:23
add a comment |
1 Answer
1
active
oldest
votes
What is the best way of storing the Arraylists in Room while knowing
that I want to there is not only one Arraylist coming?
Arguably no way is the best way.
ArrayList's don't just appear, the data and element structure (if multiple values exist per element) comes from somewhere, that is they are just containers but not persistent containers of grouped data. As the end result appears to be a persistent structured sets of data then it would likely be simpler to primarily utilise a database.
I am trying to build a Deckbuilder for a Cardgame. The different decks
are based on one Arraylists that I want to safe locally.
It would sound like this is the base of cards i.e. those available for the game. So it sounds like you want a table in the database for the cards.
That means you can create your deck and than I want you to be able to
safe it, and then create a new deck,
It sounds like you want a table in the database for the decks and that a deck can have a list of cards. So how many cards? 10, 20, 3000? Well if you utilise the relationship capabilities of a Realtional Database Manager (which SQLite and therefore Room (as the latter is an abstract layer over SQLite)) is. So it is likely that an what is know as a mapping (reference, relationship and other names for the same thing) table.
This primarily stores relationships that consist of a column that can identify one part of the relationship and another column that can identify the other part. Apply this to your case a DECK would have a relationship to a number of cards, a CARD may be able to appear in a number of DECKs. This is a many-many relationship which a mapping table caters for. So you likely want a mapping table.
For the basis of explaining further it will be assumed that we are talking about playing cards (Ace of Spades, Queen of Hearts and so on).
So we want three tables a Card table, a Deck table and a table that maps cards to decks (and therefore vice-versa) a Card_Deck_Map table.
The Card table to keep it simple will have a single column for the cardname.
The Deck tables will have a single column for the deckname
To be efficient an identifier which is an alias of the special column rowid will be used for the mapping. So the above tables will each have an extra column that will be called _id (naming the column as _id this can be beneficial for Android).
It will be assumed that you don't want a card name nor a deck name to be duplicated and as such a UNIQUE constraint will be placed applied that will not allow the names to be duplicated.
To cut things short here's how this could look like in SQL (which is ultimately how most data storage, manipulation and extraction is done) :-
-- 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
;
The output from the above would be :-
So now the database design appears to suit, then it can now be converted for use by ROOM.
First the 3 entities 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;
}
Now you want the Data Access Objects definitions Accessing data using Room DAOs
DeckBuildeDao
:-
@Dao
public interface 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();
}
A class for the Database that ties the entities and DAO's together
DeckBuilderDatabase.java
:-
@Database(entities = {Card.class, Deck.class, Card_Deck_Map.class}, version = 1)
public abstract class DeckBuilderDatabase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Now an activity that uses the database.
In this working example;
the database will be populated with 2 Decks (Deck001 and Deck002) a card base as per a pack of playing cards less the Jokers.
- Cards will be named like Ace of Spades, 2 of Hearts.
The decks will be loaded with some cards (the mapping)
- Deck002 with all 52 cards.
- Deck001 with 3 cards.
The Decks and Cards will be extracted from the database and used to populate a 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;
}
}
The Resultant mini-app :-
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
|
show 3 more comments
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53459317%2fsaving-multiple-arraylist-in-room-database-best-way-of-doing-it%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
What is the best way of storing the Arraylists in Room while knowing
that I want to there is not only one Arraylist coming?
Arguably no way is the best way.
ArrayList's don't just appear, the data and element structure (if multiple values exist per element) comes from somewhere, that is they are just containers but not persistent containers of grouped data. As the end result appears to be a persistent structured sets of data then it would likely be simpler to primarily utilise a database.
I am trying to build a Deckbuilder for a Cardgame. The different decks
are based on one Arraylists that I want to safe locally.
It would sound like this is the base of cards i.e. those available for the game. So it sounds like you want a table in the database for the cards.
That means you can create your deck and than I want you to be able to
safe it, and then create a new deck,
It sounds like you want a table in the database for the decks and that a deck can have a list of cards. So how many cards? 10, 20, 3000? Well if you utilise the relationship capabilities of a Realtional Database Manager (which SQLite and therefore Room (as the latter is an abstract layer over SQLite)) is. So it is likely that an what is know as a mapping (reference, relationship and other names for the same thing) table.
This primarily stores relationships that consist of a column that can identify one part of the relationship and another column that can identify the other part. Apply this to your case a DECK would have a relationship to a number of cards, a CARD may be able to appear in a number of DECKs. This is a many-many relationship which a mapping table caters for. So you likely want a mapping table.
For the basis of explaining further it will be assumed that we are talking about playing cards (Ace of Spades, Queen of Hearts and so on).
So we want three tables a Card table, a Deck table and a table that maps cards to decks (and therefore vice-versa) a Card_Deck_Map table.
The Card table to keep it simple will have a single column for the cardname.
The Deck tables will have a single column for the deckname
To be efficient an identifier which is an alias of the special column rowid will be used for the mapping. So the above tables will each have an extra column that will be called _id (naming the column as _id this can be beneficial for Android).
It will be assumed that you don't want a card name nor a deck name to be duplicated and as such a UNIQUE constraint will be placed applied that will not allow the names to be duplicated.
To cut things short here's how this could look like in SQL (which is ultimately how most data storage, manipulation and extraction is done) :-
-- 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
;
The output from the above would be :-
So now the database design appears to suit, then it can now be converted for use by ROOM.
First the 3 entities 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;
}
Now you want the Data Access Objects definitions Accessing data using Room DAOs
DeckBuildeDao
:-
@Dao
public interface 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();
}
A class for the Database that ties the entities and DAO's together
DeckBuilderDatabase.java
:-
@Database(entities = {Card.class, Deck.class, Card_Deck_Map.class}, version = 1)
public abstract class DeckBuilderDatabase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Now an activity that uses the database.
In this working example;
the database will be populated with 2 Decks (Deck001 and Deck002) a card base as per a pack of playing cards less the Jokers.
- Cards will be named like Ace of Spades, 2 of Hearts.
The decks will be loaded with some cards (the mapping)
- Deck002 with all 52 cards.
- Deck001 with 3 cards.
The Decks and Cards will be extracted from the database and used to populate a 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;
}
}
The Resultant mini-app :-
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
|
show 3 more comments
What is the best way of storing the Arraylists in Room while knowing
that I want to there is not only one Arraylist coming?
Arguably no way is the best way.
ArrayList's don't just appear, the data and element structure (if multiple values exist per element) comes from somewhere, that is they are just containers but not persistent containers of grouped data. As the end result appears to be a persistent structured sets of data then it would likely be simpler to primarily utilise a database.
I am trying to build a Deckbuilder for a Cardgame. The different decks
are based on one Arraylists that I want to safe locally.
It would sound like this is the base of cards i.e. those available for the game. So it sounds like you want a table in the database for the cards.
That means you can create your deck and than I want you to be able to
safe it, and then create a new deck,
It sounds like you want a table in the database for the decks and that a deck can have a list of cards. So how many cards? 10, 20, 3000? Well if you utilise the relationship capabilities of a Realtional Database Manager (which SQLite and therefore Room (as the latter is an abstract layer over SQLite)) is. So it is likely that an what is know as a mapping (reference, relationship and other names for the same thing) table.
This primarily stores relationships that consist of a column that can identify one part of the relationship and another column that can identify the other part. Apply this to your case a DECK would have a relationship to a number of cards, a CARD may be able to appear in a number of DECKs. This is a many-many relationship which a mapping table caters for. So you likely want a mapping table.
For the basis of explaining further it will be assumed that we are talking about playing cards (Ace of Spades, Queen of Hearts and so on).
So we want three tables a Card table, a Deck table and a table that maps cards to decks (and therefore vice-versa) a Card_Deck_Map table.
The Card table to keep it simple will have a single column for the cardname.
The Deck tables will have a single column for the deckname
To be efficient an identifier which is an alias of the special column rowid will be used for the mapping. So the above tables will each have an extra column that will be called _id (naming the column as _id this can be beneficial for Android).
It will be assumed that you don't want a card name nor a deck name to be duplicated and as such a UNIQUE constraint will be placed applied that will not allow the names to be duplicated.
To cut things short here's how this could look like in SQL (which is ultimately how most data storage, manipulation and extraction is done) :-
-- 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
;
The output from the above would be :-
So now the database design appears to suit, then it can now be converted for use by ROOM.
First the 3 entities 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;
}
Now you want the Data Access Objects definitions Accessing data using Room DAOs
DeckBuildeDao
:-
@Dao
public interface 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();
}
A class for the Database that ties the entities and DAO's together
DeckBuilderDatabase.java
:-
@Database(entities = {Card.class, Deck.class, Card_Deck_Map.class}, version = 1)
public abstract class DeckBuilderDatabase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Now an activity that uses the database.
In this working example;
the database will be populated with 2 Decks (Deck001 and Deck002) a card base as per a pack of playing cards less the Jokers.
- Cards will be named like Ace of Spades, 2 of Hearts.
The decks will be loaded with some cards (the mapping)
- Deck002 with all 52 cards.
- Deck001 with 3 cards.
The Decks and Cards will be extracted from the database and used to populate a 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;
}
}
The Resultant mini-app :-
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
|
show 3 more comments
What is the best way of storing the Arraylists in Room while knowing
that I want to there is not only one Arraylist coming?
Arguably no way is the best way.
ArrayList's don't just appear, the data and element structure (if multiple values exist per element) comes from somewhere, that is they are just containers but not persistent containers of grouped data. As the end result appears to be a persistent structured sets of data then it would likely be simpler to primarily utilise a database.
I am trying to build a Deckbuilder for a Cardgame. The different decks
are based on one Arraylists that I want to safe locally.
It would sound like this is the base of cards i.e. those available for the game. So it sounds like you want a table in the database for the cards.
That means you can create your deck and than I want you to be able to
safe it, and then create a new deck,
It sounds like you want a table in the database for the decks and that a deck can have a list of cards. So how many cards? 10, 20, 3000? Well if you utilise the relationship capabilities of a Realtional Database Manager (which SQLite and therefore Room (as the latter is an abstract layer over SQLite)) is. So it is likely that an what is know as a mapping (reference, relationship and other names for the same thing) table.
This primarily stores relationships that consist of a column that can identify one part of the relationship and another column that can identify the other part. Apply this to your case a DECK would have a relationship to a number of cards, a CARD may be able to appear in a number of DECKs. This is a many-many relationship which a mapping table caters for. So you likely want a mapping table.
For the basis of explaining further it will be assumed that we are talking about playing cards (Ace of Spades, Queen of Hearts and so on).
So we want three tables a Card table, a Deck table and a table that maps cards to decks (and therefore vice-versa) a Card_Deck_Map table.
The Card table to keep it simple will have a single column for the cardname.
The Deck tables will have a single column for the deckname
To be efficient an identifier which is an alias of the special column rowid will be used for the mapping. So the above tables will each have an extra column that will be called _id (naming the column as _id this can be beneficial for Android).
It will be assumed that you don't want a card name nor a deck name to be duplicated and as such a UNIQUE constraint will be placed applied that will not allow the names to be duplicated.
To cut things short here's how this could look like in SQL (which is ultimately how most data storage, manipulation and extraction is done) :-
-- 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
;
The output from the above would be :-
So now the database design appears to suit, then it can now be converted for use by ROOM.
First the 3 entities 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;
}
Now you want the Data Access Objects definitions Accessing data using Room DAOs
DeckBuildeDao
:-
@Dao
public interface 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();
}
A class for the Database that ties the entities and DAO's together
DeckBuilderDatabase.java
:-
@Database(entities = {Card.class, Deck.class, Card_Deck_Map.class}, version = 1)
public abstract class DeckBuilderDatabase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Now an activity that uses the database.
In this working example;
the database will be populated with 2 Decks (Deck001 and Deck002) a card base as per a pack of playing cards less the Jokers.
- Cards will be named like Ace of Spades, 2 of Hearts.
The decks will be loaded with some cards (the mapping)
- Deck002 with all 52 cards.
- Deck001 with 3 cards.
The Decks and Cards will be extracted from the database and used to populate a 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;
}
}
The Resultant mini-app :-
What is the best way of storing the Arraylists in Room while knowing
that I want to there is not only one Arraylist coming?
Arguably no way is the best way.
ArrayList's don't just appear, the data and element structure (if multiple values exist per element) comes from somewhere, that is they are just containers but not persistent containers of grouped data. As the end result appears to be a persistent structured sets of data then it would likely be simpler to primarily utilise a database.
I am trying to build a Deckbuilder for a Cardgame. The different decks
are based on one Arraylists that I want to safe locally.
It would sound like this is the base of cards i.e. those available for the game. So it sounds like you want a table in the database for the cards.
That means you can create your deck and than I want you to be able to
safe it, and then create a new deck,
It sounds like you want a table in the database for the decks and that a deck can have a list of cards. So how many cards? 10, 20, 3000? Well if you utilise the relationship capabilities of a Realtional Database Manager (which SQLite and therefore Room (as the latter is an abstract layer over SQLite)) is. So it is likely that an what is know as a mapping (reference, relationship and other names for the same thing) table.
This primarily stores relationships that consist of a column that can identify one part of the relationship and another column that can identify the other part. Apply this to your case a DECK would have a relationship to a number of cards, a CARD may be able to appear in a number of DECKs. This is a many-many relationship which a mapping table caters for. So you likely want a mapping table.
For the basis of explaining further it will be assumed that we are talking about playing cards (Ace of Spades, Queen of Hearts and so on).
So we want three tables a Card table, a Deck table and a table that maps cards to decks (and therefore vice-versa) a Card_Deck_Map table.
The Card table to keep it simple will have a single column for the cardname.
The Deck tables will have a single column for the deckname
To be efficient an identifier which is an alias of the special column rowid will be used for the mapping. So the above tables will each have an extra column that will be called _id (naming the column as _id this can be beneficial for Android).
It will be assumed that you don't want a card name nor a deck name to be duplicated and as such a UNIQUE constraint will be placed applied that will not allow the names to be duplicated.
To cut things short here's how this could look like in SQL (which is ultimately how most data storage, manipulation and extraction is done) :-
-- 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
;
The output from the above would be :-
So now the database design appears to suit, then it can now be converted for use by ROOM.
First the 3 entities 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;
}
Now you want the Data Access Objects definitions Accessing data using Room DAOs
DeckBuildeDao
:-
@Dao
public interface 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();
}
A class for the Database that ties the entities and DAO's together
DeckBuilderDatabase.java
:-
@Database(entities = {Card.class, Deck.class, Card_Deck_Map.class}, version = 1)
public abstract class DeckBuilderDatabase extends RoomDatabase {
public abstract DeckBuilderDao deckBuilderDao();
}
Now an activity that uses the database.
In this working example;
the database will be populated with 2 Decks (Deck001 and Deck002) a card base as per a pack of playing cards less the Jokers.
- Cards will be named like Ace of Spades, 2 of Hearts.
The decks will be loaded with some cards (the mapping)
- Deck002 with all 52 cards.
- Deck001 with 3 cards.
The Decks and Cards will be extracted from the database and used to populate a 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;
}
}
The Resultant mini-app :-
answered Nov 25 '18 at 5:57
MikeTMikeT
16.8k112642
16.8k112642
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
|
show 3 more comments
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Thanks MikeT. I check out your answer as soon as possible, probably tomorrow. I would love to give you points of I could!
– Christian.gruener
Nov 25 '18 at 15:23
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike, my setup is a bit different. I will try to still use it. I already build a fragment which enables me to basically build your deck. I am loading a Preview of all cards in Recyclerview. All cards are already stored in different tables in a (SQlite) DB. If you choose a Card for your deck, a new entry in a Arraylist is getting created. My plan was to save this Arraylist. So basically, I do not need to build up a new databank for my cards, it already exists. I just want to store my Arraylist, so I do not think there is the need for mapping table. :)
– Christian.gruener
Nov 25 '18 at 15:49
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
Hey Mike I tried to implement your solution. At the end of my post I edited my try of updated the popluateDB method. Could you show me the right way of doing that? :)
– Christian.gruener
Nov 25 '18 at 16:38
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener gives points = tick the answer as answered. You can't directly store an arraylist, it is just a container for data which will boil down to a number of data items to be stored (probably cards). Storing a list of items, such as cards, as a single column is in efficient and awkward. If you take some time too look at and understand the code. You may notice that I've included some cles as to how to store array's (albeit that Room can take them, see the methods with plural names).
– MikeT
Nov 25 '18 at 19:22
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
@Christian.gruener re getting down votes, possibly because of the broadness of the question, perhaps because some saw it as not indicating research, perhaps because some saw it as lacking specifics and perhaps as some saw it as soliciting for a solution to be written. Some because BEST is opinionated, others perhaps because of no Minimal Complete and Verifiable question. My advice, don't take down votes to heart.
– MikeT
Nov 25 '18 at 19:40
|
show 3 more comments
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53459317%2fsaving-multiple-arraylist-in-room-database-best-way-of-doing-it%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Look up many to many relationships and junction tables to get an idea of how to organize things.
– Shawn
Nov 24 '18 at 16:49
Thanks bro. I check out the decend answer below. Do you know why I got downvotes :(
– Christian.gruener
Nov 25 '18 at 15:23