目录

  • 使用事务
  • 升级数据库的最佳写法

使用事务

  • SQLite数据库时支持事务的,事务的特性可以保证让某一系列的操作要么全部完成,要么一个都不会完成。
  • 那么在什么情况下才需要使用事务呢?
    • 想象以下场景,比如你正在进行一次转账操作,银行会将转账的金额先从你的账户中扣除,然后再向收款方的账户中添加等量的金额。看上去好像没什么问题吧?
    • 可是,如果当你的账户中的金额刚刚被扣除,这时由于一些异常原因导致对方收款失败,这一部分钱就凭空消失了!
    • 当然银行已经充分考虑到了这种情况,它会保证扣钱和收钱的操作要么一起成功,要么都不会成功,而使用的技术当然就是事务了。
  • 接下来我们看看如何在Android中使用事务,仍然在上篇博文Android基础知识 - 内置SQLite数据库提到的项目里进行修改。
    • 提出需求,Book表里的数据都已经很老了,现在准备全部废弃掉替换成新数据,可以先使用delete()方法将Book表中的数据删除,然后再使用insert()方法将新的数据添加到表中。
    • 我们要保证的是删除数据和添加数据的操作必须一起完成,否则就还要保存原来的旧数据。
  • 修改activity_main.xml中的代码,如下所示。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">......<Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/replace_data"android:text="Replace Data"/></LinearLayout>
  • 修改MainActivity中的代码如下所示。
public class MainActivity extends AppCompatActivity {private MyDatabaseHelper dbOpenHelper;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);dbOpenHelper = new MyDatabaseHelper(this,"BookStore.db",null,2);......Button replaceData = (Button)findViewById(R.id.replace_data);replaceData.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {SQLiteDatabase db = dbOpenHelper.getWritableDatabase();db.beginTransaction();  // 开启事务try{db.delete("Book",null,null);// 第二、第三行参数是去约束某一行或某几行的数据,不指定的话就是删除所有行。if(true){// 这里手动抛出一个异常,让事务失败。throw new NullPointerException();}ContentValues values = new ContentValues();values.put("name","Game of Thrones");values.put("author","George Martin");values.put("pages",720);values.put("price",20.85);db.insert("Book",null,values);db.setTransactionSuccessful();// 事务已经执行成功}catch(Exception e){e.printStackTrace();}finally{db.endTransaction();// 结束事务}}});}
}
/*
上述代码就是Android事务的标准用法,首先调用的是SQLiteDatabase的beginTransaction()方法来开启一个事务,
然后在一个异常捕获的代码块中去执行具体的数据库操作,当所有的操作都完成之后,调用setTransactionSuccessful()
表示事务已经执行成功了,最后在finally代码块中调用endTransaction()来结束事务。注意观察,我们在删除旧数据的操作
完成后手动抛出了一个NullPointerException,这样添加新数据的代码就执行不到了。不过由于事务的存在,中途出现异常会
导致事务的失败,此时的旧数据应该是删除不掉的。
*/
  • 现在运行程序并点击Replace Data按钮,会发现,Book表存在的还是之前的旧数据。然后将手动抛出异常的那行代码去除,再重新运行程序,此时点击一下Repalce Data按钮,就会将Book表中的数据替换成新数据了。

升级数据库的最佳写法

  • 在上篇博文Android基础知识 - 内置SQLite数据库中提到的升级数据库的方式是非常暴力的,为了保证数据库中的表是最新的,我们只是简单地在onUpgrade()方法中删除掉了当前所有的表,然后强制重新执行了一遍onCreate()方法。这种方式在产品的开发阶段确实可以使用,但是当产品真正上线了之后就绝对不行了。
  • 想象以下场景,比如你编写的某个应用已经上线成功,并且还拥有了不错的下载量。现在由于添加新功能的原因,使得数据库也需要一起升级,然后用户更新了这个版本之后发现以前程序中存储的本地数据全部丢失了!那么很遗憾,你的用户群体可能已经流失了一大半了。
  • 那么怎么去做呢?才能不保证数据丢失。
  • 已经知道,每一个数据库版本都会对应一个版本号,当指定的数据库版本号大于当前数据库版本号的时候,就会进入到onUpgrade()方法中去执行更新操作。这里需要为每一个版本号赋予它各自改变的内容,然后在onUpgrade()方法中对当前数据库的版本号进行判断,再执行相应的改变就可以了。
  • 接下来模拟案例,还是由MyDatabaseHelper类对数据库进行管理,第一版的程序要求非常简单,只需要创建一张Book表,MyDatabaseHelper中的代码如下所示:
