1

I have implemented the Room database in Application. I want to prepopulate the data when app is first opened. The Entity class looks like

@Entity(tableName = NameConstants.TABLE_NAME)
public class Name {

    public int getId() {
        return id;
    }

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

    @PrimaryKey(autoGenerate = true)
    private int id;

    @ColumnInfo(name = NameConstants.COL_NAME)
    private String Name;

    @ColumnInfo(name= NameConstants.COL_PHONE)
    private String Phone;

    @ColumnInfo(name = NameConstants.COL_CITY)
    private String City;

    public Name(){

    }

    public Name(String name, String phone, String city) {
        Name = name;
        Phone = phone;
        City = city;
    }

    public String getName() {
        return Name;
    }

    public void setName(String name) {
        Name = name;
    }

    public String getPhone() {
        return Phone;
    }

    public void setPhone(String phone) {
        Phone = phone;
    }

    public String getCity() {
        return City;
    }

    public void setCity(String city) {
        City = city;
    }
}  

The DAO implementation is

@Dao
public interface NameDAO {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insertAll(Name... names);

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(Name name);

    @Delete
    void delete(Name name);
}  

The Database class seems to be like this where data is read from one Raw file

@Database(entities = {Name.class},version = 1,exportSchema = false)
public abstract  class NameDatabase extends RoomDatabase {

    public abstract NameDAO nameDAO();

    private static volatile  NameDatabase dInstance=null;

     static synchronized NameDatabase getInstance(final Context context){
        if(dInstance==null){
            synchronized (NameDatabase.class){
                if(dInstance==null){
                    dInstance= Room.databaseBuilder(context.getApplicationContext(),NameDatabase.class,"appdatabase")
                            .allowMainThreadQueries()
                            .addCallback(new Callback() {
                                @Override
                                public void onCreate(@NonNull SupportSQLiteDatabase db) {
                                    super.onCreate(db);
                                   fillWithDemoData(context);
                                }

                            }
                            ).build();
                }
            }
        }
        return dInstance;
    }

    @WorkerThread
    private static void fillWithDemoData(Context context){
        NameDAO dao=getInstance(context).nameDAO();
        JSONArray emoji = loadJsonArray(context);
        try {
            for (int i = 0; i < emoji.length(); i++) {
                JSONObject item = emoji.getJSONObject(i);
                dao.insert(new Name(item.getString("name"),
                        item.getString("phone"),
                        item.getString("city")));
            }
        } catch (JSONException exception) {
            exception.printStackTrace();
        }
    }
    private static JSONArray loadJsonArray(Context context) {
        StringBuilder builder = new StringBuilder();
        InputStream in = context.getResources().openRawResource(R.raw.namesdata);
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        String line;
        try {
            while ((line = reader.readLine()) != null) {
                builder.append(line);
            }
            JSONObject json = new JSONObject(builder.toString());
            return json.getJSONArray("names");

        } catch (IOException | JSONException exception) {
            exception.printStackTrace();
        }

        return null;
    }
}  

The Repository class is public

class DataRepository {

    private NameDAO nDao;

    public DataRepository(Application application) {
        NameDatabase database = NameDatabase.getInstance(application);
        nDao = database.nameDAO();
    }
}  

When app is install and open app works fine, no crash but no Database files are created and no data is inserted in database. What else is missing or anything wrong in code ?

Satyam Gondhale
  • 1,415
  • 1
  • 16
  • 43

1 Answers1

3

When app is install and open app works fine, no crash but no Database files are created and no data is inserted in database.

If you are getting no crash then I believe that you are not doing anything else with the database. Just building it and getting the instance.

The database is not created until you try to access the database. You need to try to do something with the database.

However!!!!!

You will then get an java.lang.IllegalStateException: getDatabase called recursively FATAL exception.

Basically by acessing the database you are then trying to open it, as the database doesn't exist the onCreate callback is invoked and the attemtp to access it via the insert will then try to open the database hence the recursion.

In short you can only access the database via the SupportSQLiteDatabase as passed to onCreate.

You could use :-

