Android房间。哪种插入方法会更快?
Android Room. Which method of insertion will be faster?
我使用 Room 作为 SQLite 的抽象层。看完这个 page 我发现我们可以同时插入多个对象。目前我使用 For 循环来插入对象,即在每个 For 循环迭代中插入一个对象。我目前知道的两种插入方式是:
使用 For 循环并一次插入一个对象
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void addActivity(Person person);
正在插入数组或对象列表。
@Insert(onConflict = OnConflictStrategy.REPLACE)
public void insertUsers(Person ... people);
我在写对象插入的时候,并不知道第二种插入方式。现在我想知道这两种方式之间的速度是否有明显差异,以便我可以更改我的代码以提高我的应用程序的性能。
幕后生成的 类 房间正在使用 EntityInsertionAdapter
来应对这种特殊情况。还有两个方法,我们需要检查:
这个用于插入单个实体
public final long insertAndReturnId(T entity) {
final SupportSQLiteStatement stmt = acquire();
try {
bind(stmt, entity);
return stmt.executeInsert();
} finally {
release(stmt);
}
}
而这个是用来插入实体数组的
public final void insert(Iterable<T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
for (T entity : entities) {
bind(stmt, entity);
stmt.executeInsert();
}
} finally {
release(stmt);
}
}
如您所见,内部结构与您的几乎相同 - stmt.executeInsert();
被调用一次或在循环中调用。我能想到的使用 insertUsers
方法的唯一性能变化是更改通知,当所有用户都被插入时,它只会发生一次。但是,如果您已经在用 @Transaction
包裹的循环中插入,那么就不会有任何变化。
按照 OP 在对他们问题的评论中的要求,这里是(为了清楚起见,作为答案)我为检查性能所做的工作:
之前,一个一个插入对象:
@Dao
abstract class MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(items: MyObject): Long
// ...
}
正在同步代码:
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)
}
}
这在华为 P10+ 上花了一分钟。
我将其更改为:
@Dao
abstract class MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(items: Iterable<MyObject>)
// ...
}
正在同步代码:
val response = backend.downloadItems() // download from server
val items = response.getData() // this is a List<MyObject>
response.getData()?.let { myDao.insert(it) }
这用了不到一秒钟。
这里的重点是专门使用 Iterable<>
版本的 DAO @Insert
方法,正如@iDemigod 所说,使用 Iterable<>
版本的 EntityInsertionAdapter
.
所述函数的主体在@iDemigod 的回答中,它使用一个准备好的语句进行所有插入。
将 SQL 解析成一个语句是昂贵的,并且使用一个语句为整个插入批处理创建一个事务,这可以帮助解决其他问题(我在数据库上有一个可观察的 LiveData<>
,在插入期间被通知了 12k 次...性能是 糟糕)。
我使用 Room 作为 SQLite 的抽象层。看完这个 page 我发现我们可以同时插入多个对象。目前我使用 For 循环来插入对象,即在每个 For 循环迭代中插入一个对象。我目前知道的两种插入方式是:
使用 For 循环并一次插入一个对象
@Insert(onConflict = OnConflictStrategy.REPLACE) public void addActivity(Person person);
正在插入数组或对象列表。
@Insert(onConflict = OnConflictStrategy.REPLACE) public void insertUsers(Person ... people);
我在写对象插入的时候,并不知道第二种插入方式。现在我想知道这两种方式之间的速度是否有明显差异,以便我可以更改我的代码以提高我的应用程序的性能。
幕后生成的 类 房间正在使用 EntityInsertionAdapter
来应对这种特殊情况。还有两个方法,我们需要检查:
这个用于插入单个实体
public final long insertAndReturnId(T entity) { final SupportSQLiteStatement stmt = acquire(); try { bind(stmt, entity); return stmt.executeInsert(); } finally { release(stmt); } }
而这个是用来插入实体数组的
public final void insert(Iterable<T> entities) { final SupportSQLiteStatement stmt = acquire(); try { for (T entity : entities) { bind(stmt, entity); stmt.executeInsert(); } } finally { release(stmt); } }
如您所见,内部结构与您的几乎相同 - stmt.executeInsert();
被调用一次或在循环中调用。我能想到的使用 insertUsers
方法的唯一性能变化是更改通知,当所有用户都被插入时,它只会发生一次。但是,如果您已经在用 @Transaction
包裹的循环中插入,那么就不会有任何变化。
按照 OP 在对他们问题的评论中的要求,这里是(为了清楚起见,作为答案)我为检查性能所做的工作:
之前,一个一个插入对象:
@Dao
abstract class MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(items: MyObject): Long
// ...
}
正在同步代码:
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)
}
}
这在华为 P10+ 上花了一分钟。
我将其更改为:
@Dao
abstract class MyDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
abstract fun insert(items: Iterable<MyObject>)
// ...
}
正在同步代码:
val response = backend.downloadItems() // download from server
val items = response.getData() // this is a List<MyObject>
response.getData()?.let { myDao.insert(it) }
这用了不到一秒钟。
这里的重点是专门使用 Iterable<>
版本的 DAO @Insert
方法,正如@iDemigod 所说,使用 Iterable<>
版本的 EntityInsertionAdapter
.
所述函数的主体在@iDemigod 的回答中,它使用一个准备好的语句进行所有插入。
将 SQL 解析成一个语句是昂贵的,并且使用一个语句为整个插入批处理创建一个事务,这可以帮助解决其他问题(我在数据库上有一个可观察的 LiveData<>
,在插入期间被通知了 12k 次...性能是 糟糕)。