public class MyDatabaseHelper extends SQLiteOpenHelper {public static final String CREATE_BOOK = "create table book ("+ "id integer primary key autoincrement, "+ "author text, "+ "price real, "+ "pages integer, "+ "name text)";public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(CREATE_BOOK);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}
  • 不过几星期之后又有了新需求,这次需要向数据库中添加一张Category表,于是,修改MyDatabaseHelper 中的代码,如下所示。
public class MyDatabaseHelper extends SQLiteOpenHelper {public static final String CREATE_BOOK = "create table book ("+ "id integer primary key autoincrement, "+ "author text, "+ "price real, "+ "pages integer, "+ "name text)";public static final String CREATE_CATEGORY = "create table category ("+ "id integer primary key autoincrement, "+ "author text, "+ "price real, "+ "pages integer, "+ "name text)";public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(CREATE_BOOK);db.execSQL(CREATE_CATEGORY);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {switch(oldVersion){case 1 :db.execSQL(CREATE_CATEGORY);default:}}
}
/*
可以看到,在onCreate()方法里我们新增加了一条建表语句,然后又在onUpgrade()方法中添加了一个switch判断,
如果用户当前数据库的版本号是1,就只会创建一张Category表。这样当用户是直接安装的第二版的程序时,就会将
两张表一起创建。而当用户是使用第二版的程序覆盖安装第一版的程序时,就会进入到升级数据库的操作中,此时由于
Book表已经存在了,因此只需要创建一张Category表即可。
*/
  • 但是没过多久,新的需求又来了,这次要给Book表和Category表之间建立关联,需要在Book表中添加一个category_id的字段。再次修改MyDatabaseHelper中的代码,如下所示。
public class MyDatabaseHelper extends SQLiteOpenHelper {public static final String CREATE_BOOK = "create table book ("+ "id integer primary key autoincrement, "+ "author text, "+ "price real, "+ "pages integer, "+ "name text, "+ "category_id integer)";public static final String CREATE_CATEGORY = "create table category ("+ "id integer primary key autoincrement, "+ "author text, "+ "price real, "+ "pages integer, "+ "name text)";public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {super(context, name, factory, version);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(CREATE_BOOK);db.execSQL(CREATE_CATEGORY);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {switch(oldVersion){case 1 :db.execSQL(CREATE_CATEGORY);case 2:db.execSQL("alter table Book add column category_id integer");default:}}
}
/*
可以看到,首先我们在Book表的建表语句中添加了一个category_id列,这样当用户直接安装第三版的程序时,
这个新增的列就自动添加成功了。然而,如果用户之前已经安装了某一版的程序,现在需要覆盖安装,就会进入
到升级数据库的操作中。在onUpgrade()方法里,我们添加了一个新的case,如果当前我们的数据库的版本号是2,
就会执行alter命令来为Book表新增一个category_id列。
*/
  • 这里注意一个非常重要的细节,switch中每一个case的最后都是没有使用break的,为什么要这么做呢?
  • 这是为了保证跨版本升级的时候,每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么case 2中的逻辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么case 1和case 2中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。

Android基础知识 - SQLite数据库的最佳实践(使用事务、升级数据库的最佳写法)相关推荐

  1. Android基础总结+SQlite数据库【申明:来源于网络】

    Android基础总结+SQlite数据库[申明:来源于网络] 基础总结篇之一:Activity生命周期:http://blog.csdn.net/liuhe688/article/details/6 ...

  2. Android基础知识【项目实训-实现二级导航“今日活动”及读取数据库】【5】