public static void fillWithDemoData(Context context, SupportSQLiteDatabase sdb){

    ContentValues cv = new ContentValues(); //<<<<<<<<<< ADDED


    JSONArray emoji = loadJsonArray(context);
    try {
        for (int i = 0; i < emoji.length(); i++) {
            JSONObject item = emoji.getJSONObject(i);

            //<<<<<<<<<< CANNOT USE ROOM here >>>>>>>>>
            //dao.insert(new Name(item.getString("name"),
            //        item.getString("phone"),
            //        item.getString("city")));


            cv.clear();
            cv.put(NameConstants.COL_NAME,item.getString(NameConstants.COL_NAME));
            cv.put(NameConstants.COL_CITY,item.getString(NameConstants.COL_CITY));
            cv.put(NameConstants.COL_PHONE,item.getString(NameConstants.COL_PHONE));
            sdb.insert(NameConstants.TABLE_NAME, OnConflictStrategy.IGNORE,cv);
        }
    } catch (JSONException exception) {
        exception.printStackTrace();
    }

}

BUT you need to attempt to access ROOM e.g. so in you activity or equivalent you could use :-

    ndb = NameDatabase.getInstance(this); //<<<<<<<<<< ROOM databse not yet created (opened)
    ndb.getOpenHelper(); //<<<<<<<<<< FORCE OPEN and thus create (or just access the db via a Dao call)

Example/Demo/Test

As I don't have access to you JSON file then I have tested the above using :-

@WorkerThread
public static void fillWithDemoData(Context context, SupportSQLiteDatabase sdb){

    ContentValues cv = new ContentValues(); //<<<<<<<<<< ADDED
    cv.clear();
    cv.put(NameConstants.COL_NAME,"Fred");
    cv.put(NameConstants.COL_CITY,"London");
    cv.put(NameConstants.COL_PHONE,"0000000000");
    sdb.insert(NameConstants.TABLE_NAME, OnConflictStrategy.IGNORE,cv);

    /*
    JSONArray emoji = loadJsonArray(context);
    try {
        for (int i = 0; i < emoji.length(); i++) {
            JSONObject item = emoji.getJSONObject(i);

            //<<<<<<<<<< CANNOT USE ROOM here >>>>>>>>>
            //dao.insert(new Name(item.getString("name"),
            //        item.getString("phone"),
            //        item.getString("city")));


            cv.clear();
            cv.put(NameConstants.COL_NAME,item.getString(NameConstants.COL_NAME));
            cv.put(NameConstants.COL_CITY,item.getString(NameConstants.COL_CITY));
            cv.put(NameConstants.COL_PHONE,item.getString(NameConstants.COL_PHONE));
            sdb.insert(NameConstants.TABLE_NAME, OnConflictStrategy.IGNORE,cv);
        }
    } catch (JSONException exception) {
        exception.printStackTrace();
    }
    */
}
  • also commented out the code in loadJsonArray

And the activity used was :-

public class MainActivity extends AppCompatActivity {

    NameDatabase ndb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ndb = NameDatabase.getInstance(this);
        ndb.getOpenHelper(); //<<<<<<<<<< FORCE OPEN and thus create (not needed as db accessed below)

        //This would also do the same, this used to test the data//
        List<Name> nameList = ndb.nameDAO().getALl();
        for (Name n: nameList) {
            logName(n);
        }
    }

    private void logName(Name n) {
        Log.d("NAMEINFO","Name is " + n.getName() + "\n\tCity is " + n.getCity() + "\n\tPhone is " + n.getPhone() + "ID is " + n.getId());
    }
}

Results

First run after install :-

2019-11-14 08:22:08.483 D/ONCREATE: onCreate has been called
2019-11-14 08:22:08.492 D/NAMEINFO: Name is Fred
      City is London
      Phone is 0000000000ID is 1
  • Not log added to onCreate so it shows that it was invoked.

Second run :-

2019-11-14 08:23:22.532 D/NAMEINFO: Name is Fred
      City is London
      Phone is 0000000000ID is 1
MikeT
  • 51,415
  • 16
  • 49
  • 68