How to insert a “one to one” room database

Solution for How to insert a “one to one” room database
is Given Below:

I am trying to insert an object with a one to one relationship in android.
To be more precise i am trying to follow the example from
googles official documentation

I am using the two classes User and Library.
The problem i have is how to insert the elements in database.
My code is written like this

  @Entity
data class Library(
    @PrimaryKey(autoGenerate = true) val libraryId: Long = 0,
    val userOwnerId: Long
)


@Entity
data class User(
    @PrimaryKey(autoGenerate = true) val userId: Long =0,
    val name: String,
    val age: Int
) 

data class UserAndLibrary(
    @Embedded val user: User,
    @Relation(
        parentColumn = "userId",
        entityColumn = "userOwnerId"
    )
    val library: Library
)

and the logic for inserting looks like this

private val repository = UserRepository(application)

    fun insertUser() {
        val user1 = User(name = "User1", age = 31)
        val user2 = User(name = "User2", age = 32)
        val library1 = Library(userOwnerId = user1.userId)
        val library2 = Library(userOwnerId = user2.userId)

        viewModelScope.launch {
            repository.insertUser(user1)
            
            repository.insertUser(user2)
            
            repository.insertLibrary(library1)
            
            repository.insertLibrary(library2)
         
        }

and the code for repository and dao class look like this

//repository
suspend fun insertUser(user: User) = appDataBase.userDao().insertUser(user)

suspend fun insertLibrary(library: Library)=appDataBase.userDao().insertLibrary(library)

//dao
@Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertUser(user: User)

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertLibrary(library: Library)

The problem is that the foreign key in the library table is always 0.

I believe that your issue is due to how you are inserting the Library data. More specifically when you insert a Library are you setting the value for the Library userOwnerId? (I believe not or are setting it to 0).

Consider the following that uses your classes (User, Library and userAndLibrary) as per the question with the following @Dao and a pretty standard @Database :-

@Dao
abstract class AllDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun insert(library: Library): Long
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    abstract fun insert(user: User): Long
    @Query("SELECT * FROM user")
    abstract fun getAllUsers(): List<User>
    @Query("SELECT * FROM library")
    abstract fun getAllLibraries(): List<Library>
    @Query("SELECT * FROM user")
    abstract fun getUsersAndThierLibrary(): List<UserAndLibrary>
}
  • NOTE that inserts return Long (i.e. the id of the inserted row)

In conjunction with the above the code in an activity (run on main thread for convenience and brevity):-

    db = TheDatabase.getInstance(this)
    dao = db.getAllDao()

    /* Explanatory Insert */
    var firstUser = User(name = "Susan",age = 20)
    var firstUserId = dao.insert(firstUser)
    dao.insert(Library(userOwnerId = firstUserId))

    /* More Concise Inserts */
    dao.insert(Library(userOwnerId = dao.insert(User(name ="Fred",age = 21))))
    dao.insert(Library(userOwnerId = dao.insert(User(name ="Mary",age= 19))))

    for (u: User in dao.getAllUsers()) {
        Log.d(TAG," User is ${u.name} ID is ${u.userId} age is ${u.age}")
    }
    for (l: Library in dao.getAllLibraries()) {
        Log.d(TAG,"Library is ${l.libraryId} owned by ${l.userOwnerId}")
    }
    for(ual: UserAndLibrary in dao.getUsersAndThierLibrary()) {
        Log.d(TAG,"User = ${ual.user.name} Library ID is ${ual.library.libraryId}")
    }
  • Again Note using the value returned (the userId) when insert the user.

As can be seen the above inserts 3 users and Libraries. It then extracts all the users, all the Libraries AND the UserAndLibrary’s. The result being :-

2021-07-31 12:29:41.888 D/MYINFO:  User is Susan ID is 1 age is 20
2021-07-31 12:29:41.889 D/MYINFO:  User is Fred ID is 2 age is 21
2021-07-31 12:29:41.889 D/MYINFO:  User is Mary ID is 3 age is 19
2021-07-31 12:29:41.890 D/MYINFO: Library is 1 owned by 1
2021-07-31 12:29:41.890 D/MYINFO: Library is 2 owned by 2
2021-07-31 12:29:41.890 D/MYINFO: Library is 3 owned by 3
2021-07-31 12:29:41.894 D/MYINFO: User = Susan Library ID is 1
2021-07-31 12:29:41.895 D/MYINFO: User = Fred Library ID is 2
2021-07-31 12:29:41.895 D/MYINFO: User = Mary Library ID is 3
  • i.e. everything is as expected.

This works pretty well, but i must admit that this is not so user friendly and not documented anywhere from google. The critic is directed towards google not you.
Is it possible to do something like this (c# and entity framework example)

var dept = new Department()
{
    Name = "Admin"
};
 
var emp = new Employee()
{
    Name = "Matt",
    Designation = "Head",
    Department = dept
};
 
using (var context = new CompanyContext())
{
    context.Add(emp);
    await context.SaveChangesAsync();
}

This would be a very intuitive way of using the framework library