Android入门第54天-SQLite中的Transaction

news2025/1/15 13:15:40

简介

上一篇我们完整的介绍了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中去。

自己动一下手试试吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/110061.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PCL 点云最小生成树(MST,Dijkstra算法)

文章目录 一、简介二、实现代码三、实现效果参考文献一、简介 之前使用过Kruskal算法创建过最小生成树(Open3D 点云最小生成树算法(MST,Kruskal算法)),这里使用另一种算法(Dijkstra算法)来实现创建一个最小生成树,原始的Dijkstra算法并不适用于去生成最小生成树,因此…

xxe-lab靶场安装和简单php代码审计

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是xxe-lab靶场安装和简单php代码审计。 一、xxe-lab靶场简介 xxe-lab是一个使用java、python、php和C#四种编程语言开发的存在xxe漏洞的web小型靶场。利用该靶场可以简单研究xxe漏洞&#xff0c;并且对于这四种编…

Win10微软输入法打不出汉字?

在Win10系统中自带的微软输入法无需再安装其他拼音输入法就可以轻松输入汉字&#xff0c;非常方便&#xff0c;但是有的用户却遇到了Win10专业版自带的微软输入法打不出汉字的问题&#xff0c;这要如何解决呢&#xff1f;有需要的用户就来一起看看吧。 1、点击系统左下侧的wind…

Allegro如何更改铜皮的网络操作指导

Allegro如何更改铜皮的网络操作指导 在做PCB设计的时候需要更改铜皮的网络,Allegro上可以快速的更改铜皮的网络。如下图,需要给铜皮赋上网络 具体操作如下 选择selcet shape命令选中铜皮

会计毕业生的转行之路:坚持无畏,我是我自己的英雄

有时候&#xff0c;我们面对困境&#xff0c;总会犹豫&#xff0c;不敢迈出一步。 但当我们真的鼓起勇气打破困局时&#xff0c;才会发现出路就在眼前&#xff0c;原来只要不放弃&#xff0c;一切皆有可能。 初遇&#xff1a;会计生大四想转行 我是一名来自内蒙古的少数民族女生…

还有1个月,乘用车搭载首超5百万辆!L2/L2+前装交付一路狂奔

高工智能汽车研究院监测数据显示&#xff0c;2022年1-11月中国市场&#xff08;不含进出口&#xff09;乘用车交付上险为1745.95万辆&#xff0c;同比上年同期下滑4.01%&#xff0c;降幅和1-10月数据相比&#xff0c;继续放大&#xff0c;显示市场回暖低于预期。 不过&#xff…

单商户商城系统功能拆解52—财务概况

单商户商城系统&#xff0c;也称为B2C自营电商模式单店商城系统。可以快速帮助个人、机构和企业搭建自己的私域交易线上商城。 单商户商城系统完美契合私域流量变现闭环交易使用。通常拥有丰富的营销玩法&#xff0c;例如拼团&#xff0c;秒杀&#xff0c;砍价&#xff0c;包邮…

连接稳定性最好的蓝牙耳机有哪些?盘点2023年值得入手的蓝牙耳机

2022年已准备过完&#xff0c;马上来临2023新的一年&#xff0c;大家入手了蓝牙耳机吗&#xff1f;要我说&#xff0c;一款好用的蓝牙耳机不仅只有音质、蓝牙技术、配置性能等&#xff0c;也不能缺少佩戴体验&#xff0c;要想长时间佩戴耳机听歌通话、玩游戏和运动这些&#xf…

白话说Java虚拟机原理系列【第四章】:内存结构之方法区详解

文章目录执行引擎内存结构&#xff1a;运行时数据区方法区(永久代PermGen)方法区的设计初衷&#xff1f;方法区存的什么内容&#xff1f;方法区的异常&#xff1a;运行时常量池&#xff1a;方发表&#xff1a;这里我们详细讲解前导说明&#xff1a; 本文基于《深入理解Java虚拟…

斩获数亿元B轮融资,这家Tier 1抢跑「L2/L2+」主战场

伴随着汽车智能化演进加速&#xff0c;L2/L2&#xff0b;辅助驾驶功能已经成为各家车企抢夺市场的“要塞”。 据高工智能汽车研究院监测数据显示&#xff0c;今年1-9月前装标配搭载L2级辅助驾驶搭载量为395.19万辆&#xff0c;同比增长69.53%&#xff0c;前装搭载率为27.69%。…

