Sqlite数据库


1.特点:
在Android平台上,集成了一个嵌入式 关系型数据库—SQLite,
它是一种非常轻量的数据库管理系统,
SQLite3支持 NULL、INTEGER、REAL(浮点数字、实数)、TEXT(字符串文本)
和BLOB(二进制对象)数据类型,SQLite通过 文件来保存数据库,一个文件就是一个数据库
存放的是该应用的 私有数据,存放的位置:/data/data/<包名>/databases,
支持的操作有 : 支持 标准sql语句、创建表、视图、触发器、事务处理

2.访问数据库的流程
1.第一次访问数据库时,创建数据库
如何 创建
SqliteOpenHelper辅助类创建数据库,即创建一个类,继承SqliteOpenHelper
2. 操作数据库
SqliteDatabase 来操作数据库
SqliteDatabase db=SqliteOpenHelper.getWriteableDatabase() \SqliteOpenHelper.getReadableDatabase()
- 增删改查
- 事务处理

3. 结果: Cursor: 游标


3.常用的API
- SqliteOpenHelper :Sqlite数据库辅助类
- SqliteDatabase :数据库操作类
- context.openOrCreateDatabase()//打开或者创建一个数据库
/data/data/<包名>/databases

4.采用SimpleCursorAdapter 适配数据
用游标适配器 ,则关联的表必须有一个_id字段名的主键




数据库(SQLite ) ( ★★★) )
SQLite 特点
SQLite,是一款轻量型的数据库,是遵守ACID(原子性、一致性、隔离
性、持久性)的关联式数据库管理系统,多用于嵌入式开发中。


Android平台中嵌入了一个关系型数据库SQLite,和其他数据库不同的是
SQLite存储数据时不区分类型
例如一个字段声明为Integer类型,我们也可以将一个字符串存入,一个字
段声明为布尔型,我们也可以存入浮点数。


除非是主键被定义为Integer,这时只能存储64位整数,SQLite,无需安装
,是Android平台自带的一个数据库。


 创建数据库的可以不指定数据类型,例如:
CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(20))
CREATE TABLE person(id INTEGER PRIMARY KEY AUTOINCREMENT,
name)
SQLite 支持大部分标准SQL 语句,增删改查语句都是通用的,分页查询
语句和MySQL 相同
SELECT * FROM person LIMIT 20 OFFSET 10
SELECT * FROM person LIMIT 10,20
SQLite 与MySql 的不同之处
主键自增长:SQLite是autoincrement,MySql是auto_increment
主键:
SQLite主键一般定义为_id,在做查询时要求主键列名必须是_id(本身不
是_id,可以起别名),不然拿不到主键值。


 获取可读数据库、可写数据库的区别
可读的数据库也有可能可以写,可读的数据库在获取实例时有可能拿到上一次可写的数据库

创建数据库
下面通过一个案例演示SQLiteOpenHelper的用法。

创建一个Android工程
。为该工程添加Android Junit测试环境(注意:关于Android
Junit的知识在本人的第二篇笔记中有详细的说明)。


定义类继承SQLiteOpenHelper


声明构造函数,4个参数


重写onCreate()方法


重写upGrade()方法


具体代码如下:
public class PersonOpenHelper extends SQLiteOpenHelper {
/**
*
* @param context 上下文对象
* @param name 数据库名称
* @param factory
游标结果集工厂,如果需要使用则需要自定义结果集工厂,null值代表使用默认结果集工

* @param version 数据库版本号,必须大于等于1
*/
public PersonOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
}
/**
* 数据库第一次被创建时调用该方法,这里面主要进行对数据库的初始化操作
*/
public void onCreate(SQLiteDatabase db) {
// 数据库第一次被创建的时候执行如下sql语句创建一个person表
db.execSQL("create table person(id integer primary key
autoincrement, name varchar(20), phone varchar(20), money
integer(20),age integer(10));");
}
/**
* 数据库更新的时候调用该方法
* @param db 当前操作的数据库对象
* @param oldVersion 老版本号
* @param enwVersion 新版本号
*/
public void onUpgrade(SQLiteDatabase db, int oldVersion, int
newVersion) {
// 数据库的版本更新的时候执行
if (oldVersion == 1 && newVersion == 2) {
db.execSQL("alter table person add column
balance integer");
}
}
}


