由于这是一个非常丰富的会议,我再一次看着它,并且在分钟 24:17 我注意到Virgil说
此外,在使用SQLite时使用事务,不仅会保留数据完整性,还会提高数据库操作的性能
编辑:他对" 使用交易"到底意味着什么?他是否意味着告诉我们使用BEGIN TRANSACTION
声明,还是他提到了别的什么?
如果是第一个,那么:
这是否意味着我们应该使用SQLiteDatabase#rawQuery()
方法来编写原始SQL语句而不是提供的SQLiteDatabase#query()
方法?
它与使用SELECT
语句和TRANSACTION
语句有什么区别?
答案 0 :(得分:0)
简单的例子,向您解释数据库事务的需要和预处理语句的使用等。
当插入大量记录,即数千条记录时,我们遇到了“插入速度”的问题。 Android中常用的insert命令很慢,因此我们可以使用事务和预处理语句。
在我们的例子中,我们在插入查询中使用INSERT OR REPLACE INTO,因为我们想要根据创建的触发器(INDEX)更新一行(如果它已经存在)。
如果您正在使用INSERT OR REPLACE INTO命令,则必须创建一个触发器。在创建表之后执行此SQL触发器(请参阅下面的DatabaseHandler.java)
加速插入的另一个重要因素是使用预准备语句。
您可以找到以下示例:
MainActivity.java - 包含将在用户单击按钮时将大量数据插入db的AsyncTask。
public class MainActivity extends Activity {
final String TAG = "MainActivity.java";
EditText editTextRecordNum;
TextView tvStatus;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View.OnClickListener handler = new View.OnClickListener() {
public void onClick(View v) {
switch (v.getId()) {
case R.id.buttonNormalInsert:
new AsyncInsertData("normal").execute();
break;
case R.id.buttonFastInsert:
new AsyncInsertData("fast").execute();
break;
}
}
};
// EditText for entering desired number of records to be inserted
editTextRecordNum = (EditText) findViewById(R.id.editTextRecordNum);
// Button for normal and fast insert
findViewById(R.id.buttonNormalInsert).setOnClickListener(handler);
findViewById(R.id.buttonFastInsert).setOnClickListener(handler);
// status TextView
tvStatus = (TextView) findViewById(R.id.textViewStatus);
}
// we used AsyncTask so it won't block the UI thread during inserts.
class AsyncInsertData extends AsyncTask<String, String, String> {
DatabaseHandler databaseHandler;
String type;
long timeElapsed;
protected AsyncInsertData(String type){
this.type = type;
this.databaseHandler = new DatabaseHandler(MainActivity.this);
}
// @type - can be 'normal' or 'fast'
@Override
protected void onPreExecute() {
super.onPreExecute();
tvStatus.setText("Inserting " + editTextRecordNum.getText() + " records...");
}
@Override
protected String doInBackground(String... aurl) {
try {
// get number of records to be inserted
int insertCount = Integer.parseInt(editTextRecordNum.getText().toString());
// empty the table
databaseHandler.deleteRecords();
// keep track of execution time
long lStartTime = System.nanoTime();
if (type.equals("normal")) {
databaseHandler.insertNormal(insertCount);
} else {
databaseHandler.insertFast(insertCount);
}
// execution finised
long lEndTime = System.nanoTime();
// display execution time
timeElapsed = lEndTime - lStartTime;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(String unused) {
tvStatus.setText("Done inserting " + databaseHandler.countRecords() + " records. Time elapsed: " + timeElapsed / 1000000 + " ms.");
}
}
}
DatabaseHandler.java - 处理数据库操作,例如创建表,清空数据库,计算数据库记录以及使用循环插入数据。
public class DatabaseHandler extends SQLiteOpenHelper {
// for our logs
public static final String TAG = "DatabaseHandler.java";
// database version
private static final int DATABASE_VERSION = 7;
// database name
protected static final String DATABASE_NAME = "DatabaseName";
// table details
public String tableName = "locations";
public String fieldObjectId = "id";
public String fieldObjectName = "name";
public String fieldObjectDescription = "description";
// constructor
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// creating table
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "";
sql += "CREATE TABLE " + tableName;
sql += " ( ";
sql += fieldObjectId + " INTEGER PRIMARY KEY AUTOINCREMENT, ";
sql += fieldObjectName + " TEXT, ";
sql += fieldObjectDescription + " TEXT ";
sql += " ) ";
db.execSQL(sql);
// create the index for our INSERT OR REPLACE INTO statement.
// this acts as the WHERE name="name input" AND description="description input"
// if that WHERE clause is true, I mean, it finds the same name and description in the database,
// it will be REPLACEd.
// ELSE, what's in the database will remain and the input will be INSERTed (new record)
String INDEX = "CREATE UNIQUE INDEX locations_index ON "
+ tableName + " (name, description)";
db.execSQL(INDEX);
}
// When upgrading the database, it will drop the current table and recreate.
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String sql = "DROP TABLE IF EXISTS " + tableName;
db.execSQL(sql);
onCreate(db);
}
// insert data using transaction and prepared statement
public void insertFast(int insertCount) {
// you can use INSERT only
String sql = "INSERT OR REPLACE INTO " + tableName + " ( name, description ) VALUES ( ?, ? )";
SQLiteDatabase db = this.getWritableDatabase();
/*
* According to the docs http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
* Writers should use beginTransactionNonExclusive() or beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)
* to start a transaction. Non-exclusive mode allows database file to be in readable by other threads executing queries.
*/
db.beginTransactionNonExclusive();
// db.beginTransaction();
SQLiteStatement stmt = db.compileStatement(sql);
for(int x=1; x<=insertCount; x++){
stmt.bindString(1, "Name # " + x);
stmt.bindString(2, "Description # " + x);
stmt.execute();
stmt.clearBindings();
}
db.setTransactionSuccessful();
db.endTransaction();
db.close();
}
// inserts the record without using transaction and prepare statement
public void insertNormal(int insertCount){
try{
SQLiteDatabase db = this.getWritableDatabase();
for(int x=1; x<=insertCount; x++){
ContentValues values = new ContentValues();
values.put(fieldObjectName, "Name # " + x);
values.put(fieldObjectDescription, "Description # " + x);
db.insert(tableName, null, values);
}
db.close();
}catch(Exception e){
e.printStackTrace();
}
}
// deletes all records
public void deleteRecords(){
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("delete from "+ tableName);
db.close();
}
// count records
public int countRecords(){
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery("SELECT count(*) from " + tableName, null);
cursor.moveToFirst();
int recCount = cursor.getInt(0);
cursor.close();
db.close();
return recCount;
}
}
activity_main.xml - 布局,以便我们可以输入要插入的所需记录数,选择我们是希望它是“正常”还是“快速”插入,以及操作的状态。 /强>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<EditText
android:id="@+id/editTextRecordNum"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:inputType="number"
android:singleLine="true"
android:ems="10" >
<requestFocus />
</EditText>
<Button
android:id="@+id/buttonNormalInsert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/editTextRecordNum"
android:layout_below="@+id/editTextRecordNum"
android:text="Normal Insert" />
<Button
android:id="@+id/buttonFastInsert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/buttonNormalInsert"
android:layout_alignBottom="@+id/buttonNormalInsert"
android:layout_toRightOf="@+id/buttonNormalInsert"
android:text="Fast Insert" />
<TextView
android:id="@+id/textViewStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/buttonNormalInsert"
android:layout_below="@+id/buttonNormalInsert"
android:padding="10dp"
android:text="Status" />
答案 1 :(得分:0)
根据SQLITE文档:
事务数据库是所有更改和查询的数据库 似乎是Atomic,Consistent,Isolated和Durable(ACID)。 SQLite的 实现原子化,一致的可序列化事务 孤立的,持久的,即使交易被中断了 程序崩溃,操作系统崩溃或电源故障 计算机。
在Android中,通常插入速度很慢,因此当您想要插入大量数据时必须使用事务。 如果要将数千条记录插入数据库,插入每条记录需要花费大量时间和宝贵的资源。在这种情况下,批量插入或更新可以加快该过程。
这里有一个如何在android中使用事务的例子:
database.beginTransaction();
// or use use beginTransactionNonExclusive() or beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)
// to start a transaction. Non-exclusive mode allows database file to be in readable by other threads executing queries.
database.beginTransactionNonExclusive();
try {
String sql = "Insert or Replace into Students (student_number, age, phone) values(?,?,?)";
SQLiteStatement compileStatement = database.compileStatement(sql);
for(int i = 0; i < studentList.size(); i++) {
compileStatement.bindString(1, studentList.get(i).student_numerb());
compileStatement.bindString(2, studentList.get(i).age());
compileStatement.bindString(3, studentList.get(i).phone());
compileStatement.execute();
database.setTransactionSuccessful();
} catch(Exception e){
e.printStackTrace();
} finally {
database.endTransaction();
}