我的目标
我想批量插入多个记录到sqlite中(事务性地)。
我的问题
我发现方法android.content.ContentResolver
。bulkInsert(..)很有趣,但javadoc声明:
此函数不保证插入的原子性。
为什么android会提供一种瘫痪的方法?你能说出用于非原子插入的用例吗?我显然会覆盖ContentProvider.bulkInsert(..)
以确保自己的原子性,所以我不确定为什么它是这样的短语。
答案 0 :(得分:4)
我们需要覆盖批量插入方法,如下面的内容......
public class Provider extends ContentProvider {
public static final Uri URI = Uri.parse("content://com.example.android.hoge/");
@Override
public String getType(Uri uri) {
return null;
}
@Override
public boolean onCreate() {
return false;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Helper helper = Helper.getInstance(getContext(), null);
SQLiteDatabase sdb = helper.getReadableDatabase();
Cursor cursor = sdb.query(
Table.TABLENAME,
new String[]{Table.ID, Table.DATA, Table.CREATED},
selection,
selectionArgs,
null,
null,
sortOrder,
null
);
return cursor;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
Helper helper = Helper.getInstance(getContext(), null);
SQLiteDatabase sdb = helper.getWritableDatabase();
sdb.insert(Table.TABLENAME, null, values);
getContext().getContentResolver().notifyChange(uri, null);
return uri;
}
/**
* super.bulkInsert is implemented the loop of insert without transaction
* So we need to override it and implement transaction.
*/
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
Helper helper = Helper.getInstance(getContext(), null);
SQLiteDatabase sdb = helper.getWritableDatabase();
sdb.beginTransaction();
SQLiteStatement stmt = sdb.compileStatement(
"INSERT INTO `" + Table.TABLENAME + "`(`" + Table.DATA + "`, `" + Table.CREATED + "`) VALUES (?, ?);"
);
int length = values.length;
for(int i = 0; i < length; i++){
stmt.bindString(1, values[i].getAsString(Table.DATA));
stmt.bindLong(2, values[i].getAsLong(Table.CREATED));
stmt.executeInsert();
}
sdb.setTransactionSuccessful();
sdb.endTransaction();
return length;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Helper helper = Helper.getInstance(getContext(), null);
SQLiteDatabase sdb = helper.getWritableDatabase();
int rows = sdb.update(Table.TABLENAME, values, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return rows;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Helper helper = Helper.getInstance(getContext(), null);
SQLiteDatabase sdb = helper.getWritableDatabase();
int rows = sdb.delete(Table.TABLENAME, selection, selectionArgs);
getContext().getContentResolver().notifyChange(uri, null);
return rows;
}
private static class Helper extends SQLiteOpenHelper {
static Helper INSTANCE = null;
private Helper(Context context, CursorFactory factory) {
super(context, Table.FILENAME, factory, Table.VERSION);
}
public static Helper getInstance(Context context, CursorFactory factory) {
if (INSTANCE == null) {
INSTANCE = new Helper(context, factory);
}
return INSTANCE;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(
"CREATE TABLE `" + Table.TABLENAME + "`(" +
" `" + Table.ID + "` INTEGER PRIMARY KEY AUTOINCREMENT," +
" `" + Table.CREATED + "` INTEGER," +
" `" + Table.DATA + "` TEXT" +
");"
);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
}
答案 1 :(得分:1)
改为使用applyBatch()。
这允许您以事务方式执行许多不同的操作,但是这种灵活性会受到性能影响。
相关文档可在ContentResolver SDK documentation
中找到我提供了一个关于在the symantics of backReferences
中使用applybatch的快速教程我还建议查看讨论覆盖applyBatch的this question
答案 2 :(得分:1)
这个函数不能保证原子性 插入。
如果我错了,请纠正我,但这是因为我们不知道给定的内容提供商是否会覆盖bulkInsert()
方法,除非它是我们自己的提供商。如果未覆盖bulkInsert()方法,则默认实现将迭代值并在每个值上调用insert(Uri, ContentValues)
。如果您使用自己的提供程序并且知道您已经实现了bulkInsert()
方法,如下面的示例并在finally块中使用endTransaction()
方法,那么应该没问题:
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
final int match = sUriMatcher.match(uri);
switch (match) {
case WEATHER:
db.beginTransaction();
int returnCount = 0;
try {
for (ContentValues value : values) {
normalizeDate(value);
long _id = db.insert(WeatherEntry.TABLE_NAME,
null, value);
if (_id != -1) {
returnCount++;
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
getContext().getContentResolver().notifyChange(uri, null);
return returnCount;
default:
return super.bulkInsert(uri, values);
}
}
答案 3 :(得分:0)
首先在内容提供商中添加批量插入方法
@Override
public int bulkInsert(@NonNull Uri uri, @NonNull ContentValues[] values) {
switch (uriMatcher.match(uri)) {
case USERS:
for (ContentValues value : values) {
long rowID = sqLiteDatabase.insert(YOUR_TABLENAME, "", value);
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); //append ID into CONTENT_URI
getContext().getContentResolver().notifyChange(_uri, null);
return values.length; //return total number of data inserted
}
}
break;
}
return super.bulkInsert(uri, values);
}
在按钮单击中添加以下代码(下面执行以插入批量数据)
String userName = editTextUserName.getText().toString();
String userCity = editTextUserCity.getText().toString();
Log.d("BulkInsert", "onClick: -------START------");
ContentValues[] contentValue = new ContentValues[5000];
for (int i = 0; i < 5000; i++) {
contentValue[i] = new ContentValues(); // initialize Array of content values
//store data in content values object
contentValue[i].put(UserModel.USER_CITY, userCity);
contentValue[i].put(UserModel.USER_NAME, userName);
contentValue[i].put(UserModel.USER_PINCODE, userPincode);
}
int count = getContentResolver().bulkInsert(YOUR_TABLE_URI, contentValue); //insert data
Log.d("BulkInsert", "onClick: " + count); //Display number of data inserted
Log.d("BulkInsert", "onClick: -------STOP------");