倒角算法推导

推导原理基本很简单&#xff1a; 已知AB&#xff0c; BC两条线段&#xff0c;且交于B点&#xff0c;求倒角半径为 L&#xff0c;AB&#xff0c;BC的倒角 以最短边&#xff08;假定为AB&#xff09;长 LAB&#xff0c; 在BC中&#xff0c;以B为起点&#xff0c;找出与LAB同长度…

FOC算法与SVPWM技术

最近看到了FOC这个东西&#xff0c;感觉很有意思&#xff0c;想着以后用这个算法做个东西&#xff0c;目前的想法是用开源的ODrive方案&#xff0c;自己做一个有感单电机驱动的板子&#xff0c;并且加入一点自己的东西&#xff0c;但是这不是目前工作的重点&#xff0c;所以就先…

基于Vue+Element实现的电商后台管理系统的前端项目,主要包括商品管理、订单管理、会员管理、促销管理、运营管理、内容管理

前言 该项目为前后端分离项目的前端部分&#xff0c; 项目介绍 mall-admin-web是一个电商后台管理系统的前端项目&#xff0c;基于VueElement实现。主要包括商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等功能。 完整…

MySQL面试常问问题(锁 + 事务) —— 赶快收藏

目录 1.MySQL中有哪几种锁&#xff0c;列举一下&#xff1f; 2.说说InnoDB里的行锁实现? 3.意向锁是什么知道吗&#xff1f; 4.MySQL的乐观锁和悲观锁了解吗&#xff1f; 5.MySQL 遇到过死锁问题吗&#xff0c;你是如何解决的&#xff1f; 6.MySQL 事务的四大特性说一下…

ChatGPT的各项超能力从哪儿来?万字拆解追溯技术路线图来了

作者&#xff1a;符****尧、彭昊、Tushar Khot、郭志江等**** 符尧&#xff08;yao.fued.ac.uk&#xff09;&#xff0c;爱丁堡大学 (University of Edinburgh) 博士生&#xff0c;本科毕业于北京大学。他与彭昊、Tushar Khot在艾伦人工智能研究院 (Allen Institute for AI) 共…

jQuery 插件开发

文章目录jQuery 插件开发插件概述常用插件文本溢出&#xff1a;dotdotdot.js单行文本省略多行文本省略延迟加载&#xff1a;lazyload.js插件编写方法类插件函数类插件jQuery 插件开发 插件概述 jQuery插件可以理解成是使用jQuery来封装的一个功能或特效。 一般来说&#xff…

【我亲身经历的2022年软件质量工作】

软件危机&#xff08;softwarecrisis&#xff09;&#xff0c;20世纪60年代以前&#xff0c;计算机刚刚投入实际使用&#xff0c;软件设计往往只是为了一个特定的应用而在指定的计算机上设计和编制&#xff0c;采用密切依赖于计算机的机器代码或汇编语言&#xff0c;软件的规模…

如何避免编程从入门到放弃?

写代码不是什么太需要创造力的劳动&#xff0c;现在的代码从业者本质上与工业时代的纺织工人没什么差异。大多数人写代码也并不是真的有兴趣&#xff0c;只不过金钱的诱惑使然&#xff0c;这没什么不好&#xff0c;也十分正确。 但对于零基础转行编程的人来说&#xff0c;坚持下…

关于居住办公人口的统计技术解决方案

在数字化转型的浪潮下&#xff0c;大数据产业作为城市数字化转型的重要助力&#xff0c;带来了城市管理手段、模式、理念的深刻变革与创新。为了更好地了解国家城镇的职住分布结构&#xff0c;帮助城市管理部门制定更加合理的规划策略&#xff0c;为城市商业产业提供有效的规划…

面向对象的软件工程

面向对象的软件工程1 面向对象的演化1.1 生活中复杂系统的特点1.2 软件系统的复杂性1.2.1 复杂性的四个方面1.2.1.1 问题域的复杂性1.2.1.2 管理开发的困难性1.2.1.3 软件中的灵活性1.2.1.4 描述离散系统行为1.2.2 复杂系统的五个属性1.2.2.1 层次结构1.2.2.1.1 对象结构1.2.2.…