持久化技术——SQLite数据库
SQLite数据库
概述
在Android中使用了一个开源的、与操作系统无关的SQL数据库——SQLite数据库。
SQLite是一款轻量级数据库,它的设计目的是嵌入式,因为它占用的资源非常少,在嵌入式设备中可能只需要几百KB,所以它是应用最广泛的嵌入式数据库。满足嵌入式的需求,这也是Android系统采用SQLite数据库的原因之一。
目前的版本是3.27.2
官网: https://www.sqlite.org/index.html
????是网上copy的一段资料,可以简单浏览一下。
出处:http://www.runoob.com/sqlite/sqlite-intro.html
什么是 SQLite?
SQLite是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。它是一个零配置的数据库,这意味着与其他数据库一样,您不需要在系统中配置。
就像其他数据库,SQLite 引擎不是一个独立的进程,可以按应用程序需求进行静态或动态连接。SQLite 直接访问其存储文件。
为什么要用 SQLite?
不需要一个单独的服务器进程或操作的系统(无服务器的)。
SQLite 不需要配置,这意味着不需要安装或管理。
一个完整的 SQLite 数据库是存储在一个单一的跨平台的磁盘文件。
SQLite 是非常小的,是轻量级的,完全配置时小于 400KiB,省略可选功能配置时小于250KiB。
SQLite 是自给自足的,这意味着不需要任何外部的依赖。
SQLite 事务是完全兼容 ACID 的,允许从多个进程或线程安全访问。
SQLite 支持 SQL92(SQL2)标准的大多数查询语言的功能。
SQLite 使用 ANSI-C 编写的,并提供了简单和易于使用的 API。
SQLite 可在 UNIX(Linux, Mac OS-X, Android, iOS)和 Windows(Win32, WinCE, WinRT)中运行。
历史
2000 -- D. Richard Hipp 设计 SQLite 是为了不需要管理即可操作程序。
2000 -- 在八月,SQLite1.0 发布 GNU 数据库管理器(GNU Database Manager)。
2011 -- Hipp 宣布,向 SQLite DB 添加 UNQl 接口,开发 UNQLite(面向文档的数据库)。
SQLite3数据类型
SQLite3它支持以下五种类型:
- NULL
- INTEGER
- REAL(浮点数字)
- TEXT(字符串文本)
- BLOB(二进制对象)数据类型。
实际上SQLite3也接受varchar(n)、char(n)、 decimal(p,s) 等数据类型,但在运算或保存时会转成对应的五种数据类型。
SQLite最大的特点是你可以保存任何类型的数据到任何字段中,无论这列声明的数据类型是什么:
- 可以在Integer类型的字段中存放字符串或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。
但有一种情况例外:
- 定义为INTEGER PRIMARY KEY的字段只能存储64位整数,当向这种字段中保存除整数以外的数据时,将会产生错误。
SQLite命令行操作
在命令行下创建数据库:
- 启动模拟器后,打开命令行,执行adb shell
- 进入所在工程目录
- 执行sqlite3创建数据库文件
这时可以使用:
- .help可以获取帮助界面
- .quit退出/离开
SQL指令都是以分号()结尾的,如果遇到两个减号(--)则代表注解,sqlite3会略过去。
创建表:
create table film(_id integer primary key autoincrement,
title text, length int, year int, starring text);
语法上基本上和SQL差别不大,就是好多地方会以“."打头
可以参考下:http://www.runoob.com/sqlite/sqlite-syntax.html
注意:请确保你已经拥有root权限。否则在使用SQLite命令行会遇到一定的问题,如遇到Permission denied的提示、访问不了data/data目录等问题
一般来说,ADV创建的安卓模拟器是具有root权限的,但是需要注意,如果创建的模拟器版本较高,需要确保后面有(Google APIs),否则你就得考虑一下怎么给你的模拟器root一下的问题了,那就是另一个麻烦事情了。
也可以通过SQLite可视化管理工具快速进行管理,下面提供官方的链接
当然如果已经装了navicat之类的...其实也是支持SQLite的 ← ←
定义数据库的元数据
元数据
元数据(Metadata)是描述其它数据的数据(data about other data),或者说是用于提供某种资源的有关信息的结构数据(structured data)
包android.database.sqlite包含了使用SQLite数据库的所有API
SQL数据库主要概念之一就是Schema——一个关于如何组织数据库的定义
单表定义表名和列名
//以下是一个为单表定义表名和列名的代码片段
public final class FeedReaderContract {
public FeedReaderContract(){}
/* Inner class that defines the table contents */
public static abstract class FeedEntry implements BaseColumns{
public static final String TABLE NAME = "entry";
public static final String COLUMN NAME ENTRY_ID = "entryid";
public static final String COLUMN NAME TITLE ="title";
public static final String COLUMN NAME SUBTITLE = "subtitle";
...
}
}
使用SQLiteOpenHelper创建数据库
- android系统里,数据库存储在private空间里,其他app无法访问,比较安全。
- SQLiteOpenHelper类里有很多有用的API,当你使用这个类获取数据库的引用的时候,只有当你需要的时候,系统才有可能行耗时较长的操作,比如创建和更新数据库。你只要调用getWritableDatabase()和getReadableDatabase()方法就可以。
- 因为有些操作可能会long running,所以务必在后台进程调用getWritableDatabase()和getReadableDatabase()方法。
- 要使用SQLiteOpenHelper类(抽象类),就需要创建一个子类,并且覆写onCreate(),onUpgrade()和onOpen0这几个回调方法,同时,也最好实现onDowngrade()方法。
//创建表和删除表的典型的语句
private static final String SQL_CREATE ENTRIES =
"CREATE TABLE"+ FeedEntry.TABLE NAME+ "("+FeedEntry. ID +"INTEGER PRIMARY KEY
AUTOINCREMENT,"+FeedEntry.COLUMN NAME ENTRY ID + "text,"+
FeedEntry.COLUMN NAME TITLE + "text)";
private static final String SQL DELETE ENTRIES =
"DROP TABLE IF EXISTS "+ FeedEntry.TABLE NAME;
//示例
public class DatabaseHelper extends SQLiteOpenHelper {
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME ="FeedReader.db";
public FeedReaderDbHelper(Context context) {
super(context, DATABASE NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL CREATE ENTRIES);)
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execsQL(SQL DELETE ENTRIES);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
//使用以下语句创建数据库助手类对象
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());
插入数据
SQLiteDatabase db =mDbHelper.getWritableDatabase();
// Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN NAME ENTRYID, id);
values.put(FeedEntry.COLUMN NAME TITLE, title);
values.put(FeedEntry.COLUMN NAME CONTENT, content);
// Insert the new row, returning the primary key value of the new row
long newRowld;
newRould = db.insert(
FeedEntry.TABLE NAME,
FeedEntry.COLUMN NAME NULLABLE,
values);
增删改查小案例
PetMetaData.java
package com.example.a4_11sqlite;
import android.provider.BaseColumns;
/*元数据的定义*/
//不希望让别的类继承
public final class PetMetaData {
//不能进行实例化
private PetMetaData(){}
//Dog表的定义
public static abstract class DogTable implements BaseColumns {
//表名
public static final String TABLE_NAME="dog";
//字段
public static final String NAME="name";
public static final String AGE="age";
}
}
DatabaseHelper.java
package com.example.a4_11sqlite;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME="pet.db";
private static final int VERSION=1;
private static final String CREATE_TABLE_DOG="create table dog(_id integer primary key autoincrement,"+
"name text,age integer)";
private static final String DROP_TABLE_DOG="DROP TABLE IF EXISTS dog";
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, VERSION);
}
//数据库表不存在时调用
@Override
public void onCreate(SQLiteDatabase db) {
//用于操作数据库的工具类——SQLiteDatabase
db.execSQL(CREATE_TABLE_DOG);
}
//升级
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL(DROP_TABLE_DOG);
db.execSQL(CREATE_TABLE_DOG);
}
}
DatabaseAdapter.java
package com.example.a4_11sqlite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
public class DatabaseAdapter {
//工具类
private DatabaseHelper dbHelper;
//构造方法
public DatabaseAdapter(Context context) {
dbHelper = new DatabaseHelper(context);
}
//添加
public void add(Dog dog){
//获取操作数据库的工具类
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put(PetMetaData.DogTable.NAME,dog.getName());
values.put(PetMetaData.DogTable.AGE,dog.getAge());
//插入,参数(表名,可以为NULL的列名,ContentValues)
//中间的参数是为了ContentValues为空的时候保证Sqlite语句的合法性,如果ContentValues确保不为空,中间参数可以为NULL
db.insert(PetMetaData.DogTable.TABLE_NAME,null,values);
db.close();
}
//删除
public void delete(int id){
SQLiteDatabase db = dbHelper.getWritableDatabase();
//删除的条件——
String whereClause = PetMetaData.DogTable._ID+"=?";
String[] whereArgs= {String.valueOf(id)};
//删除,参数1表名,参数2条件,参数3条件的值
db.delete(PetMetaData.DogTable.TABLE_NAME,whereClause,whereArgs);
db.close();
}
//更新
public void update(Dog dog){
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values=new ContentValues();
values.put(PetMetaData.DogTable.NAME,dog.getName());
values.put(PetMetaData.DogTable.AGE,dog.getAge());
String whereClause=PetMetaData.DogTable._ID+"=?";
String[] whereArgs= {String.valueOf(dog.getId())};
//更新,参数1表名,参数2ContentValues,参数3条件,参数4条件的值
db.update(PetMetaData.DogTable.TABLE_NAME,values,whereClause,whereArgs);
}
//根据ID查询
public Dog findById(int id){
SQLiteDatabase db = dbHelper.getReadableDatabase();
String[] colums = {PetMetaData.DogTable._ID,PetMetaData.DogTable.NAME,PetMetaData.DogTable.AGE};
//参数从左到右分别为:
// 是否去重,表名,要查询的列,查询条件,查询条件值,分组条件,分组条件值,排序,分页条件)
Cursor c = db.query(true,PetMetaData.DogTable.TABLE_NAME,colums,PetMetaData.DogTable._ID+"=?",new String[]{String.valueOf(id)},null,null,null,null);
//返回一个游标
Dog dog=null;
//游标移动到下一条
if (c.moveToNext()){
dog=new Dog();
dog.setId(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable._ID)));
dog.setName(c.getString(c.getColumnIndexOrThrow(PetMetaData.DogTable.NAME)));
dog.setAge(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable.AGE)));
}
c.close();
db.close();
return dog;
}
//查询所有数据
public ArrayList<Dog> findAll(){
SQLiteDatabase db = dbHelper.getReadableDatabase();
String[] colums={PetMetaData.DogTable._ID,PetMetaData.DogTable.NAME,PetMetaData.DogTable.AGE};
//参数从左到右分别为:
// 是否去重,表名,要查询的列,查询条件,查询条件值,分组条件,分组条件值,排序,分页条件)
Cursor c=db.query(true,PetMetaData.DogTable.TABLE_NAME,colums,null,null,null,null,null,null);
ArrayList<Dog> dogs=new ArrayList<>();
Dog dog=null;
//游标移动到下一条
while (c.moveToNext()){
dog=new Dog();
dog.setId(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable._ID)));
dog.setName(c.getString(c.getColumnIndexOrThrow(PetMetaData.DogTable.NAME)));
dog.setAge(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable.AGE)));
dogs.add(dog);
}
c.close();
db.close();
return dogs;
}
}
实体类Dog.java
package com.example.a4_11sqlite;
public class Dog {
private int id;
private String name;
private int age;
//空
public Dog() {
}
//全
public Dog(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
//无id
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/add"
android:onClick="add"
android:text="添加"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/delete"
android:onClick="delete"
android:text="删除"
app:layout_constraintTop_toBottomOf="@id/add"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/delete"
android:onClick="delete"
android:text="删除"
app:layout_constraintTop_toBottomOf="@id/add"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/update"
android:onClick="update"
android:text="修改"
app:layout_constraintTop_toBottomOf="@id/delete"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/query_id"
android:onClick="query_id"
android:text="通过id查询"
app:layout_constraintTop_toBottomOf="@id/update"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/query_all"
android:onClick="query_all"
android:text="全部查询"
app:layout_constraintTop_toBottomOf="@id/query_id"/>
</android.support.constraint.ConstraintLayout>
MainActivity.java
package com.example.a4_11sqlite;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private DatabaseAdapter dbAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
dbAdapter = new DatabaseAdapter(this);
}
public void add(View view){
Dog dog = new Dog("被加入的狗",1);
dbAdapter.add(dog);
System.out.println("加入了一只狗");
}
public void delete(View view){
dbAdapter.delete(1);
System.out.println("删除了一只id为1的狗");
}
public void update(View view){
Dog dog = new Dog(1,"被修改的狗",2);
dbAdapter.update(dog);
System.out.println("修改了一只狗");
}
public void query_id(View view){
Dog dog = dbAdapter.findById(1);
System.out.println("查询id为1的结果是:"+dog);
}
public void query_all(View view){
ArrayList<Dog> dogs = dbAdapter.findAll();
int size = dogs.size();
for(int i = 0;i<size;i++){
System.out.println("查询全部的结果为:"+dogs.get(i));
}
}
}
测试效果
添加
id查询
修改
点击全部查询修改情况
删除
通过ID查询,id为1的狗已经不存在了
然后点击两次添加,再次点击查询全部
使用原生SQL语句
之前的案例利用dbAdapter调用封装好的增删改查对应方法实现了对SQLite数据的增删查改操作。
但是很多场景下,这些封装好的方法太过死板,只能实现基本操作,不能满足复杂需求。
能不能使用原生SQL语句来进行数据库操作?显然是可以的。
类似JAVA JDBC中的executeQuery和executeUpdate方法,安卓中也有两个方法可以输入参数SQL语句。
- rawQuery()方法:用于执行查询语句。
- execSQL()方法可以执行插入、更新、删除,或建立表单之类有更改行为的SQL语句。
在使用它们之前还需要完成一个操作:
- 通过databaseHelper.getWritableDatabase()或databaseHelper.getReadableDatabase()获取SQLiteDatabase对象
DatabaseHelper工具类仍为????部分的代码,下面提供四种操作的参考代码,效果和之前一样,就不演示了。
读者可以自行建四个按钮,然后在点击事件里分部调用下面几个方法并传入对应参数进行测试。
实际上下面的代码也可以考虑把sql作为一个参数,这样更加灵活一些,可以提高代码复用性。
插入
public void rawAdd(Dog dog){
SQLiteDatabase db = dbHelper.getWritableDatabase();
String sql="insert into dog(name,age) values(?,?)";
Object[] args={dog.getName(),dog.getAge()};
db.execSQL(sql,args);
db.close();
}
删除
public void rawDelete(int id){
SQLiteDatabase db = dbHelper.getWritableDatabase();
String sql="delete from dog where _id=?";
Object[] args={id};
db.execSQL(sql,args);
db.close();
}
修改(更新)
public void rawUpdate(Dog dog){
SQLiteDatabase db = dbHelper.getWritableDatabase();
String sql="update dog set name=?,age=? where _id=?";
Object[] args={dog.getName(),dog.getAge(),dog.getId()};
db.execSQL(sql,args);
db.close();
}
查询
public ArrayList<Dog> rawFindAll(){
SQLiteDatabase db= dbHelper.getReadableDatabase();
String sql="select * from dog";
Cursor c=db.rawQuery(sql,null);
ArrayList<Dog> dogs=new ArrayList<>();
Dog dog=null;
while (c.moveToNext()){
dog=new Dog();
dog.setId(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable._ID)));
dog.setName(c.getString(c.getColumnIndexOrThrow(PetMetaData.DogTable.NAME)));
dog.setAge(c.getInt(c.getColumnIndexOrThrow(PetMetaData.DogTable.AGE)));
dogs.add(dog);
}
c.close();
db.close();
return dogs;
}
使用事务
尽管SQLite是轻量级的数据库,但是它的功能依旧强大,和别的标准数据库一样,它同样提供了事务支持,遵循ACID原则。
//事务处理案例
public void transaction(){
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction();//开始事务
try{
db.execSQL("insert into dog(name,age) values('事务处理狗1','1')");
db.execSQL("insert into dog(name,age) values('事务处理狗2','2')");
db.setTransactionSuccessful();//设置事务成功的标志
}finally {
db.endTransaction();//(结束)提交事务,检查标记,如果为flase,回滚。
}
db.close();
}