Android入门第54天-SQLite中的Transaction

Posted TGITCIC

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android入门第54天-SQLite中的Transaction相关的知识,希望对你有一定的参考价值。

简介

上一篇我们完整的介绍了SQLite在android中如何使用,今天我们要来讲一下“Transaction“即事务这个问题。

我们经常在编程中会碰到这样的业务场景:

  •  没问题一系列有业务关联性表操作的数据一起提交;
  • 事务中只要有一步有问题,那么就认为在此过程中的每一步都失败,为了保证业务数据的完整性所以每一步的表操作数据都不成功-即:回滚;

在SQLite中,我们使用:

  • dbOpenHelper.getWritableDatabase().beginTransaction()来开启事务;
  • dbOpenHelper.getWritableDatabase().setTransactionSuccessful()申明整个事务成功;

因此我们会把所有的单条DB操作如果有问题都需要抛出Exception,然后在外部调用的块中以“beginTransaction"为起初步骤,全部步骤成功后我们会调用setTransactionSuccessful();那么整个DB事务就会被提交。

否则在beginTransaction后的每一步db操作都不会成功。

如果我们没有在一开始就以beginTransaction()开头,那么每一个单独的db操作如:db.insert()这样的一条语句只要不抛错就会自动被提交到数据库,并且如果有一系列关联的db操作动作的话不使用beginTransaction开头的话那么它们是不含事务操作的。

课程目标

首先我们在这个APP启动时会创建这么三个表(MAC下我用了SQLite Explorer-比我在前一篇介绍的SQLite Manager还要好用、还免费,真心在MAC下面不少免费的东西比Windows下的工具要好用,可能真的是预收费?我这边又想放出那个“吼叫着的土拨鼠”的表情了。)

 然后,在界面上输入学号、班级号然后模拟两个场景:

  1. 以beginTransaction起始以setTransactionSuccessful结束包裹的往t_student、t_class、t_student_class三张表里插数据,一切无误的情况下它会把三张表都插进数据;
  2. 以beginTransaction起始往t_student、t_class、t_student_class三张表里插数据,一切无误的情况下不调用setTransactionSuccessful来模拟事务中有失败场景,然后去表内查找记录,我们会发觉往这三个表里没有能够插入任何新的数据,因为只要没有调用(跳过)setTransactionSuccessful,任何一张表里都不会有数据(即被回滚掉了);

下面放出相应的代码

全代码

建表语句

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)"+
                ");";

 前端UI

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
    tools:context=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="student_id:" />


    <EditText
        android:id="@+id/editStudentId"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="studentId" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="classId:" />

    <EditText
        android:id="@+id/editClassId"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="classId" />

    <Button
        android:id="@+id/buttonAddItem"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加一条记录" />

</LinearLayout>

StudentBean

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;
    

ClassBean

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;
    

StudentClassMappingBean

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;
    

DBAdapter

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<StudentClassMappingBean> queryAll() 
        List<StudentClassMappingBean> stdClassMappingList = new ArrayList<StudentClassMappingBean>();
        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);

        
    

MainActivity

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();
    

运行效果

一切DB操作步骤在无误情况下且正确提交了事务

我们分别输入:

  1. student_id:101, classId:1
  2. student_id:102, classId:1
  3. student_id:103, classId:4

 正确提交数据后我们从Device File Explorer中脱机出来student.db用SQLite Explorer打开,发觉数据正确进入了3张表中

注释掉DBAdapter里的setTransactionSuccessful语句再操作

我们把这一处代码中的setTransactionSuccessful注释掉

重新运行起APP来然后在界面中输入:

然后我们脱机出我们的student.db并用SQLite Explorer打开这个文件来看

我们可以看到,我们新输入的记录由于没有正确setTransactionSuccessful,因此这条数据就没有被插入SQLite中去。

自己动一下手试试吧。

以上是关于Android入门第54天-SQLite中的Transaction的主要内容,如果未能解决你的问题,请参考以下文章

Android入门第41天-Android中的Service(bindService)

Android入门第40天-Android中的Service(SimpleStartService)

Android入门第57天-使用OKHttp多线程制作像迅雷一样的断点续传功能

Android入门第42天-Android中的Service(IntentService)

Android入门第60天-MVVM中的Databinding与ListView结合使用

Android入门第60天-MVVM中的Databinding与ListView结合使用