    [该项目实训是Android基础知识的一个综合练习,特别提示:项目中会用到一些图片素材,都是随意整理的,稍后会上传一个资源,包含该事项项目的基本功能,也含有图片素材] [项目题目]:校园订餐App设计 ...

  3. Android基础知识:在UI线程中运行代码

    本文翻译自:Android basics: running code in the UI thread In the viewpoint of running code in the UI threa ...

  4. Android 基础知识+app测试权限问题

    Android 基础知识(权限篇)** 前言 ​ Android是一个开源的,基于Linux的移动设备操作系统,主要用于移动设备,如智能手机和平板电脑.Android是由谷歌及其他公司带领的开放手机联 ...

  5. android基础知识

    技术型男 随笔 - 20, 文章 - 0, 评论 - 4, 引用 - 0 android基础知识 1. 前言 1.1. 什么是3G.4G Ÿ 第三代移动通信技术(3rd - Generation),速 ...

  6. Android基础知识巩固系列 Android之四大组件——ContentProvider(内容提供者)

    因为最近要面试,于是打算整理整理一下Android的基础知识,由于之前本人已经学习过大概的Android基础知识,这里主要讲这四大组件.五大存储.六大布局.网络请求等这些内容,其他一些等有时间再整理, ...

  7. Android基础知识(二十):Notification、提醒式通知(横幅)踩坑与通知界面设置跳转

    Android基础知识(二十):Notification.提醒式通知(横幅)踩坑与通知界面设置跳转 一.Notification通知与基本用法 通知Notification是Android系统中比较有 ...

  8. Android基础知识——完善

    首页 下载App × Android基础知识--完善 布鲁马 2016.05.17 10:29* 字数 5478 阅读 2672评论 1喜欢 38 疯狂Android摘要,Android基础知识好乱好 ...

  9. Android基础知识(二十一):Android五大存储之文件存储、Content Provider存储和网络存储

    Android基础知识(二十一):Android五大存储之文件存储.Content Provider存储和网络存储 一.Android存储--持久化技术 数据持久化是指将那些内存中的瞬时数据保存到存储 ...

最新文章

  1. [转载]实现Application Tile 更新
  2. 【转】Visio画用例模型图竟然没有include关系
  3. python代码计算矩形面积_学习资料Python语言基础知识笔记以及答案
  4. 【网络安全】某安全网关前端JS分析
  5. Boost:bimap双图的序列化的测试程序
  6. jQuery实现获取选中复选框的值
  7. EISCONN的故事
  8. 迅雷下载Linux Oracle11gR2和Oracle12c
  9. 【Java】ArrayList 为啥要实现 RandomAccess 接口
  10. C# 图像编程 (1) 准备工作; 你好,空姐; 为空姐照片添加特效
  11. 天地图 + geojson 绘制中国行政区划
  12. vivo和OPPO手机刷机
  13. matlab表示大于等于,matlab不等于怎么表示
  14. 如何用一根网线连接两台电脑,传输大文件
  15. sql server中binary怎么得到char类型
  16. 四重境界:人渣、小人、君子、圣人
  17. css3上箭头怎么写,纯css作箭头
  18. 【Java】移位运算
  19. seo推广绩效考核指标是什么(新媒体运营的绩效考核指标)
  20. kirin710f是什么处理器_我告诉你麒麟710f相当骁龙什么处理器

热门文章

  1. 苹果CMSV10高端安全干净模板宽屏大图轮播支持DIY的自适应模板
  2. mysql用c语言连接驱动程序,C语言连接MySql数据库
  3. 华为防火墙在NAT安全策略设置的解释
  4. [Android]StackLabel - 一个很简单的堆叠标签
  5. java webwork_WebWork简单示例
  6. AsyncDisplaykit(Texture)技术分享
  7. JavaScript高级+ES6
  8. c语言实现矩阵乘法 函数调用,C语言关于矩阵乘法的函数,,
  9. groupid 和artifactid分别填写上项目结构和项目唯标识
  10. php之万能密码登陆