Android基础之十四数据存储 之 SQLite数据库详解
Posted zhupengqq
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android基础之十四数据存储 之 SQLite数据库详解相关的知识,希望对你有一定的参考价值。
android基础之十四数据存储 之 SQLite数据库详解
SQLite 是一款 轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百 K 的内存就足够了,因而特别适合在移动设备上使用。
SQLite 不仅支持标准的 SQL 语法,还遵循了数据库的 ACID( 原子性(Atomicity) 、一致性(Consistency) 、 隔离性(Isolation) 、 持久性(Durability))事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手 SQLite。而 SQLite 又比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。Android 正是把这个功能极为强大的数据库嵌入到了系统当中,使得本地持久化的功能有了一次质的飞跃。
SQLite 不仅支持标准的 SQL 语法,还遵循了数据库的 ACID( 原子性(Atomicity) 、一致性(Consistency) 、 隔离性(Isolation) 、 持久性(Durability))事务,所以只要你以前使用过其他的关系型数据库,就可以很快地上手 SQLite。而 SQLite 又比一般的数据库要简单得多,它甚至不用设置用户名和密码就可以使用。Android 正是把这个功能极为强大的数据库嵌入到了系统当中,使得本地持久化的功能有了一次质的飞跃。
1 创建数据库
Android 为了让我们能够更加方便地管理数据库,专门提供了一个 SQLiteOpenHelper帮助类, 借助这个类就可以非常简单地对 数据库进行创建和升级‘
SQLiteOpenHelper 中有两个构造方法可供重写, 一般使用参数少一点的那个构造方法即
可。这个构造方法中接收四个参数,第一个参数是 Context 第二个参数是 数据库名, 创建数据库时使用的就是这里指定的名
称。第三个参数允许我们在查询数据的时候的 返回一个自定义的 Cursor,一般都是传入 null。
第四个参数表示 当前数据库的版本号,可用于 对数据库进行升级操作
可。这个构造方法中接收四个参数,第一个参数是 Context 第二个参数是 数据库名, 创建数据库时使用的就是这里指定的名
称。第三个参数允许我们在查询数据的时候的 返回一个自定义的 Cursor,一般都是传入 null。
第四个参数表示 当前数据库的版本号,可用于 对数据库进行升级操作
MyDatabaseHelper(context, "BookStore.db", null, 1);
<span style="font-size:14px;">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)"; private Context mContext; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); mContext = context; } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }</span>2 升级数据库
如果你足够细心, 一定会发现 MyDatabaseHelper 中还有一个空方法呢! 没错, onUpgrade()方法是用于对数据库进行升级的, 它在整个数据库的管理工作当中起着非常重要的作用, 可千万不能忽视它哟。目前 DatabaseTest 项目中已经有一张 Book 表用于存放书的各种详细数据,如果我们想
张 再添加一张 Category 表用于记录书籍的分类该怎么做呢?
package com.example.sqllitehelp; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper; import android.widget.Toast; public class MyDatabaseHelper extends SQLiteOpenHelper { public static final String CREATE_BOOK="create table Book (" +"id integer primary key autoincrement ," +"author test , " +"price real , " +"pages integer , " +"name text) " ; public static final String CREATE_CATEGORY="create table Category (" +"id integer primary key autoincrement ," +"category_name test , " +"category_code integer)" ; private Context mContext; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); // TODO Auto-generated constructor stub mContext=context; } @Override public void onCreate(SQLiteDatabase db) { // TODO Auto-generated method stub db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); Toast.makeText(mContext, "create successed", Toast.LENGTH_SHORT).show(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // TODO Auto-generated method stub db.execSQL("drop table if exists Book"); db.execSQL("drop table if exists Category"); onCreate(db); Toast.makeText(mContext, "删除数据", Toast.LENGTH_SHORT).show(); } }MyDatabaseHelper(context, "BookStore.db", null, 1); 把这里的版本号改成2就可以执行升级方法了
3 添加数据
public class MainActivity extends Activity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2); ?? Button addData = (Button) findViewById(R.id.add_data); addData.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); // 开始组装第一条数据 values.put("name", "The Da Vinci Code"); values.put("author", "Dan Brown"); values.put("pages", 454); values.put("price", 16.96); db.insert("Book", null, values); // 插入第一条数据 values.clear(); // 开始组装第二条数据 values.put("name", "The Lost Symbol"); values.put("author", "Dan Brown"); values.put("pages", 510); values.put("price", 19.95); db.insert("Book", null, values); // 插入第二条数据 } }); } }4 更新数据
public class MainActivity extends Activity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2); ?? Button updateData = (Button) findViewById(R.id.update_data); updateData.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("price", 10.99); db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" }); } }); } }db.update("Book", values, "name = ?", new String[] { "The DaVinci Code" }) 这句话相当于 Update Book set price=10.99 where name=’The DaVinci Code"‘
5 删除数据
<span style="font-size:14px;">public class MainActivity extends Activity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2); ?? Button deleteButton = (Button) findViewById(R.id.delete_data); deleteButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete("Book", "pages > ?", new String[] { "500" }); } }); } }</span>db.delete("Book", "pages > ?", new String[] { "500" }); 相当于 delete from Book where pages>500
6 查询数据
public class MainActivity extends Activity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2); ?? Button queryButton = (Button) findViewById(R.id.query_data); queryButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.getWritableDatabase(); // 查询Book 表中所有的数据 Cursor cursor = db.query("Book", null, null, null, null, null, null); if (cursor.moveToFirst()) { do { // 遍历Cursor 对象,取出数据并打印 String name = cursor.getString(cursor. getColumnIndex("name")); String author = cursor.getString(cursor. getColumnIndex("author")); int pages = cursor.getInt(cursor.getColumnIndex ("pages")); double price = cursor.getDouble(cursor. getColumnIndex("price")); Log.d("MainActivity", "book name is " + name); Log.d("MainActivity", "book author is " + author); Log.d("MainActivity", "book pages is " + pages); Log.d("MainActivity", "book price is " + price); } while (cursor.moveToNext()); } cursor.close(); } }); } }7 使用 SQL 操作数据库
下面我就来简略演示一下, 如何直接使用 SQL 来完成前面几小节中学过的 CRUD 操作。添加数据的方法如下:
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
更新数据的方法如下:
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99",
"The Da Vinci Code" });
删除数据的方法如下:
db.execSQL("delete from Book where pages > ?", new String[] { "500" });
查询数据的方法如下:
db.rawQuery("select * from Book", null);
可以看到, 除了查询数据的时候调用的是 SQLiteDatabase 的 rawQuery()方法, 其他的操
作都是调用的 execSQL()方法。以上演示的几种方式,执行结果会和前面几小节中我们学习
的 CRUD 操作的结果完全相同,选择使用哪一种方式就看你个人的喜好了
8.使用事务
想象以下场景,比如你正在进行一次转账操作,银行会将转账的金额先从你的账户中扣除,然后再向收款方的账户中添加等量的金额。 看上去好像没什么问题吧?可是, 如果当你账户中的金额刚刚被扣除,这时由于一些异常原因导致对方收款失败,这一部分钱就凭空消失了!当然银行肯定已经充分考虑到了这种情况, 它会保证扣钱和收款的操作要么一起成功, 要么都不会成功,而使用的技术当然就是事务了。
<span style="font-size:14px;">public class MainActivity extends Activity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2); ?? Button replaceData = (Button) findViewById(R.id.replace_data); replaceData.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { SQLiteDatabase db = dbHelper.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(); // 结束事务 } } }); } </span><span style="font-size:18px;color:#ff0000;">}</span>8. 2升级数据库的最佳写法
只需要创建一张 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(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); } @Override public 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, " + "category_name text, " + "category_code integer)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { switch (oldVersion) { case 1: db.execSQL(CREATE_CATEGORY); default: } } }是没过多久,新的需求又来了,这次要给 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, " + "category_name text, " + "category_code integer)"; public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_BOOK); db.execSQL(CREATE_CATEGORY); } @Override public 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数据库详解的主要内容,如果未能解决你的问题,请参考以下文章