上一篇我们完整的介绍了SQLite在Android中如何使用,今天我们要来讲一下“Transaction“即事务这个问题。
我们经常在编程中会碰到这样的业务场景:
在SQLite中,我们使用:
因此我们会把所有的单条DB操作如果有问题都需要抛出Exception,然后在外部调用的块中以“beginTransaction"为起初步骤,全部步骤成功后我们会调用setTransactionSuccessful();那么整个DB事务就会被提交。
否则在beginTransaction后的每一步db操作都不会成功。
如果我们没有在一开始就以beginTransaction()开头,那么每一个单独的db操作如:db.insert()这样的一条语句只要不抛错就会自动被提交到数据库,并且如果有一系列关联的db操作动作的话不使用beginTransaction开头的话那么它们是不含事务操作的。
首先我们在这个APP启动时会创建这么三个表(MAC下我用了SQLite Explorer-比我在前一篇介绍的SQLite Manager还要好用、还免费,真心在MAC下面不少免费的东西比Windows下的工具要好用,可能真的是预收费?我这边又想放出那个“吼叫着的土拨鼠”的表情了。)
然后,在界面上输入学号、班级号然后模拟两个场景:
下面放出相应的代码
private static final String DB_CREATE_STD_CLASS = "CREATE TABLE t_student_class(student_id VARCHAR(20), class_id VARCHAR(6),"+
" PRIMARY KEY(student_id, class_id)"+
");";
private static final String DB_CREATE_STD = "CREATE TABLE t_student(student_id VARCHAR(20), student_name VARCHAR(20),"+
" PRIMARY KEY(student_id)"+
");";
private static final String DB_CREATE_CLASS = "CREATE TABLE t_class(class_id VARCHAR(20),"+
" PRIMARY KEY(class_id)"+
");";
package org.mk.android.demo.transaction;
import java.io.Serializable;
public class StudentBean implements Serializable {
private String studentId="";
private String studentName="";
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
}
package org.mk.android.demo.transaction;
import java.io.Serializable;
public class ClassBean implements Serializable {
private String classId="";
public String getClassId() {
return classId;
}
public void setClassId(String classId) {
this.classId = classId;
}
}
package org.mk.android.demo.transaction;
import java.io.Serializable;
public class StudentClassMappingBean implements Serializable {
private String studentId = "";
private String classId = "";
public String getStudentId() {
return studentId;
}
public void setStudentId(String studentId) {
this.studentId = studentId;
}
public String getClassId() {
return classId;
}
public void setClassId(String classId) {
this.classId = classId;
}
}
package org.mk.android.demo.transaction;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class DBAdapter {
private static final String TAG = "DemoSQLiteTransaction";
private static final String DB_NAME = "student.db";
private static final int DB_VERSION = 2;
private SQLiteDatabase db;
private Context context = null;
private DBOpenHelper dbOpenHelper;
public DBAdapter(Context ctx) {
context = ctx;
}
public void close() {
try {
if (db != null) {
db.close();
db = null;
}
} catch (Exception e) {
}
}
public void open() {
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
try {
db = dbOpenHelper.getWritableDatabase();
} catch (SQLiteException ex) {
Log.e(TAG, ">>>>>>open db error: " + ex.getMessage(), ex);
}
}
public void addStudentAndClass(StudentBean stdBean,ClassBean clsBean)throws Exception{
try {
db.beginTransaction();
ContentValues stdValues=new ContentValues();
stdValues.put("student_id", stdBean.getStudentId());
stdValues.put("student_name", stdBean.getStudentName());
ContentValues classValues=new ContentValues();
classValues.put("class_id", clsBean.getClassId());
ContentValues stdClassValues=new ContentValues();
stdClassValues.put("student_id", stdBean.getStudentId());
stdClassValues.put("class_id", clsBean.getClassId());
db.insert("t_student", null, stdValues);
db.insert("t_class", null, classValues);
//throw new Exception("mk define the customized exception");
db.insert("t_student_class", null, stdClassValues);
db.setTransactionSuccessful();
} catch (Exception e) {
Log.e(TAG, "addItem error: " + e.getMessage(), e);
throw new Exception("addItem error: " + e.getMessage(), e);
}finally {
db.endTransaction();
}
}
public List queryAll() {
List stdClassMappingList = new ArrayList();
try {
StringBuilder sqlStr = new StringBuilder();
sqlStr.append("select student_id, class_id from t_student_cass");
Cursor cur = db.rawQuery(sqlStr.toString(), null);
while (cur.moveToNext()) {
String studentId = cur.getString(cur.getColumnIndexOrThrow("student_id"));
String classId = cur.getString(cur.getColumnIndexOrThrow("classId"));
StudentClassMappingBean stdClassMappingBean=new StudentClassMappingBean();
stdClassMappingBean.setStudentId(studentId);
stdClassMappingBean.setClassId(classId);
stdClassMappingList.add(stdClassMappingBean);
}
} catch (Exception e) {
Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e);
}
return stdClassMappingList;
}
private static class DBOpenHelper extends SQLiteOpenHelper {
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
private static final String DB_CREATE_STD_CLASS = "CREATE TABLE t_student_class(student_id VARCHAR(20), class_id VARCHAR(6),"+
" PRIMARY KEY(student_id, class_id)"+
");";
private static final String DB_CREATE_STD = "CREATE TABLE t_student(student_id VARCHAR(20), student_name VARCHAR(20),"+
" PRIMARY KEY(student_id)"+
");";
private static final String DB_CREATE_CLASS = "CREATE TABLE t_class(class_id VARCHAR(20),"+
" PRIMARY KEY(class_id)"+
");";
@Override
public void onCreate(SQLiteDatabase db) {
Log.i(TAG, ">>>>>>execute create table" );
db.execSQL(DB_CREATE_STD_CLASS);
db.execSQL(DB_CREATE_STD);
db.execSQL(DB_CREATE_CLASS);
Log.i(TAG, ">>>>>>execute create table successfully" );
}
@Override
public void onUpgrade(SQLiteDatabase db, int _oldVersion, int _newVersion) {
//db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE);
//onCreate(_db);
db.execSQL("DROP TABLE IF EXISTS t_student");
db.execSQL("DROP TABLE IF EXISTS t_class");
db.execSQL("DROP TABLE IF EXISTS t_student_class");
onCreate(db);
}
}
}
package org.mk.android.demo.transaction;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
public class MainActivity extends AppCompatActivity {
private SQLiteDatabase db;
private Context context;
private DBAdapter dbAdapter;
Button buttonAddItem;
EditText editStudentId;
EditText editClassId;
private static final String TAG = "DemoSQLiteTransaction";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
dbAdapter = new DBAdapter(context);
buttonAddItem=(Button)findViewById(R.id.buttonAddItem);
editStudentId=(EditText)findViewById(R.id.editStudentId);
editClassId=(EditText)findViewById(R.id.editClassId);
buttonAddItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String studentId=editStudentId.getText().toString();
String classId=editClassId.getText().toString();
try{
dbAdapter.open();
StudentBean stdBean=new StudentBean();
ClassBean clsBean=new ClassBean();
stdBean.setStudentId(studentId);
stdBean.setStudentName(studentId);
clsBean.setClassId(classId);
dbAdapter.addStudentAndClass(stdBean,clsBean);
Log.i(TAG,">>>>>>insert success");
}catch(Exception e){
Log.e(TAG,">>>>>>addItem error: "+e.getMessage(),e);
}finally{
dbAdapter.close();
}
}
});
dbAdapter.open();
}
@Override
protected void onStop() {
super.onStop();
dbAdapter.close();
}
}
我们分别输入:
正确提交数据后我们从Device File Explorer中脱机出来student.db用SQLite Explorer打开,发觉数据正确进入了3张表中
我们把这一处代码中的setTransactionSuccessful注释掉
重新运行起APP来然后在界面中输入:
然后我们脱机出我们的student.db并用SQLite Explorer打开这个文件来看
我们可以看到,我们新输入的记录由于没有正确setTransactionSuccessful,因此这条数据就没有被插入SQLite中去。
自己动一下手试试吧。