Android SQLite 数据库存储详解

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/legend12300/article/details/52556960

Android SQLite 数据库存储详解


SQLite 是一款轻量级的关系型数据库

Android为了让我们能够更加方便地管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。

SQLiteOpenHelper是一个抽象类。

SQLiteOpenHelper中有两个抽象方法,分别是onCreate()和onUpgrade()。

SQLiteOpenHelper中还有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase()。两者的区别参考

Android SQLiteOpenHelper 实例方法getReadableDatabase()和getWritableDatabase() 区别


数据库文件存放在  : /data/data/<package name>/databases/ 目录下


SQLite不像其他的数据库拥有众多繁杂的数据类型,它的数据类型很简单,integer表示整型,real表示浮点型,text表示文本类型,blob表示二进制类型。


SQLiteOpenHelper中有两个构造方法可供重写。

    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
        this(context, name, factory, version, null);
    }
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
            DatabaseErrorHandler errorHandler) {
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);

        mContext = context;
        mName = name;
        mFactory = factory;
        mNewVersion = version;
        mErrorHandler = errorHandler;
    }

第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null。


创建数据库

举个栗子:

创建一个BookStore.db的数据库。然后在这个数据库中新建一张Book表,表中有id(主键)、作者、价格、页数和书名等列。

建表语句

create table Book (
    id integer primary key autoincrement, 
    author text, 
    price real, 
    pages integer, 
    name text)

建表语句中我们还使用了primary key将id列设为主键,并用autoincrement关键字表示id列是自增长的。


升级数据库

再添加一张Category表用于记录书籍的分类。

Category表中有id(主键)、分类名和分类代码

create table Category (
	id integer primary key autoincrement, 
	category_name text, 
	category_code integer)


	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(CREATE_BOOK);
		db.execSQL(CREATE_CATEGORY);
		Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT). show();
	}

点击Create Database 按钮,查看数据后发现,Category表并没有创建。

因为此时BookStore.db数据库已经存在了,之后不管我们怎样点击Create database按钮,MyDatabaseHelper中的onCreate()方法都不会再次执行,因此新添加的表也就无法得到创建了。

需要更改update方法

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("drop table if exists Book");
		db.execSQL("drop table if exists Category");
		onCreate(db);
	}

在onUpgrade()方法中执行了两条DROP语句,如果发现数据库中已经存在Book表或Category表了,就将这两张表删除掉,然后再调用onCreate()方法去重新创建。

更改数据库版本号后,运行项目。即可更新数据库。创建了Category表。

但是这种方法是相当粗暴。直接删表。在正常的产品项目中是不可能这么干的。

正确的更新数据库方法是:

	@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:
		}
	}
在onCreate()方法里我们新增了一条建表语句,然后又在onUpgrade()方法中添加了一个switch判断,如果用户当前数据库的版本号是1,就只会创建一张Category表。这样当用户是直接安装的第二版的程序时,就会将两张表一起创建。而当用户是使用第二版的程序覆盖安装第一版的程序时,就会进入到升级数据库的操作中,此时由于Book表已经存在了,因此只需要创建一张Category表即可。


ADB 查询数据库

如果使用FileExplorer ,最多只能看到databases目录下有一个BookStore.db 文件。

要查询数据库数据,需要 使用 adb shell 。

adb.exe 存放在sdk的platform-tools目录下,如果想要在命令行中使用这个工具,就需要先把它的路径配置到环境变量里。

如果你使用的是Windows系统,可以右击我的电脑→属性→高级→环境变量,然后在系统变量里找到Path并点击编辑,将platform-tools目录配置进去。

如果要使用真机调试程序,真机需要root权限。

配置好环境变量后,输入 adb shell 。如果出现如下格式。说明adb 指令权限不够。再输入 su 指令即可。



C:\Users\Tan>adb shell
shell@cancro:/ $


输入su 后,显示:

C:\Users\Tan>adb shell
shell@cancro:/ $ su
root@cancro:/ #

参考: adb shell 访问真机手机数据库文件


进入数据后,用sqlite3  指令增删改查数据时,会提示sqlite3 不是内部指令。这是因为手机中没有sqlite3 文件。

解决办法:参考如何设置Android手机的sqlite3命令环境


如果条件允许可以使用模拟器。模拟器是支持sqlite3指令集的。


也可以使用【RootExplorerRE文件管理器.apk】  ,安装到手机上直接查看手机data/data/目录下的文件。查看数据库。但是需要手机root权限。

下载地址:下载链接

也可以将 数据库文件导出,然后利用第三方插件查看。参考:Android开发工具---SQLiteManager插件




输入 adb shell 

然后使用cd命令进行到 /data/data/com.example.databasetest/databases/目录下 (注意cd 后面有空格)

使用ls命令查看到该目录里的文件

接下来我们就要借助sqlite命令来打开数据库了,只需要键入sqlite3,后面加上数据库名即可。 sqlite3 BookStore.db

进入数据库后,

输入.table 查看数据库中的表

输入.schema 查看建表语句

键入.exit或.quit命令可以退出数据库的编辑,再键入exit命令就可以退出设备控制台了。


