简体   繁体   中英

Unable to insert data to Room database

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?

Your code (less use of Subscirptions and getAllApaces) ie 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 documentation saying

The AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.

Here's all of the tables when using autoGenerate=true (again using App Inspection)

在此处输入图像描述

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 (eg duplicate) then the value for that insert would be -1.

With the suggested changes then App Inspection shows:-

在此处输入图像描述

ie no noticeable difference, the data has been added (app was uninstalled deleting the entire database)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM