I am making an Android application using GreenDao, and I have these two entities:
#Entity
public class Quiz {
#Id(autoincrement = true)
private Long id;
private Date date;
private String type;
#ToMany(referencedJoinProperty = "quizId")
private List<Answer> answers;
}
#Entity
public class Answer {
#Id(autoincrement = true)
private Long id;
private int answer;
private float value;
private int questionNumber;
private String type;
private Long quizId;
}
I'm trying to fetch the list of Quiz using the following code:
DaoSession daoSession = AndroidAdapter.getDaoSession();
QuizDao quizDao = daoSession.getQuizDao();
List<Quiz> quizs = quizDao.loadAll();
But the list of answers always comes empty, what am I doing wrong?
My AndroidAdapter class:
public class AndroidAdapter {
public static Context getContext() {
return AppApplication.getContext();
}
public static DaoSession getDaoSession() {
return AppApplication.getDaoSession();
}
}
And AppApplication class
#Override
public void onCreate() {
super.onCreate();
context = this;
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this,ENCRYPTED ? "exam-db-encrypted" : "exam-db");
Database db = ENCRYPTED ? helper.getEncryptedWritableDb("exam-secret") : helper.getWritableDb();
daoSession = new DaoMaster(db).newSession();
this.addAllEvents();
}
public static DaoSession getDaoSession() {
return daoSession;
}
For me, I've not entered some Answer entities in my db.
Once I've added a code to add those entities to the db and I've requested the list with a generated getter for the List<Answer> answers - it all worked as it should 😊
Related
I have a Room database, containing two entities: MusicItem and FormatItem, with the former containing a foreign key to the latter. Using "DB Browser for SQLite" I have filled the table for FormatItem with some formats and added this DB as a prepackaged DB to the Room database with createFromAsset.
The database seems to be working fine - the user can add, remove and edit items in the database as expected, but for some reason, when the app is closed and opened again, the DB is empty. It seems that the fallbackToDestructiveMigration method is called (I haven't implemented a migration strategy yet) when building the database, and I don't understand why. Room doesn't say anything about the prepackaged DB (meaning the schema is correct). When I disable createFromAsset everything works fine, and the data is persistent after closing and restarting the app.
My question is why is the migration code called, and how can I prevent the deletion of the data.
My code:
class ItemsRoomDatabase:
public abstract class ItemsRoomDatabase extends RoomDatabase {
private static final String TAG = ItemsRoomDatabase.class.getSimpleName();
private static ItemsRoomDatabase INSTANCE;
static ItemsRoomDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
synchronized (ItemsRoomDatabase.class) {
if (INSTANCE == null) {
Log.d(TAG, "called");
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
ItemsRoomDatabase.class, "items_database")
// Wipes and rebuilds instead of migrating
// if no Migration object.
// Migration is not part of this practical.
// TODO: add migration support
.fallbackToDestructiveMigration()
.createFromAsset("prepackaged_db.db")
.build();
}
}
}
return INSTANCE;
}
public abstract MusicItemDao musicItemDao();
public abstract FormatItemDao formatItemDao();
}
class MusicItem:
#Entity(tableName = "music_items_table",
foreignKeys = #ForeignKey(entity = FormatItem.class,
parentColumns = "id", childColumns = "formatId", onDelete = NO_ACTION))
class MusicItem implements Parcelable {
enum Classification {
COLLECTION,
WISHLIST,
NONE;
}
#PrimaryKey
#NonNull
private final String id;
#NonNull
private final String title;
#NonNull
private final String artist;
private final String thumbUrl;
private List<String> genres;
#NonNull
private List<Track> tracks;
private String year;
#NonNull
private Classification classification;
#NonNull
private int formatId;
.
.
.
}
class FormatItem:
#Entity(tableName = "format_items_table")
class FormatItem {
#PrimaryKey(autoGenerate = true)
#NonNull
private int id;
#NonNull
private final String name;
#NonNull
private int itemsCount = 0;
FormatItem(String name) {
this.name = name;
}
int getId() {
return id;
}
String getName() {
return name;
}
int getItemsCount() {
return itemsCount;
}
void setId(int id) {
this.id = id;
}
void setItemsCount(int itemsCount) {
this.itemsCount = itemsCount;
}
}
I would like to build a Deckbuilder that allows you to save created decks locally on the device.
The Decklist are stored in Arraylists, called TransferDeck. Which I would like to store in room database. My issue is, that I do not know how to populate my database correctly, with the data comming out of the Arraylist.
I am used to working with Arraylist and below you see my try for storing the data:
So this is what I tried but what sadly does not work:
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();
}
Below you can find the rest of my code, but the above one should be enough to make clear what I want to do...
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();
}
The last class is a fragment, where I try to populate my database, and in the populateDB() class is the issue
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();
}
}
I like to mention that this should be a comment rather than an answer.
First, either use AysncTask or use more robust Executors.newSingleThreadExecutor(). If you prefer the second one then it's best if you create a helper class (example). Example:
private void populateDB(final List<TransferDeck> mTransferDeck) {
AppExecutors.diskIO().execute(() -> {
for(int i = 0; i<mTransferDeck.size(); i++){
mSavedDecksDB.deckBuilderDao().insertCards(new SaveDeck(...);
}
});
}
(1) Create a blank constructor.
(4) Room Database should not be initialized there and it's best if it's singleton. So the your database class (3) can be like:
public abstract class SaveDecksDataBase extends RoomDatabase {
private static SaveDecksDataBase sINSTANCE;
private static final Object LOCK = new Object();
public static SaveDecksDataBase getDatabase(final Context context) {
if (sINSTANCE == null) {
synchronized (LOCK) {
if (sINSTANCE == null) {
sINSTANCE = Room.databaseBuilder(context.getApplicationContext(),
SaveDecksDataBase.class, "SavedDecksDB.db")
.build();
}
}
}
return sINSTANCE;
}
public abstract DeckBuilderDao deckBuilderDao();
}
Lastly, to get SaveDeck object you also has to use Executors or AsyncTask to do the work in background, and then populate the RecyclerView.
Android Room Database
Practice set
Working with room persistence, when trying to get the database to insert or make select in the items, the error appears:
AppDataBase.getMovieDao()' on a null object reference
The classes related to the process are as follows:
AppDataBase class:
#Database(entities = {Movies.class}, version = 1)
public abstract class AppDataBase extends RoomDatabase {
public static final String DB_NAME = "Movies";
private static AppDataBase INSTANCE;
public static AppDataBase getDataBase(Context context){
if (INSTANCE == null){
INSTANCE = Room.databaseBuilder(context.getApplicationContext(),AppDataBase.class,DB_NAME).build();
}
return INSTANCE;
}
public abstract MovieDao getMovieDao();
}
Dao Class:
#Dao
public interface MovieDao {
#Insert
void insertAll(Movies movies);
#Update
void updateAll(Movies... notes);
#Query("SELECT * FROM moviestb")
List<Movies> getAll();
#Delete
void deleteAll(Movies... notes);
}
Entity class:
#Entity(tableName = "moviestb")
public class Movies {
#PrimaryKey(autoGenerate = true)
#ColumnInfo(name = "idmovie")
private long id;
#ColumnInfo(name = "titlemovie")
private String titlemovie;
........
}
Searching for the registrations:
public void loadFromDB(){
db.getDataBase(view.getContext());
if(db.getMovieDao().getAll().size() > 0){
adapter.setResults(db.getMovieDao().getAll());
}else{
Toast.makeText(view.getContext(),"Não há filmes cadastrados",Toast.LENGTH_SHORT).show();
view.getActivity().finish();
}
}
Insert:
public View.OnClickListener onSaveClick(final String plot, final String diretor, final String autor,
final String nome, final String tipo, final String ano, final String ator, final String imdb) {
return new View.OnClickListener() {
#Override
public void onClick(View itemView) {
db.getDataBase(view.getContext());
Movies movies = new Movies(nome,plot,imdb,"",ator,ano,tipo,diretor,autor);
new InsertAsyncTask(db).execute(movies);
}
};
}
private class InsertAsyncTask extends AsyncTask<Movies,Void,Void>{
private AppDataBase db;
public InsertAsyncTask(AppDataBase appDataBase) {
db = appDataBase;
}
#Override
protected Void doInBackground(Movies... params) {
db.getMovieDao().insertAll(params[0]);
return null;
}
}
Searching all:
public void loadFromDB(){
db.getDataBase(view.getContext());
if(db.getMovieDao().getAll().size() > 0){
adapter.setResults(db.getMovieDao().getAll());
}else{
Toast.makeText(view.getContext(),"Não há filmes cadastrados",Toast.LENGTH_SHORT).show();
view.getActivity().finish();
}
}
The crash occurs while fetching the database, what am I doing wrong? thank you!
db is null, apparently. Your question does not show where you ever assign it a value.
Please note that getDataBase() is a static method. Perhaps you should have a db=AppDataBase.getDataBase() statement somewhere.
I'm testing out the model layer of my application and I want to add an element to a list. But whenever I try to add some data into my data model the application crashes. I cannot find the reason for this.
My code for the data model.
public class DataModel {
private List<Log> logs;
private static DataModel instance;
private Context ctx;
//Singleton constructor
private DataModel()
{
//This makes it crash
logs.add(new Log("1234","sms", 123545, 1, 0));
//Load logs from database - Not done yet.
}
public static DataModel getInstance()
{
if (instance == null)
{
//Creates the instance
instance = new DataModel();
}
return instance;
}
My code for log
public class Log {
private String phonenumber;
private String type;
private long date;
private int incoming;
private int outgoing;
private long id;
//Constructor for incoming sms or call
public Log( String Phonenumber, String Type, long Date, int Incoming, int Outgoing)
{
this.phonenumber = Phonenumber;
this.type = Type;
this.date = Date;
this.incoming = Incoming;
this.outgoing = Outgoing;
}
public long getId()
{
return id;
}
public void setId(long id)
{
this.id = id;
}
public String getPhonenumber()
{
return phonenumber;
}
public void setPhonenumer(String phonenumber)
{
this.phonenumber = phonenumber;
}
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public long getDate()
{
return date;
}
public void setDate(long date)
{
this.date = date;
}
public int getIncoming()
{
return incoming;
}
public void setIncoming(int incoming)
{
this.incoming = incoming;
}
public int getOutgoing()
{
return outgoing;
}
public void setOutgoing (int outgoing)
{
this.outgoing = outgoing;
}
You are not initializing logs. Its null when you execute this statement:
logs.add(new Log("1234","sms", 123545, 1, 0));
Change:
private List<Log> logs;
to:
private List<Log> logs = new ArrayList<Log>();
I see a context in your code, but you don't set it or use it anywhere so maybe you stripped part of your code. In relation to that, if you use it to UI related stuff (and some other cases) I can guarantee you that it will crash your app if you don't reset it every time the screen orientation changes or you change activities.
You have not Instantiated list object
private List<Log> logs;
Update your constructor to this
//Singleton constructor
private DataModel()
{
//This makes it crash
logs = new ArrayList<Log>();
logs.add(new Log("1234","sms", 123545, 1, 0));
//Load logs from database - Not done yet.
}
Now every time you constructor gets called you will get a fresh copy of list object.
Initialize the List before use
you can initialize the List in Constructor as well
public class DataModel {
private List<Log> logs= new ArrayList<Log>();
private static DataModel instance;
private Context ctx;
//Singleton constructor
private DataModel()
{
//This makes it crash
logs.add(new Log("1234","sms", 123545, 1, 0));
int i=0;
//Load logs from database - Not done yet.
}
public static DataModel getInstance()
{
if (instance == null)
{
//Creates the instance
instance = new DataModel();
}
return instance;
}
}
Don't initialize globally logs and also use synchronized getInstance method so that only one instance should get created if two threads are trying to access at the same time.
Use this code:
public class DataModel {
private List<Log> logs;
private static DataModel instance;
private Context ctx;
//Singleton constructor
private DataModel()
{
if(logs == null){
logs = new ArrayList<Log>();
}
logs.add(new Log("1234","sms", 123545, 1, 0));
//Load logs from database - Not done yet.
}
public synchronized static DataModel getInstance()
{
if (instance == null)
{
//Creates the instance
instance = new DataModel();
}
return instance;
}
I am trying to save the arraylist of class objects into the ormlite database , but it is giving the error , java.lang.IllegalArgumentException: No fields have a DatabaseField annotation in class java.util.ArrayList
my code is
#DatabaseTable
public class ManageModelDetails {
#DatabaseField(generatedId = true)
private int id;
#DatabaseField(foreign = true, foreignAutoRefresh = true)
private ArrayList<ModelDetails> listModelDetails;
// ===============================================
public ManageModelDetails() {
super();
}
// ===============================================
public ManageModelDetails(int id, ArrayList<ModelDetails> listModelDetails) {
super();
this.id = id;
this.listModelDetails = listModelDetails;
}
// ===============================================
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setModelList(ArrayList<ModelDetails> listModelDetails) {
this.listModelDetails = listModelDetails;
}
public ArrayList<ModelDetails> getModelList() {
return listModelDetails;
}
}
I think you need to use Foreign Collections. Take a look here:
Foreign Collections
Another similar question
If you want to save an ArrayList of objects to ORMLite the easiest way is this:
#DatabaseField(dataType = DataType.SERIALIZABLE)
private SerializedList<MyObject> myObjects;
and to get my list of objects:
public List<MyObject> getMyObjects() {
return myObjects;
}
and here is SerializedList:
public class SerializedList<E> extends ArrayList<E> implements Serializable {
}