创建一个PersonOpenHelperTest类,用于测试上面的代码:
public class PersonOpenHelperTest extends AndroidTestCase {
public SQLiteDatabase getDataBase(){
PersonOpenHelper helper = new
PersonOpenHelper(getContext(), "person.db", null, 1);
SQLiteDatabase writableDatabase =
helper.getWritableDatabase();
return writableDatabase;
}
}
执行完上面代码后,通过DDMS,查看/data/data/com.itheima.sqlite/databases
目录,发现产生了两个文件,person.db和person.db-
journal。其中第一个文件就是我们的数据库文件。
第一次操作数据库时,person.db-
journal文件会被自动创建,该文件是sqlite的一个临时的日志文件,主要用于sqli
te数据库的事务回滚操作了。
但是Android系统中将该文件永久的保存在磁盘中,不会被自动清除的,如果没
有操作异常或者不需要事务回滚时,此文件的大小为0。这种机制避免了每次生
成和删除person.db-journal文件的开销。
在测试类中创建该类对象,调用getWritableDatabase()或者getReadableDat
abase():
如果数据库不存在,创建数据库文件,执行onCreate()方法,并获取数据库
对象。如果数据库存在,版本号没有发生改变,直接获取数据库对象。如果数
据库存在,版本号提升,先执行onUpgrade()方法,再获取数据库对象。
数据库并不是初始化MyHelper时创建。
如果版本号降低,应用降级。并且不重写onDowngrade,就会调用父类的o
nDowngrade方法,抛出异常。一般不重写,没这个需求。
获取可读数据库、可写数据库的区别:可读的数据库也有可能可以写,可
读的数据库在获取实例时有可能拿到上一次可写的数据库。

使用 SQLiteDatabase 操作数据库
SQLiteDatabase ( ★★★ )
 Android提供了一个名为SQLiteDatabase的类,该类封装了一些操
作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve
)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。


 和JDBC访问数据库不同,操作SQLite数据库无需加载驱动,不用
获取连接,直接可以使用


 常用方法:
获取SQLiteDatabase对象之后通过该对象直接可以执行SQL语句。sqliteData
base.execSQL():可以执行insert、delete、update和CREATE TABLE
之类有更改行为的SQL语句sqliteDatabase.rawQuery():用于执行select查询
语句。
 getReadableDatabase()和getWritableDatabase()的区别
查看源代码后我们发现getReadableDatabase()在通常情况下返回的就是getW
ritableDatabase()拿到的数据库。 只有在抛出异常的时候才会以只读方式打开
 数据库对象缓存
getWritableDatabase()方法最后会使用一个成员变量记住这个数据库对象,
下次打开时判断是否重用
 SQLiteDatabase封装了insert()、delete()、update()、query()四个方
法也可以对数据库进行操作,详情参照7.3.4
执行SQL 语句来操作数据库
执行SQL语句来操作数据库有两种方式,拼串和使用占位符”?。使用占位
符”?”来执行SQL语句能够防止SQL注入攻击。


拼串方式使用的方法:execSQL(String sql):增、删、改。

Cursor rawQuery(String sql, String[] selectionArgs):
查询(拼串方式,第二个参数传null即可)。


占位符”?”使用的方法:void execSQL(String sql, Object[]
bindArgs)。Cursor rawQuery(String sql, String[] selectionArgs)。
第二个参数对应的是第一个参数中占位符”?”对应的数据数组


