Google I / O 2010关于SQLite交易

时间:2014-08-13 02:04:35

标签: android googleio

由于这是一个非常丰富的会议,我再一次看着它,并且在分钟 24:17 我注意到Virgil说

  

此外,在使用SQLite时使用事务,不仅会保留数据完整性,还会提高数据库操作的性能

编辑:他对" 使用交易"到底意味着什么?他​​是否意味着告诉我们使用BEGIN TRANSACTION声明,还是他提到了别的什么?

如果是第一个,那么:

  1. 这是否意味着我们应该使用SQLiteDatabase#rawQuery()方法来编写原始SQL语句而不是提供的SQLiteDatabase#query()方法?

  2. 它与使用SELECT语句和TRANSACTION语句有什么区别?

2 个答案:

答案 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();
        }