I am using Room as an abstraction layer over SQLite. After reading this page I found out that we can insert multiple objects at the same time. Currently I use a For loop to insert objects, i.e one object in each For loop iteration. The two ways of inserting that I know of currently are:
Using a For loop and inserting each object one at a time
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void addActivity(Person person);
Inserting an array or a list of objects.
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(Person ... people);
When I was coding the insertion of objects, I did not know of the second way of insertion. Now I want to know if there is any noticeable difference in speeds between the two ways so that I can change my code to increase performance of my app.
androidx.room.Insert. Marks a method in a Dao annotated class as an insert method. The implementation of the method will insert its parameters into the database. All of the parameters of the Insert method must either be classes annotated with Entity or collections/array of it.
android.arch.persistence.room.Dao. Marks the class as a Data Access Object. Data Access Objects are the main classes where you define your database interactions. They can include a variety of query methods. The class marked with @Dao should either be an interface or an abstract class.
Room vs SQLiteRoom provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite. In the case of SQLite, There is no compile-time verification of raw SQLite queries. But in Room, there is SQL validation at compile time.
Is Android Room an ORM? Room isn't an ORM; instead, it is a whole library that allows us to create and manipulate SQLite databases more easily. By using annotations, we can define our databases, tables, and operations.
As requested by OP in a comment of their question, here's (for the sake of clarity, as an answer) what I did to check the performance:
Before, inserting objects one by one:
@Dao
abstract class MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(items: MyObject): Long
// ...
}
Syncing code:
val response = backend.downloadItems() // download from server
val items = response.getData() // this is a List<MyObject>
if (items != null) {
for (i in items) {
myDao.persist(s)
}
}
This took a minute on a Huawei P10+.
I changed this to:
@Dao
abstract class MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(items: Iterable<MyObject>)
// ...
}
Syncing code:
val response = backend.downloadItems() // download from server
val items = response.getData() // this is a List<MyObject>
response.getData()?.let { myDao.insert(it) }
This took less than a second.
The point here is to use specifically the Iterable<>
version of the DAO @Insert
method, which as said by @iDemigod, uses the Iterable<>
version of the EntityInsertionAdapter
.
The body of said function is in @iDemigod's answer, and it uses a single prepared statement for all the insertions.
Parsing the SQL into a statement is expensive, and using a statement creates a transaction for the whole insert batch, which can help solve other issues (I had an observable LiveData<>
on the database, which was notified 12k times during the insert... performance was awful).
Under the hood Room generated classes are using EntityInsertionAdapter
for this particular situation. And there is two methods, we need to check:
This one is used for inserting a single entity
public final long insertAndReturnId(T entity) {
final SupportSQLiteStatement stmt = acquire();
try {
bind(stmt, entity);
return stmt.executeInsert();
} finally {
release(stmt);
}
}
While this one is used to insert an array of entities
public final void insert(Iterable<T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
for (T entity : entities) {
bind(stmt, entity);
stmt.executeInsert();
}
} finally {
release(stmt);
}
}
AS you can see the internals are pretty much the same as yours - stmt.executeInsert();
is called once or in the loop. The only performance change using the insertUsers
method I can think of is the change notification, which will happen only once, when all the users will be inserted. But if you're already doing you insertion in the loop wrapped with @Transaction
then there would be no change.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With