下面通过一个案例来演示通过执行SQL实现对数据的增删改查操作,这
里使用本文档中7.2章节中的工程(★★★★)。
在本文档7.2章节中,我们继续使用PersonOpenHelperTest类,在该测试类中
添加如下方法:
//插入一条数据
public void insert(){
//获取数据库对象
SQLiteDatabase dataBase = getDataBase();
String sql = "insert into person(name,age,phone)
values(?,?,?)";
//执行sql语句
dataBase.execSQL(sql,new
Object[]{"lisi","22","13240217764"});
//关闭数据库
dataBase.close();
}
//查询单个数据
public void query(){
SQLiteDatabase dataBase = getDataBase();
String sql = "select name,age,phone from person where
name=?";
//执行rawQuery查询,返回Cursor对象
Cursor cursor = dataBase.rawQuery(sql , new
String[]{"zhangsan"});
Person person = new Person();
//如果游标还有下一个元素,跟我们集合中Iterator中hasNext()方法类似
while(cursor.moveToNext()){
//获取当前游标的第0个元素,元素是从0开始的,而不是1
String name = cursor.getString(0);
//也可以通过列名来查询该字段在游标中的位置
int age =
cursor.getInt(cursor.getColumnIndex("age"));
String phone = cursor.getString(2);
person.setName(name);
黑马Android课程笔记 就业服务部出品
person.setAge(age);
person.setPhone(phone);
}
//关闭游标
cursor.close();
System.out.println(person);
}
//更新数据
public void update(){
SQLiteDatabase dataBase = getDataBase();
String sql = "update person set age=? where name=?";
//将zhangsan的年龄修改为18
dataBase.execSQL(sql,new String[]{"18","zhangsan"});
dataBase.close();
}
//查询所有数据
public void queryAll(){
SQLiteDatabase dataBase = getDataBase();
List<Person> persons = new ArrayList<Person>();
String sql = "select name,age,phone from person";
Cursor cursor = dataBase.rawQuery(sql , null);
while(cursor.moveToNext()){
String name = cursor.getString(0);
int age = cursor.getInt(1);
String phone = cursor.getString(2);
Person p = new Person();
p.setAge(age);
p.setName(name);
p.setPhone(phone);
persons.add(p);
}
//关闭游标
cursor.close();
//输出集合中的数据
for(Person p : persons){
System.out.println(p);
}
}
在上面例子各个方法中,为了方便Android
Junit测试,因此我把数据“写死”在代码里了,真正开发中是不会这么设计的。

游标结果集Cursor ( ★★★ )
Cursor是结果集游标,用于对结果集进行随机访问。Cursor与JDBC中的Res
ultSet作用很相似。
Cursor中维护一个行索引一个列索引,游标中本身没有数据,它只是指向
数据库的索引,模拟一个行、列的表结构。其起始位置是在-1的位置上的。
常用方法:
moveToNext():将游标从当前行移动到下一行,如果已经移过了结果集的最
后一行,返回结果为false,否则为true。
moveToPrevious():用于将游标从当前行移动到上一行,如果已经移过了结
果集的第一行,返回值为false,否则为true。
moveToFirst():用于将游标移动到结果集的第一行,如果结果集为空,返回
值为false,否则为true。
moveToLast():用于将游标移动到结果集的最后一行,如果结果集为空,返
回值为false,否则为true。
SQLiteDataBase 自带的增删改查
SQLiteDatabase专门提供了对应于添加(insert)、删除(delete)、更新(
update)、查询(query)的操作方法。
这些方法封装了部分SQL语句,通过参数进行拼接,这些方法实际上是给
那些不太了解SQL语法的开发者使用的。对于熟悉SQL语法的程序员而言,直
接使用execSQL()和rawQuery()方法执行SQL语句就能完成数据的添加、删除、
更新、查询操作。
这四个方法主要是在使用内容提供者时使用,因为contentprovider中提供的
增删改查方法,与这一套一样,方便调用传参。(等学完ContentProvider自然明
白)
insert("表名",nullColumnHack,"",contentValue)
nullColumnHack:如果写null,就无法插入一条空数据(2.3会出异
常,4.0之后可以写null)
如果想插入空数据,第二个参数必须写一个列名(任意列)
这个列名是用来拼接sql语句的,如果contentValue为空,则后台不
知道表的列名,无法构建sql语句
ContentValue:键值对:键对应列明,值对应插入的值
返回值 long id,插入的主键id

