简体   繁体   English

无法将数据插入 Room 数据库

[英]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.我在 java 中创建了数据库、DAO 和存储库,并在 MainActivity Kotlin class 中的协程中调用插入 dao。

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主活动.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..) Logcat(这里不多..)

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?这是使用 insert dao 的正确方法吗?
  • What can I improve on this implementation?我可以在此实施上改进什么?

Your code (less use of Subscirptions and getAllApaces) ie MainActivity being:-您的代码(较少使用 Subscirptions 和 getAllApaces)即 MainActivity 是:-

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:-与 AppInspection 一起显示:-

在此处输入图像描述

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)?您是否使用过 App Inspection(并等待它开展业务)?

The following assumptions have been made:做出了以下假设:

  • AppDatabase is Java as per AppDatabase 为 Java

:- :-

@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)房间的依赖项是(根据模块 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?这是使用 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.您可以消除autoGenerate=true的低效使用,它实际上不会导致自动生成,而是添加AUTOINCREMENT关键字,但接受 0 不是 0,而是将 0 的值解释为无值,从而生成值。

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. AUTOINCREMENT又名autoGenerate=true引入了一个约束(规则),强制生成的值大于任何曾经使用过的值和每个表的基础。 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.如果没有 AUTOINCRMENT,生成的值将比当前最大值大 1,从而允许重用已删除的行的值。

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.效率低下是为了知道已存在的行的值 SQLite 创建了一个名为 sqlite_sequence 的表,该表存储最高分配的值。 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. AUTOINCREMENT 关键字强加额外的 CPU、memory、磁盘空间和磁盘 I/O 开销,如果不是严格需要,应避免使用。 It is usually not needed.通常不需要。

Here's all of the tables when using autoGenerate=true (again using App Inspection)这是使用autoGenerate=true时的所有表格(再次使用 App Inspection)

在此处输入图像描述

With the suggested removal of autoGenerate=true then然后建议删除autoGenerate=true

在此处输入图像描述

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.另一个改进是,对于此类列(INTEGER 列类型和 PRIMARY KEY),使用 long 或 Long 代替 int 或 Integer 更正确,因此在插入时如果不提供值,则生成一个值。 The value can exceed what an int/Integer can hold (max 2147483647) as SQLite stores up to a 64bit signed integer (9223372036854775807).该值可以超过 int/Integer 可以容纳的值(最大 2147483647),因为 SQLite 存储最多 64 位签名的 integer(9223372036854775807)。

  • with AUTOINCREMENT when this massive value has been reached the next insert would fail with an SQLITE_FULL error.使用 AUTOINCREMENT 当达到这个巨大的值时,下一个插入将失败并出现 SQLITE_FULL 错误。 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.您可以在插入一行或多行时获取此值,但它将作为 long 而不是 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.如果使用@Insert(onConflict = OnConflictStrategy.IGNORE)那么如果一行由于可忽略的冲突(例如重复)而被忽略,那么该插入的值将为 -1。

With the suggested changes then App Inspection shows:-根据建议的更改,App Inspection 显示:-

在此处输入图像描述

ie no noticeable difference, the data has been added (app was uninstalled deleting the entire database)即没有明显的区别,数据已添加(应用程序已卸载删除整个数据库)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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