事務就拿轉帳的例子來說:兩個用戶,要么都成功,要么都失敗,這樣才是安全🔐的,可以保證兩方或者多方,全部成功才成功,否則就回滾(不會對數據有任何修改)
定義數據庫與表
package liudeli.datastorage.db; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class MySQLiteOpenHelper2 extends SQLiteOpenHelper { public static MySQLiteOpenHelper2 mySQLiteOpenHelper; /** * 由於表名每次使用很頻繁,所有定義成常量 */ public static final String TABLE_NAME = "person_table"; private static final String DB_NAME = "csbank.db"; private static final int VERSION = 1; public synchronized static MySQLiteOpenHelper2 getInstance(Context context) { if (null == mySQLiteOpenHelper) { mySQLiteOpenHelper = new MySQLiteOpenHelper2(context, DB_NAME, null, VERSION); } return mySQLiteOpenHelper; } /** * 當開發者調用 getReadableDatabase(); 或者 getWritableDatabase(); * 就會通過此構造方法配置的信息 來創建 person_info.db 數據庫 * 此方法的另外作用是,如果存着數據庫就打開數據庫,不存着數據庫就創建數據庫 * @param context 上下文 * @param name 數據庫名 * @param factory 游標工廠 * @param version 版本,最低為1 */ private MySQLiteOpenHelper2(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } /** * 此方法是何時調用? ,是需要開發者調用 getReadableDatabase(); 或者 getWritableDatabase(); * 此方法的作用是,如果沒有表就創建打開,如果有表就打開 * @param db 可執行SQL語句 */ @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table "+TABLE_NAME+"(_id integer primary key autoincrement, name text, age integer, my_assets text);"); ContentValues values = new ContentValues(); values.put("name", "張三"); values.put("age", 62); values.put("my_assets", "1000000"); db.insert(TABLE_NAME, null, values); values.put("name", "李四"); values.put("age", 22); values.put("my_assets", "2000"); db.insert(TABLE_NAME, null, values); } /** * 此方法用於數據庫升級 * @param db 可執行SQL語句 * @param oldVersion 以前舊版本的版本號 * @param newVersion 現在目前最新的版本號 */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
事務的實際操作:
package liudeli.datastorage; import android.app.Activity; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Toast; import liudeli.datastorage.db.MySQLiteOpenHelper2; public class TransactionActivity extends Activity { private final String TAG = TransactionActivity.class.getSimpleName(); MySQLiteOpenHelper2 dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_transaction); dbHelper = MySQLiteOpenHelper2.getInstance(this); } public void queryALl(View view) { query(null); // 查詢全部 } private void query(String orderBy) { SQLiteDatabase db = dbHelper.getReadableDatabase(); Cursor cursor = db.query(MySQLiteOpenHelper2.TABLE_NAME, new String[]{"*"}, null, null, null, null, orderBy); while (cursor.moveToNext()) { int _id = cursor.getInt(0); String name = cursor.getString(1); int age = cursor.getInt(2); String myAssets = cursor.getString(3); Log.d(TAG, " id:" + _id + " name:" + name + " age" + age + " my_assets:" + myAssets); } cursor.close(); db.close(); } /** * 轉賬 * @param view */ public void moveMoney(View view) { SQLiteDatabase db = dbHelper.getWritableDatabase(); try { int moneyValue1 = 1000000; int moneyValue2 = 2000; db.beginTransaction(); // 開啟事務 ContentValues contentValues = new ContentValues(); contentValues.put("my_assets", moneyValue1 - 6000); int result1 = db.update(MySQLiteOpenHelper2.TABLE_NAME, contentValues, "_id = ?", new String[]{"1"}); contentValues.put("my_assets", moneyValue2 + 6000); int result2 = db.update(MySQLiteOpenHelper2.TABLE_NAME, contentValues, "_id = ?", new String[]{"2"}); if (result1>0 && result2>0) { db.setTransactionSuccessful(); // 事務默認是失敗的,要設置成功,否則數據不會修改 Toast.makeText(TransactionActivity.this, "轉帳成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(TransactionActivity.this, "轉帳失敗", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); Toast.makeText(TransactionActivity.this, "轉帳失敗", Toast.LENGTH_SHORT).show(); } finally { db.endTransaction(); // 注意⚠️:一定要結束事務,不然查詢會報錯 db.close(); } } @Override protected void onDestroy() { super.onDestroy(); } }
Layout:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="轉賬" android:onClick="moveMoney" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="queryALl" android:text="查詢" android:layout_alignParentRight="true" /> </RelativeLayout>