delete("表名",条件,条件值),返回受影响的行数。
update("表名",contentValues(更新列、值),条件,条件值),返回受影响的
行数
query("表名", 查询的字段, 条件, 条件值);
下面通过代码来演示SQLiteDatabase操作数据库的过程,我们这里直接使用
本文档7.2中工程,只需修改PersonOpenHelperTest类中方法即可。
//测试添加数据
public void insert() {
SQLiteDatabase dataBase = getDataBase();
ContentValues values = new ContentValues();
values.put("name", "heima");
values.put("age", 5);
values.put("phone", "010-82826816");
/*
* 第一个参数 table,代表要将数据插入哪家表 第二个参数
*
nullColumnHack,字符串类型,指明如果某一字段没有值,那么会将该字段的值设为N
ULL
* ,一般给该参数传递null就行如果没有特殊要求
*
,在这里我传递了phone字符串,也就是说当我的ContentValues中phone字段为空的
时候系统自动给其值设置为NULL
* 第三个参数ContentValues
类似一个Map<key,value>的数据结构,key是表中的字段,value是值
*/
dataBase.insert("person", "phone", values);
}
//测试删除数据
public void delete() {
SQLiteDatabase database = getDataBase();
/*
* 第一个参数 table,代表要删除的表名
* 第二个参数 whereClause
,选择的条件选项,如果为null则删除表中的所有数据
* 第三个参数 whereArgs
,如果有条件选项,对应的条件选项的具体参数,没有写null
* 删除名字为"heima"的记录
*/
database.delete("person", "name=?", new
String[]{"heima"});
}


//测试修改数据
public void update() {
SQLiteDatabase database = getDataBase();
ContentValues values = new ContentValues();
values.put("age", "100");
/*
* 第一个参数table,要更新的表名
* 第二个参数ContentValues
设置要修改的字段的新值,没有涉及到的字段则默认不修改
* 第三、四个参数的含义同方法delete
*/
database.update("person", values , "name=?", new
String[]{"heima"});
}
//测试查询单个数据
public void query() {
SQLiteDatabase database = getDataBase();
/*
* 第一个参数 table,查询的表名
* 第二个参数 columns,要查询的字段
* 第三个参数selection 过滤字段
* 第四个参数selectionArgs 过滤字段的值
* 第五个参数groupBy 分组字段,null代表不分组
* 第六个参数having
* A filter declare which row groups to include in the
cursor,
* if row grouping is being used, formatted as an SQL
HAVING clause
* (excluding the HAVING itself). Passing null will
cause all row groups
* to be included, and is required when row grouping
is not being used.
* 第七个参数orderBy
排序字段,asc正序,desc倒序,null代表自然顺序
*/
Cursor cursor = database.query("person", new
String[]{"name,age,phone"}, "name=?", new String[]{"heima"},
null, null, null);
int id = cursor.getInt(0);
String name = cursor.getString(1);
String phone = cursor.getString(2);
System.out.println(id + "_" + name + "_" +
phone);
}

//测试查询所有数据
public void queryAll(){
SQLiteDatabase database = getDataBase();
List<Person> persons = new ArrayList<Person>();
/*
* 该方法跟query()方法是完全一样的,因此参数的含义不在介绍
*/
Cursor cursor = database.query("person", new
String[]{"name,age,phone"}, null, null, null, null, null);
while(cursor.moveToNext()){
Person p = new Person();
String name = cursor.getString(0);
int age = cursor.getInt(1);
String phone = cursor.getString(2);
p.setName(name);
p.setAge(age);
p.setPhone(phone);
persons.add(p);
}
}
事务管理
事务简介
跟MySql、Oracle等常用数据库一样,SQLite数据库也对事物有较好的支持
。使用方法:
 beginTransaction(): 开启一个事务
 setTransactionSuccessful():设置事务成功标记
 endTransaction():