添加数据

我们可以对数据进行的操作也就无非四种,即CRUD。其中C代表添加(Create),R代表查询(Retrieve),U代表更新(Update),D代表删除(Delete)。每一种操作又各自对应了一种SQL命令,添加数据时使用insert,查询数据时使用select,更新数据时使用update,删除数据时使用delete。

方法一:

insert()方法

接收三个参数,第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。

第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。

第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。


		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); // 插入第二条数据
			}
		});

方法二、如果擅长SQL语句也可用如下方法

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 方法:



用adb 进入BookStores.db 数据库。输入查询语句  select * from Book  ,即可查看表中的数据。


方法三:在adb 指令中,用sql 语句增加数据。

adb shell  

cd  /data/data/com.example.databasetest/databases/          //进入数据库目录

ls                                                                                       //查看目录下文件

sqlite3 BookStore.db                                             // 利用sqlite3 指令进入数据库

.table                                                               //查看数据库中的表

select * from Book;                                          //查询Book表中的数据

insert into Book ( name , author , pages , price) values ('diyihangdaima' , 'guolin', '550' ,'70.5');    

 //向Book表中插入一条数据

// 注意 ,用 insert into  指令 ,key 值和values 值 不能用 new String[] {"diyihsangdaima "……}  这种格式。并且,values中的值用单引号。

select * from Book;      //查询Book表中的数据




更新数据


方法一:

update() ; 方法

方法接收四个参数,

第一个参数和insert()方法一样,也是表名,在这里指定去更新哪张表里的数据。

第二个参数是ContentValues对象,要把更新数据在这里组装进去。

第三、第四个参数用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。


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

这里使用了第三、第四个参数来指定具体更新哪几行。第三个参数对应的是SQL语句的where部分,表示去更新所有name等于?的行,而?是一个占位符,可以通过第四个参数提供的一个字符串数组为第三个参数中的每个占位符指定相应的内容。


方法二: 

db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });



方法三:

在cmd中用sqlite3 指令更新数据;

sqlite> update Book set price = '9.99' where id = 1;    //更新ID=1 的数据
sqlite> update Book set price = '50' where author = 'guolin';   // 更新author 为guolin的数据



删除数据

方法一:

delete()方法

方法接收三个参数,

第一个参数仍然是表名

第二、第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行

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

方法二:

db.execSQL("delete from Book where pages > ?", new String[] { "500" });


方法三:

sqlite> delete from Book where id = 1;
sqlite> delete from Book where author = 'guolin';





查询数据


SQL的全称是Structured Query Language,翻译成中文就是结构化查询语言

方法一:

query();

query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。那我们就先来看一下这七个参数各自的含义吧,

第一个参数不用说,当然还是表名,表示我们希望从哪张表中查询数据。

第二个参数用于指定去查询哪几列,如果不指定则默认查询所有列。

第三、第四个参数用于去约束查询某一行或某几行的数据,不指定则默认是查询所有行的数据。

第五个参数用于指定需要去group by的列,不指定则表示不对查询结果进行group by操作。

第六个参数用于对group by之后的数据进行进一步的过滤,不指定则表示不进行过滤。

第七个参数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。

 

 

 

query()方法参数

对应SQL部分

描述

table

from table_name

指定查询的表名

columns

select column1, column2

指定查询的列名

selection

where column = value

指定where的约束条件

selectionArgs

-

为where中的占位符提供具体的值

groupBy

group by column

指定需要group by的列

having

having column = value

对group by后的结果进一步约束

orderBy

order by column1, column2

指定查询结果的排序方式


		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();  // 不关闭会导致内存泄漏
			}
		});

查询完之后就得到了一个Cursor对象,接着我们调用它的moveToFirst()方法将数据的指针移动到第一行的位置,然后进入了一个循环当中,去遍历查询到的每一行数据。在这个循环中可以通过Cursor的getColumnIndex()方法获取到某一列在表中对应的位置索引,然后将这个索引传入到相应的取值方法中,就可以得到从数据库中读取到的数据了。接着我们使用Log的方式将取出的数据打印出来,借此来检查一下读取工作有没有成功完成。最后别忘了调用close()方法来关闭Cursor。


方法二:

db.rawQuery("select * from Book", null);


方法三:

sqlite

select * from Book;


sqlite> select price from Book;
19.95
16.96
19.95

使用事务

		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(); // 结束事务
				}
			}
		});

首先调用SQLiteDatabase的beginTransaction()方法来开启一个事务,然后在一个异常捕获的代码块中去执行具体的数据库操作,当所有的操作都完成之后,调用setTransactionSuccessful()表示事务已经执行成功了,最后在finally代码块中调用endTransaction()来结束事务。注意观察,我们在删除旧数据的操作完成后手动抛出了一个NullPointerException,这样添加新数据的代码就执行不到了。不过由于事务的存在,中途出现异常会导致事务的失败,此时旧数据应该是删除不掉的。


数据库升级最佳写法


给Book表和Category表之间建立关联,需要在Book表中添加一个category_id的字段。

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的!



展开阅读全文

没有更多推荐了,返回首页