持久化技术之SQLite 数据库存储
一、SQLite数据库
SQLite是一款轻量级的关系型数据库,运算速度非常快,占用资源少。支持标准的SQL语法,遵循数据库的ACID事务。不用设置用户名和密码即可使用。
二、SQLiteOpenHelper帮助类
- 这是一个抽象类,使用需要创建一个类继承它,并重写两个抽象方法(onCreate(…) && onUpgrade(…)),在这两个方法中去实现创建、升级数据库的逻辑。
- 两个重要方法:getReadableDatabase() 和 getWritableDatabase()
这两个方法都可以创建或打开一个现有的数据库,并返回一个可对数据库进行读写操作的对象。 - 两个构造函数,一般使用参数少的那个。
public 类名 (…) 四个参数:
参数一:Context;
参数二:(String)数据库名;
参数三:允许查询数据的时候返回一个自定义的Cursor,一般传入null;
参数四:当前数据库的版本号。 - 数据库文件会存放在/data/data//databases/目录下
- 使用 adb shell 来对数据库和表的创建情况进行检查。
三、对数据库进行CRUD操作
C添加(Create)R查询(Retrieve)U更新(Update)D删除(Delete)
使用 getReadableDatabase() 和 getWritableDatabase() 返回的 SQLiteDatabase 对象对数据进行CRUD操作。
1. 添加数据 insert(…)方法:3个参数
参数一:表名;
参数二:在未指定添加数据的情况下给某些可为空的列自动赋值NULL;
参数三:ContentValues对象 提供一系列put(列名,数据)方法重载。
2. 更新数据 update(…)方法:4个参数
参数一:表名;
参数二:ContentValues对象 提供一系列put(列名,数据)方法重载;将要修改的数据放入其中;
参数三:对应于SQL语句的WHERE部分,这里表示更新所有name等于?的行?是一个占位符,通过参数四赋值;
参数四:字符串数组 来指定相应内容。
第三、四个参数用于约束更新某一行或某几行中的数据,不指定的话默认更新所有行。
3. 删除数据 delete(…)方法:三个参数
参数一:表名;
参数二:WHERE条件;
参数三:字符串数组。
第二、三个参数用于约束删除某一行或某几行中的数据,不指定的话默认删除所有行。
4. 查询数据 query(…)方法:七个参数
参数一:表名;
参数二:用于查询哪几列,不指定则默认查询所有列;
第三(WHERE)、四(为占位符提供具体的值)个参数用于约束查询某一行或某几行中的数据,不指定的话默认查询所有行;
参数五:用于指定需要group by的列,不指定则表示不对查询结果进行group by操作;
参数六:对group by 之后的数据进行进一步的过滤,不指定则表示不过滤;
参数七:对查询结果指定排序方式,不指定则表示使用默认的排序方式。
四、Demo 代码
1. 效果界面:
2. 具体代码:
A.)activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.thinkpad.databasetest.MainActivity">
<!--创建数据表按钮-->
<Button
android:id="@+id/create_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Create database"/>
<!--添加数据按钮-->
<Button
android:id="@+id/add_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Add data"/>
<!--更新数据按钮-->
<Button
android:id="@+id/update_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Update data"/>
<!--删除数据按钮-->
<Button
android:id="@+id/delete_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Delete data"/>
<!--查询数据按钮-->
<Button
android:id="@+id/query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Query data"/>
<!--用于显示数据库的查询结果-->
<EditText
android:id="@+id/show_query_data"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
B.)MyDatabaseHelper.java
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class MyDatabaseHelper extends SQLiteOpenHelper {
//建表语句1
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement,"
+ "author text,"
+ "price real,"
+ "pages inteegr,"
+ "name text)";
//建表语句2
public static final String CREATE_CATEGORY = "create table Category("
+ "id integer primary key autoincrement,"
+ "category_name text,"
+ "category_code integer)";
private Context mContext;
//构造器 参数一:上下文;参数二:数据库名;参数三:自定义的Cursor,一般为null;参数四:数据库的版本号
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,int version){
super(context,name,factory,version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
db.execSQL(CREATE_CATEGORY);
Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
}
}
C.)MainActivity.java
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
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 createBase = (Button)findViewById(R.id.create_database);
createBase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dbHelper.getWritableDatabase();
}
});
//添加数据
Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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);
//插入第一条数据
//参数一:表名;
//参数二:在未指定添加数据的情况下给某些可为空的列自动赋值NULL;
//参数三:ContentValues对象 提供一系列put(列名,数据)方法重载
db.insert("Book",null,values);
//开始组装第二条数据
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);
}
});
//更新数据 将名字为The Da Vinci Code 的这本书的价格改为10.99
Button updateButton = (Button)findViewById(R.id.update_data);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price",10.99);
//更新数据
//参数一:表名;
//参数二:ContentValues对象 提供一系列put(列名,数据)方法重载;将要修改的数据放入其中;
//参数三:对应于SQL语句的WHERE部分,这里表示更新所有name等于?的行
//?是一个占位符,通过参数四:字符串数组 来指定相应内容。
//第三、四个参数用于约束更新某一行或某几行中的数据,不指定的话默认更新所有行
db.update("Book",values,"name=?",new String[]{"The Da Vinci Code"});
}
});
//删除数据
//参数一:表名;
//参数二:WHERE条件;
//参数三:字符串数组;
//第二、三个参数用于约束删除某一行或某几行中的数据,不指定的话默认删除所有行
Button deleteButton = (Button)findViewById(R.id.delete_data);
updateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book","pages > ? ",new String[]{"500"});
}
});
//查询数据
//参数一:表名;
//参数二:用于查询哪几列,不指定则默认查询所有列;
//第三(WHERE)、四(为占位符提供具体的值)个参数用于约束查询某一行或某几行中的数据,不指定的话默认查询所有行;
//参数五:用于指定需要group by的列,不指定则表示不对查询结果进行group by操作;
//参数六:对group by 之后的数据进行进一步的过滤,不指定则表示不过滤;
//参数七:对查询结果指定排序方式,不指定则表示使用默认的排序方式。
Button queryButton = (Button)findViewById(R.id.query_data);
queryButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
//查询Book表中所有数据
Cursor cursor = db.query("Book",null,null,null,null,null,null);
TextView showQueryText = (TextView)findViewById(R.id.show_query_data);
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);
String text = name + " " + author;
showQueryText.setText(text);
}while(cursor.moveToNext());
}
cursor.close();
}
});
}
}
五、使用 LitePal 操作数据库
LitePal 是一款开源得到Android数据库框架,采用对象关系映射(ORM)的模式,将一些常用的数据库功能进行了封装。
使用文档:https://github.com/LitePalFramework/LitePal
1. 基本配置说明:
A.)编辑app/build.gradle文件,在dependencies闭包中添加:
implementation 'org.litepal.android:core:3.0.0'
固定部分:implementation ‘org.litepal.android:core:’
变动部分:3.0.0 版本号
B.)在main目录下新建目录assets,再在这个目录下新建文件litepal.xml
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value="BookStore"></dbname><!--用于指定数据库的名称-->
<version value="1"></version><!--用于指定数据库的版本号-->
<list></list><!--用于指定所有的映射模型-->
</litepal>
C.)配置项目的application(AndroidManifest.xml)
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.thinkpad.litepaltest">
<application
android:name="org.litepal.LitePalApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2. 对象关系映射
将面向对象的语言和面向关系的数据库之间建立一种映射关系。
3. 创建数据库
A.)新建Book类
//Book类会对应数据库中的Book表
//类中的每一个字段对应表中的每一个列
public class Book {
private int id;
private String author;
private double price;
private int pages;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public int getPages() {
return pages;
}
public void setPages(int pages) {
this.pages = pages;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
B.)将Book类添加到映射模型列表当中(litepal.xml)
<?xml version="1.0" encoding="utf-8" ?>
<litepal>
<dbname value="BookStore"></dbname><!--用于指定数据库的名称-->
<version value="1"></version><!--用于指定数据库的版本号-->
<list>
<mapping class="com.example.thinkpad.litepaltest.Book"></mapping>
<!--使用<mapping>标签来声明我们要配置的映射模型类,注意一定要使用完整的类名-->
</list><!--用于指定所有的映射模型-->
</litepal>
C.)只要进行任意一次数据库的操作,BookStore.db数据库就会自动创建出来。在主函数的按钮的点击事件中,写一个最简单的数据库操作:Connector.getDatabase();点击按钮,数据库创建成功。
4. 升级数据库
修改想修改的内容,并且将数据库版本号+1即可。
想要给Book表添加一列,则直接修改Book.java在其中添加一个字段,并且设置它的get和set方法,版本号+1即可。
想要添加一张新的表,则创建一个新的类,写好字段及其对应get和set方法,然后将它添加到映射模型列表中,版本号+1即可。
5. 使用LitePal进行CRUD操作
LitePal进行表管理操作时不需要模型类有任何的继承结构,但是进行CRUD操作时就不行,必须要继承自DataSupport类才行。
A.) 添加数据
将Book类加上继承结构:extends DataSupport
创建模型实例,再将所有要存储的数据设置好,最后调用一下save();
Button addData = (Button)findViewById(R.id.add_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("Unknow");
book.save();//继承自DataSupport
}
});
B.) 更新数据
a.)最简单的一种更新方式就是对已存储的对象重新设值,然后重新调用save()即可。
用 model.isSaved() 方法判断对象是否已经存储。true 已存储 false 未存储
返回true的两种情况:
- 已经调用过model.save()方法去添加数据,此时的model会被认为已经存储。
- model对象是通过LitePal提供的查询API查出来的,由于是从数据库中查出来的对象,所有也认为是已存储的对象。
Button updateData = (Button)findViewById(R.id.update_data);
addData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("Unknow");
book.save();//继承自DataSupport
book.setPrice(10.99);
book.save();
}
});
b.)使用updateAll()方法
Button updateData = (Button)findViewById(R.id.update_data);
updateData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Book book = new Book(); //new 出一个Book实例
//设置要更新的数据
book.setPrice(14.95);
book.setPress("Anchor");
//调用updateAll()方法去执行更新操作
//该方法可以制定一个约束条件,若不指定,则表示更新所有数据
book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");
}
});
注意:在使用updateAll()时,当要把一个字段的值更新为默认值时是不可以使用上面的方式来set数据的,而是使用setToDefault(列名)方法
C.) 删除数据
a.)方法一:直接调用已存储对象的 delete() 方法。
b.)方法二:调用 DataSupport.deleteAll(…) 方法。
参数一:指定删除哪张表的数据。
剩下的参数:用于指定约束条件,和给占位符赋值。
如若不指定约束条件,则表示删除表中所有数据。
Button deleteData = (Button)findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
DataSupport.deleteAll(Book.class,"price < ?","15");
}
});
D.) 查询数据
List<Book> books = DataSupport.findAll(Book.class);//参数指定要查询的表
查询API:
- Book firstBook = DataSupport.findFirst(Book.class);//获取第一条数据
- Book lastBook = DataSupport.findLast(Book.class);//获取最后一条数据
- 可通过连缀查询定制更多查询功能:
- select()指定查询哪几列数据,对应SQL中的SELECT关键字
- where()指定查询的约束条件,对应SQL中的WHERE关键字
- order()指定结果的排序方式 desc降序 asc(默认值)升序,对应SQL中的ORDER BY关键字
- limit(n)指定查询结果的数量为n条数据。
- limit(n).offset(m)指定查询结果的偏移量,查询表中第m+1,m+2,…,m+n条数据。
- limit()和offset()方法共同对应了SQL中的LIMIT关键字
//查询Book表中第11-20条
//满足页数大于400这个条件的name,author和pages这3列数据,
//并将查询结果按照页数升序排列。
List<Book> books = DataSupport.select("name","author","pages")
.where("pages > ?","400")
.order("pages")
.limit(10)
.offset(10)
.find(Book.class);
E.) LitePal也支持原生的SQL查询,findBySQL(SQL语句,若干参数指定占位符的值),返回的是Cursor对象。
整理学习自郭霖大佬的《第一行代码》
目前小白一名,持续学习Android中,如有错误请批评指正!