结束事务,包括提交和回滚,需要放在finally中执行,否则事
务只有到超时的时候才自动结束,会降低数据库并发效率
执行过程:
使用beginTransaction开启一个事务,程序执行到endTransaction方法时会检
查事务的标志是否为成功,如果程序执行到endTransaction之前调用了setTransac
tionSuccessful方法设置事务的标志为成功,则提交事务;如果没有调用setTrans
actionSuccessful方法则回滚事务。
那么下面通过一个案例演示SQLite操作事务的过程。为了便于直奔主题,
我们直接使用本文档7.2章节中的Android工程中的PersonOpenHelperTest类即可

案例:银行转账,需求:客户lisi向zhangsan的账户上转了100块。
public void testTransaction(){
//这里的最后一个参数(数据库版本号)设置为2,那么会执行PersonOpenHel
per类中的onUpgrade方法
PersonOpenHelper helper = new
PersonOpenHelper(getContext(), "person", null, 2);
SQLiteDatabase database =
helper.getWritableDatabase();
try {
//开启事务
database.beginTransaction();
database.execSQL("update person set balance =
balance-100 where name=?",new String[]{"lisi"});
//当把int
a=1/0;放开的时候,发现抛出异常,那么事务就会回滚,上面的扣除lsii的100元钱不
会被真正执行
//如果把int a =
1/0;注释掉,才发现事务成功了,lisi的钱被扣除了100元,同时zhangsan的钱也多了
100元。
int a=1/0;
database.execSQL("update person set balance = balance+100 where
name=?",new String[]{"zhangsan"});
//设置事务成功,也就是只有当代码执行到此行,才代表事务已经成功
database.setTransactionSuccessful();
} finally{
//提交事务,如果setTransactionSuccessful()方法已经执行,则begin
Transaction()后的语句执行成功
//否则,事务回滚到开启事务前的状态
database.endTransaction();
}
}
事务对效率的提高
在批量修改数据的时候,由于事务是在进行事务提交时将要执行的SQL操
作一次性打开数据库连接执行,其执行速度比逐条执行SQL语句的速度快了很
多倍。因此当我们开发中遇到对数据库的批量操作那么,使用事务是提高效率
的重要原则。

下面通过一个案例,来演示批量操作的情况下,使用事务和不使用事务在
效率上的差异。同样的该案例依然使用本文档7.2中的Android工程。
案例:插入一万条数据到数据库,比较使用事务和不使用事务各自所需的
时间。
public void testTransactionEfficient(){
PersonOpenHelper helper = new
PersonOpenHelper(getContext(), "person", null, 2);
SQLiteDatabase database =
helper.getWritableDatabase();
// ------测试不使用事务时插入1w条数据耗时--------------------
long beginTime = System.currentTimeMillis();
for(int i=0;i<10000;i++){
database.execSQL("insert into
person(name,age,phone)
values('text'+"+i+","+i+",'"+(1320000+i)+""+"')");
}
long endTime = System.currentTimeMillis();
System.out.println("不使用事务插入1w条数据耗时:"+(endTime-
beginTime)+"毫秒");
// ---------测试使用事务时耗时-----------------------
beginTime = System.currentTimeMillis();
database.beginTransaction();
for(int i=0;i<10000;i++){
database.execSQL("insert into
person(name,age,phone)
values('text'+"+i+","+i+",'"+(1320000+i)+""+"')");
}
database.setTransactionSuccessful();
database.endTransaction();
endTime = System.currentTimeMillis();
System.out.println("使用事务插入1w条数据耗时:"+(endTime-
beginTime)+"毫秒");
}
执行上面代码,查看控制台,发现不使用事务耗时19397毫秒,使用事务耗
时3404毫秒,性能差别还是相当的明显。

查看SQLite 数据库文件
查看SQLite数据库有多种方法,上面章节的演示过程其实就是通过Android
API的方式查看。那么下面要介绍的是非常常用的两种方式:1、通过SQLite
Expert工具2、通过Android sqlite3工具。


1、 、 通过SQLite Expert 工具
SQLite
Expert(http://www.sqliteexpert.com/)是一款强大的SQLite数据库管理工具。从
官网上下载的版本只能免费试用30天,试用期过后如果继续使用需要支付一定
的费用购买许可。该工具的安装过程很简单,在这里就不再演示。只给大家演
示如果利用该工具打开我们在上一个章节中生成的测试数据。
在DDMS视图中打开/data/data/com.itheima.sqlite/databases/person文件,点击
右上角的导出按钮,然后在弹出的文件对话框中选择需要保存的位置,然后点
击确定即可将模拟器中的数据库文件导出到本地。
然后打开SQLite Expert软件,将person数据拖拽到如下图的左侧区域即可。


2、 、 通过Android sqlite3 工具

Android提供了一个sqlite3.exe程序,位于sdk的tools目录下,用于操作SQLit
e数据库,其常用命令为:
sqlite3 数据库名称:进入数据库操作模式eg: sqlite3 contacts.db
tables:查看所有的表 eg: .tables
schema:查看查看库中所有表的DDL语句 eg: .schema
help : 查看帮助  eg: .help
headers on/off :显示表头,默认off   eg: headers on
mode list|column|insert|line|tabs|tcl|csv:改变输出格式。eg: .mode column
nullValue: NULL空值数据显示问题 eg: .nullValue NULL
dump表名 : 生成形成表的SQL脚本 eq: .dump person
dump : 生成整个数据库的SQL脚本 eq: .dump
exit : 退出sqlite操作模式 eq: .exit


操作步骤:
1、 在命令行界面使用adb shell命令进入linux内核
2、 使用cd命令进入数据库所在目录(数据库的路径为”/data/data/应用
包名/databases/数据库”)
3、 使用”sqlite3 数据库名”进入数据库操作模式
4、 直接使用各种命令操作数据库



结合工作和面试
1. 面试中
 SQLite特点?
不区分数据类型等等,参照上边的章节即可。
 数据库如何维护?
面试中有可能会被问到,你们公司之前应用的数据库是如何维护的?

其实意思就是你们的数据库表、表字段是怎么做增删修改的,你可以说
如果数据库要修改,就改下数据库版本号,然后在onUpgrade里调用即
可。
 数据库用事务么?
这个就可以说,多表操作,或者是循环操作一个表时会用,其他情况不
用。
多表操作,例如转账。循环操作,比如缓存listview数据,要循环插入li
stview的每一个item对象。
2. 工作中
 SQLiteDataBase
这个比较重要,每个应用都会用到数据库,用到数据库就要用到这个类

会使用增删改查方法,事务,以及execSQL即可。
 SQLiteOpenHelper
维护数据库升级,需要用到这个类。熟悉核心方法onCreate方法以及on
Upgrade方法有什么作用,什么时候调用即可。
 事务
上边也提到了,多表操作,循环操作数据库都需要用到事务。所以这个
也比较重要。会用就行。
 SQL语句 重点
做开发的,不管是web端还是客户端,都会接触到sql语句。作为两年工
作经验的开发人员,sql语句是必须要会的,所以大家一定要注意。多
加练习!!!
如果sql语句写的好,会提高性能,会少些很多逻辑代码。



更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. ContentProvider 学习之01
  3. android触摸屏事件手势操作滑动处理
  4. blockCanary原理
  5. Android(安卓)进阶——代码插桩必知必会之初识ASM7字节码操作库
  6. 好用的 Android(安卓)数据库 DBFlow
  7. Android数据库升级、降级、创建(onCreate() onUpgrade() onDowng
  8. Android(安卓)Design in Action —— 编与集
  9. [android研究联系人之二]联系人Email/Nickname数据操作

随机推荐

  1. 编程语言与Python学习(二)
  2. Python基础--图形用户界面GUI
  3. python tkinter窗口弹出置顶的方法
  4. Python 身份运算符
  5. Python基础之socket编程
  6. Fast RCNN的配置流程
  7. 在Python中的大文件中拆分行的内存问题
  8. Python之CMDB资产管理系统
  9. 在python中,如何能同时打开多个excel而只
  10. 怎样写贪吃蛇小游戏?用100行python代码轻