以插入数据为例,有三种方法:
1)直接拼接SQL语句,执行execSQL方法;
2)借用ContentValues进行插入;
3)使用compileStatement进行插入;
1)直接拼接SQL语句,执行execSQL方法
String sql = "create table msgTable(uid INTEGER NOT NULL, msg TEXT NOT NULL)";
db.execSQL(sql);缺点:存在SQL注入危险;
源码分析:
execSQL 调用关系如下:
public void execSQL(String sql) throws SQLException {
executeSql(sql, null);
}
public void execSQL(String sql, Object[] bindArgs) throws SQLException {
if (bindArgs == null) {
throw new IllegalArgumentException("Empty bindArgs");
}
executeSql(sql, bindArgs);
}
private int executeSql(String sql, Object[] bindArgs) throws SQLException {
if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
disableWriteAheadLogging();
mHasAttachedDbs = true;
}
SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
try {
return statement.executeUpdateDelete();
} catch (SQLiteDatabaseCorruptException e) {
onCorruption();
throw e;
} finally {
statement.close();
}
}从调用关系看:execSQL调用的本质就是创建SQLiteStatement对象,调用其executeUpdateDelete插入方法,
而executeUpdateDelete() 方法调用native c++方法,具体如下:
public int executeUpdateDelete() {
try {
saveSqlAsLastSqlStatement();
acquireAndLock(WRITE);
int numChanges = 0;
if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
// since the statement doesn't have to be prepared,
// call the following native method which will not prepare
// the query plan
native_executeSql(mSql);
} else {
numChanges = native_execute();
}
return numChanges;
} finally {
releaseAndUnlock();
}
}2)借用ContentValues进行插入
ContentValues values = new ContentValues();
values.put("uid", 12);
values.put("msg", "test");
long newRowId = db.insert("msgTable", null, values);优点:解决了执行纯SQL语句引入的sql注入漏洞。
源码分析:
public long insert(String table, String nullColumnHack, ContentValues values) {
try {
return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
} catch (SQLException e) {
Log.e(TAG, "Error inserting " + values, e);
return -1;
}
}
public long insertWithOnConflict(String table, String nullColumnHack,
ContentValues initialValues, int conflictAlgorithm) {
StringBuilder sql = new StringBuilder();
sql.append("INSERT");
sql.append(CONFLICT_VALUES[conflictAlgorithm]);
sql.append(" INTO ");
sql.append(table);
sql.append('(');
Object[] bindArgs = null;
int size = (initialValues != null && initialValues.size() > 0) ? initialValues.size() : 0;
if (size > 0) {
bindArgs = new Object[size];
int i = 0;
for (String colName : initialValues.keySet()) {
sql.append((i > 0) ? "," : "");
sql.append(colName);
bindArgs[i++] = initialValues.get(colName);
}
sql.append(')');
sql.append(" VALUES (");
for (i = 0; i < size; i++) {
sql.append((i > 0) ? ",?" : "?");
}
} else {
sql.append(nullColumnHack + ") VALUES (NULL");
}
sql.append(')');
SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
try {
return statement.executeInsert();
} catch (SQLiteDatabaseCorruptException e) {
onCorruption();
throw e;
} finally {
statement.close();
}
}使用ContentValues 最终是创建SQLiteStatement对象,并调用executeInsert()方法。
3)使用compileStatement进行插入
SQLiteStatement sqLiteStatement = db.compileStatement("insert into msgTable(uid, msg) values(?, ?)");
sqLiteStatement.bindLong(1, 12);
sqLiteStatement.bindString(3, "text");
long newRowId = sqLiteStatement.executeInsert();
源码分析:
public SQLiteStatement compileStatement(String sql) throws SQLException {
verifyDbIsOpen();
return new SQLiteStatement(this, sql, null);
}compileStatement 更加直接,之间是根据传入的参数,创建一个SQLiteStatement对象。
小结:
对于执行纯sql,ContentValues和compileStatement最终都是new 一个SQLiteStatement对象,并调用SQLiteStatement对象的相应方法。
本文详细介绍了在Android中插入SQLite数据的三种方式:直接拼接SQL语句、使用ContentValues以及通过compileStatement。每种方法的执行过程和优缺点都有所阐述,特别强调了ContentValues和compileStatement如何避免SQL注入问题,并且最终都会转化为SQLiteStatement执行。


被折叠的 条评论
为什么被折叠?



