I am trying to insert values to room database but it's not working, I checked the database, and the tables were not created. I have created the database, DAO and Repository in java and calling the insert dao inside a coroutine in MainActivity Kotlin class.
DAO
@Insert
public void addExpense(List<Expenses> exp);
Repository
public class Repository {
private ExpensesDao expensesDao;
private SubscriptionsDao subscriptionsDao;
private static AppDatabase db;
public Repository(Context context) {
initDb(context);
expensesDao = db.expensesDao();
subscriptionsDao = db.subscriptionsDao();
}
private static void initDb(Context context) {
if (db == null) {
db = Room.databaseBuilder(
context,
AppDatabase.class, "local_db"
)
.addMigrations(AppDatabase.DbMigration)
.build();
}
}
public void addExpense(List<Expenses> exp) {
expensesDao.addExpense(exp);
}
}
MainActivity.kt
class MainActivity : AppCompatActivity() {
private var firstRun = true
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val db = Repository(applicationContext)
var spacesList: List<String> = listOf("No Spaces Found")
var expList: List<Expenses> = listOf(
Expenses("dummy", LocalDate.now().toString(), 12.22, "dummy1"),
Expenses("dummy", LocalDate.now().toString(), 12.22, "dummy2"),
Expenses("dummy", LocalDate.now().toString(), 13.22, "dummy3"),
Expenses("dummy", LocalDate.now().toString(), 14.22, "dummy4"),
Expenses("dummy", LocalDate.now().toString(), 15.22, "dummy5"),
Expenses("dummy", LocalDate.now().toString(), 16.22, "dummy6")
)
CoroutineScope(Dispatchers.IO).launch {
// the insert call
val x = db.addExpense(expList)
println("-->> " x.toString())
}
val tempSpacesList = db.getAllSpaces().value
if (tempSpacesList?.isEmpty() == true) {
spacesList = tempSpacesList
}
}
}
Edit
@Entity
public class Expenses {
public Expenses(String space, String date, double amount, String description) {
this.uid = uid;
this.space = space;
this.date = date;
this.amount = amount;
this.description = description;
}
@PrimaryKey(autoGenerate = true)
int uid;
@ColumnInfo(name = "space")
String space;
@ColumnInfo(name = "date")
String date;
@ColumnInfo(name = "amount")
double amount;
@ColumnInfo(name = "description")
String description;
}
Logcat (not much here..)
15:11:55.340 E Could not remove dir '/data/data/org.android.app/code_cache/.ll/': No such file or directory
- Is this the right way of using insert dao?
- What can I improve on this implementation?
CodePudding user response:
Your code (less use of Subscirptions and getAllApaces) i.e. MainActivity being:-
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val db = Repository(applicationContext)
var spacesList: List<String> = listOf("No Spaces Found")
var expList: List<Expenses> = listOf(
Expenses("dummy", LocalDate.now().toString(), 12.22, "dummy1"),
Expenses("dummy", LocalDate.now().toString(), 12.22, "dummy2"),
Expenses("dummy", LocalDate.now().toString(), 13.22, "dummy3"),
Expenses("dummy", LocalDate.now().toString(), 14.22, "dummy4"),
Expenses("dummy", LocalDate.now().toString(), 15.22, "dummy5"),
Expenses("dummy", LocalDate.now().toString(), 16.22, "dummy6")
)
CoroutineScope(Dispatchers.IO).launch {
// the insert call
val x = db.addExpense(expList)
println("-->> " x.toString())
}
/*
val tempSpacesList = db.getAllSpaces().value
if (tempSpacesList?.isEmpty() == true) {
spacesList = tempSpacesList
}
*/
}
}
Works with AppInspection showing:-
Perhaps your issue is with how you are trying to see if tables exist. Have you used App Inspection (and waited for it to do it's business)?
The following assumptions have been made:
- AppDatabase is Java as per
:-
@Database(entities = {Expenses.class}, exportSchema = false, version = 1)
abstract class AppDatabase extends RoomDatabase {
abstract ExpensesDao expensesDao();
}
- The dependencies, for room are use are (as per the module build.gradle)
:-
kapt 'androidx.room:room-compiler:2.5.0'
implementation 'androidx.room:room-ktx:2.5.0'
implementation 'androidx.room:room-runtime:2.5.0'
annotationProcessor 'androidx.room:room-compiler:2.5.0'
- with the plugins section including
:-
id 'kotlin-kapt'
Is this the right way of using insert dao?
Yes
What can I improve on this implementation?
You could eradicate the inefficient use of autoGenerate=true
, which doesn't actually cause auto generation but instead adds the AUTOINCREMENT
keyword, but accepts 0 as not being 0 rather interpreting the value of 0 as no value and hence having the value generated.
That is you could use:-
@Entity
public class Expenses {
public Expenses(String space, String date, double amount, String description) {
this.space = space;
this.date = date;
this.amount = amount;
this.description = description;
}
@PrimaryKey
Integer uid= null; /* <<<<<<<< now Inetger (object not primitive) so can be null
....
AUTOINCREMENT aka autoGenerate=true
introduces a constraint (rule) that forces a generated value to be greater than any ever used and a per table basis. Without AUTOINCRMENT the generated value will be 1 greater than the current maximum value, thus allowing the reuse of values of rows that have been deleted.
The inefficiency is that to know the values of rows that have existed that SQLite creates a table named sqlite_sequence which stores the highest allocated value. This additional table needs to be read and updated whenever generating a value and hence the
With the suggested removal of autoGenerate=true
then
Another improvement is that instead of int or Integer, it is more correct to use long or Long for such columns (INTEGER column type and the PRIMARY KEY) and thus a generated value if not value is provided when inserting. The value can exceed what an int/Integer can hold (max 2147483647) as SQLite stores up to a 64bit signed integer (9223372036854775807).
- with AUTOINCREMENT when this massive value has been reached the next insert would fail with an SQLITE_FULL error. Without then an unused value would be applied if available.
Additionally the actual generated value can be very useful to have as it uniquely identifies the row (especially so if using relationships between tables). You can get this value when inserting a row or rows but it will be returned as a long not an Int. You can also retrieve an array of values when inserting multiple rows again longs not ints. So you could have:-
@Insert
public long[] addExpense(List<Expenses> exp); /* or Long[] */
- If using
@Insert(onConflict = OnConflictStrategy.IGNORE)
then if a row were IGNOREd due to an ignorable conflict (e.g. duplicate) then the value for that insert would be -1.
With the suggested changes then App Inspection shows:-
i.e. no noticeable difference, the data has been added (app was uninstalled deleting the entire database)