本节介绍Android的数据库存储方式--SQLite的使用方法,包括:SQLite用到了哪些SQL语法,如何使用数据库管理操纵SQLitem,如何使用数据库帮助器简化数据库操作,以及如何利用SQLite改进登录页面的记住密码功能。
6.2.1 SQL的基本语法
SQL本质上是一种编程语言,它的学名叫做“结构化查询语言”(全称为Structured Query Language,简称SQL)。不过SQL语言并非通用的编程语言,它专用于数据库的访问和处理,更像是一种操作命令,所以常说SQL语句而不说SQL代码。标准的SQL语句分为3类:数据定义,数据操纵和数据控制。但不同的数据库往往有自己的实现。
SQLite是一种小巧的嵌入式数据库,使用方便,开发简单。如同MYSQL,Oracle那样,SQLite也采用SQL语句管理数据,由于它属于轻型数据库,不涉及复杂的数据控制操作,因此App开发只用到数据定义和数据操纵两类SQL语句。此外,SQLite的SQL语法与通用的SQL语法略有不同,接下来介绍的两类SQL语法全部基于SQLite。
1. 数据定义语言
数据定义语言(全称Data Definition Language,简称DDL)描述了怎样变更数据实体的框架结构。就SQLite而言,DDL语言主要包括3种操作:创建表格,删除表格,修改表结构,分别说明如下。
(1)创建表格
表格的创建动作由create命令完成,格式为“CREATE TABLE IF NOT EXISTS 表格名称(以逗号分隔的名字段定义);”。以用户信息表为例,它的建表语句如下:
CREATE TABLE IF NOT EXISTS user_info(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR NOT NULL, age INTEGER NOT NULL,
height LONG NOT NULL, weight FLOAT NOT NULL,
married INTEGER NOT NULL, update_time VARCHAR NOT NULL);
上面的SQL语法与其他数据库的SQL语法有所出入,相关的注意点说明如下:
① SQL语句不区分大小写,无论是create与table这类关键词,还是表格名称,字段名称都不区分大小写。唯一区分大小写的是被单引号括起来的字符串值。
② 为避免重复建表,应加上IF NOT EXISTS关键词,例如CREATE TABLE IF NOT EXISTS 表格名称 ▪▪▪▪
③ SQLite支持整型INTEGER,长整型LONG,字符串VARCHAR,浮点型FLOAT,但不支持布尔类型。布尔类型的数据要使用整型保存,如果直接保存布尔数据,在入库时SQLite会自动将它转为0或1,其中0表示false,1表示true。
④ 建表时需要唯一标识字段,它的字段名为_id。创建新表都要加上该字段定义,例如_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL。
(2)删除表格
表格的删除动作由drop命令完成,格式为“DROP TABLE IF EXISTS 表格名称;”。下面是删除用户信息的SQL语句例子:
DROP TABLE IF EXISTS user_info;
(3)修改表结构
表格的修改动作由alter命令完成,格式为“ALTER TABLE 表格名称 修改操作;”。不过SQLite只支持增加字段,不支持修改字段,也不支持删除字段。对于字段增加操作,需要在alter之后补充add命令,具体格式如“ALTER TABLE 表格名称 ADD COLUMN 字段名称 字段类型;”。下面是给用户信息表增加手机号字段的SQL语句例子:
ALTER_TABLE user_info ADD COLUMN phone VARCHAR;
2. 数据操纵语言
数据操纵语言(全称Data Manipulation Language,简称DML)描述了怎样处理数据实体的内部记录。表格记录的操作类型包括添加,删除,修改,查询4类,分别说明如下:
(1)添加记录
记录的添加动作由insert命令完成,格式为“INSERT INTO 表格名称(以逗号分隔的字段名列表)VALUES(以逗号分隔的字段值列表);”。下面是往用户信息表插入一条记录的SQL语句例子:
INSERT INTO user_info (name,age,height,weight,married,update_time)
VALUES ('张三',20,170,50,0,'20200504');
(2)删除记录
记录的删除动作由delete命令完成,格式为“DELETE FROM 表格名称 WHERE 查询条件;”,其中查询条件的表达式形如“字段名=字段值”,多个字段的条件交集通过“AND”连接,条件并集通过“OR”连接。下面是从用户信息表删除指定记录的SQL语句例子:
DELETE FROM user_info WHERE name='张三';
(3)修改记录
记录的修改动作由update命令完成,格式为“UPDATE 表格名称 SET 字段名=字段值 WHERE 查询条件;”。下面是对用户信息表更新指定记录的SQL语句例子:
UPDATE user_info SET married=1 WHERE name='张三';
(4)查询记录
记录的查询动作由select命令完成,格式为“SELECT 以逗号分隔的字段名列表 FROM 表格名称 WHERE 查询条件;”。如果字段名列表填星号(*),则表示查询该表的所有字段。下面是从用户信息表查询指定记录的SQL语句例子:
SELECT*FROM user_info ORDER BY age ASC;
6.2.2 数据库管理器 SQLiteDatabase
SQL语句毕竟只是SQL命令,若要在Java代码中操纵SQLite,还需专门的工具类。SQLiteDatabase便是Android提供的SQLite数据库管理器,开发者可以在活动页面代码中调用openOrCreateDatabase方法获得数据库实例,参考代码如下:
SQLiteDatabase db = openOrCreateDatabase(mDatabaseName, Context.MODE_PRIVATE,null);
String desc = String.format("数据库%s创建%s", db.getPath(), (db!=null)?"成功":"失败");
tv_database.setText(desc);
完整代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".DatabaseActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_database_create"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="创建数据库"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_database_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="删除数据库"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<TextView
android:id="@+id/tv_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
package com.example.datastorage;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class DatabaseActivity extends AppCompatActivity implements View.OnClickListener {
private TextView tv_database; // 声明一个文本视图对象
private String mDatabaseName;// 包含完整路径的数据库名称
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_database);
tv_database=findViewById(R.id.tv_database);
findViewById(R.id.btn_database_create).setOnClickListener(this);
findViewById(R.id.btn_database_delete).setOnClickListener(this);
// 生成一个测试数据库的完整路径
mDatabaseName=getFilesDir()+"/test.db";
}
@Override
public void onClick(View view) {
if (view.getId()==R.id.btn_database_create){
// 创建或打开数据库。数据库如果不存在就创建它,如果存在就打开它
SQLiteDatabase db = openOrCreateDatabase(mDatabaseName, Context.MODE_PRIVATE,null);
String desc = String.format("数据库%s创建%s", db.getPath(), (db!=null)?"成功":"失败");
tv_database.setText(desc);
} else if (view.getId()==R.id.btn_database_delete) {
boolean result = deleteDatabase(mDatabaseName);// 删除数据库
String desc = String.format("数据库%s删除%s", mDatabaseName, result?"成功":"失败");
tv_database.setText(desc);
}
}
}
首次运行测试App,调用openOrCreateDatabase方法会自动创建数据库,并返回该数据库的管理器实例,创建结果如图所示。
获得数据库实例之后,就能对该数据库开展各项操作了。数据库管理器SQLiteDatabase提供了若干操作数据表的API,常用的方法有3类,列举如下:
1. 管理类,用于数据库层面的操作
● openDatabase:打开指定路径的数据库。
● isOpen:判断数据库是否已打开。
● close:关闭数据库。
● getVersion:获取数据库的版本号。
● setVersion:设置数据库的版本号。
2. 事务类,用于事务层面的操作
● beginTransaction:开始事务。
● setTransactionSuccessful:设置事务的成功标志。
● endTransaction:结束事务。执行本方法时,系统会判断之前是否调用了 setTransactionSuccessful方法,如果之前已调用该方法就提交事务,如果没有调用该方法就 回滚事务。
3. 数据处理类,用于数据表层面的操作
● execSQL:执行拼接好的SQL控制语句。一般用于建表,删表,变更表结构。
● delete:删除符合条件的记录。
● update:更新符合条件的记录信息。
● insert:插入一条记录。
● query:执行查询操作,并返回结果集的游标。
● rawQuery:执行拼接好的SQL查询语句,并返回结果集的游标。
在实际开发中,经常用到的是查询语句,建议写好查询操作的select语句,再调用rawQuery方法执行查询语句。
6.2.3 数据库帮助器 SQLiteOpenHelper
由于SQLiteDatabase存在局限性,一不小心就会重复打开数据库,处理数据库的升级也不方便,因此Android提供了数据库帮助器SQLiteOpenHelper,帮助开发者合理使用SQLite。
SQLiteOpenHelper的具体使用步骤如下:
01 新建一个继承SQLiteOpenHelper的数据库操作类,按提示重写onCreate和onUpgrade两个方 法。其中,onCreate方法只在第一次打开数据库时执行,在此可以创建表结构;而onUpgrade 方法在数据库版本升高时执行,在此可以根据新旧版本号变更表结构。
02 为保证数据库的安全使用,需要封装几个必要方法,包括获取单例对象,打开数据库连接,关 闭数据库连接,说明如下:
● 获取单例对象,确保在App运行过程中数据库只会打开一次,避免重复打开引起错误。
● 打开数据库连接:SQLite有锁机制,即读锁和写锁的处理,故而数据库连接也分两种,读 连接可调用getReadableDatabase方法获得,写连接可调用getWritableDatabase方法获得。
● 关闭数据库连接,数据库操作完毕,调用数据库实例的close方法关闭连接。
03 提供对表记录增加,删除,修改,查询的操作方法。
能被SQLite直接使用的数据结构是ContentValues类,它类似于映射Map,也提供了put和get方法存取键值对。区别之处在于:ContentValues的键只能是字符串,不能是其他类型。ContentValues主要用于增加记录和更新记录,对应数据库的insert和update方法。
记录的查询操作用到了游标类Cursor,调用query和rawQuery方法返回的都是Cursor对象,若要获取全部的查询结果,则需要根据游标的指示一条一条遍历结果集合。Cursor的常用方法可分为3类,说明如下:
1. 游标控制类方法,用于指定游标的状态
● close:关闭游标。
● isClosed:判断游标是否关闭。
● isFirst:判断游标是否在开头。
● isLast:判断游标是否在末尾。
2. 游标移动类方法,把游标移动到指定位置
● moveToFirst:移动游标到开头。
● moveToLast:移动游标到末尾。
● moveToNext:移动游标到下一条记录。
● moveToPrevious:移动游标到上一条记录。
● move:往后移动游标若干条记录。
● moveToPosition:移动游标到指定位置的记录。
3. 获取记录类方法,可获取记录的数量,类型以及取值
● getCount:获取结果记录的数量。
● getInt:获取指定字段的整型值。
● getLong:获取指定字段的长整数型值。
● getFloat:获取指定字段的浮点数值。
● getString:获取指定字段的字符串值。
● getType:获取指定字段的字段类型。
鉴于数据库操作的特殊性,不方便单独演示某个功能,接下来从创建数据库开始介绍,完整演示一下数据库的读写操作。用户注册信息的演示页面包括两个,分别是记录保存页面和记录读取页面,其中记录保存页面通过insert方法向数据库添加用户信息,完整代码如下:
package com.example.datastorage;
public class UserInfo {
public long rowid; // 行号
public int xuhao; // 序号
public String name; // 姓名
public int age; // 年龄
public long height; // 身高
public float weight; // 体重
public boolean married; // 婚否
public String update_time; // 更新时间
public String phone; // 手机号
public String password; // 密码
public UserInfo() {
rowid = 0L;
xuhao = 0;
name = "";
age = 0;
height = 0L;
weight = 0.0f;
married = false;
update_time = "";
phone = "";
password = "";
}
}
package com.example.datastorage;
import static android.provider.SyncStateContract.Helpers.update;
import static androidx.core.content.ContentResolverCompat.query;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class UserDBHelper extends SQLiteOpenHelper {
private static final String TAG = "UserDBHelper";
private static final String DB_NAME = "user.db"; // 数据库的名称
private static final int DB_VERSION = 1; // 数据库的版本号
private static UserDBHelper mHelper = null; // 数据库帮助器的实例
private SQLiteDatabase mDB = null; // 数据库的实例
public static final String TABLE_NAME = "user_info"; // 表的名称
private UserDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
private UserDBHelper(Context context, int version) {
super(context, DB_NAME, null, version);
}
// 利用单例模式获取数据库帮助器的唯一实例
public static UserDBHelper getInstance(Context context, int version) {
if (version > 0 && mHelper == null) {
mHelper = new UserDBHelper(context, version);
} else if (mHelper == null) {
mHelper = new UserDBHelper(context);
}
return mHelper;
}
// 打开数据库的读连接
public SQLiteDatabase openReadLink() {
if (mDB == null || !mDB.isOpen()) {
mDB = mHelper.getReadableDatabase();
}
return mDB;
}
// 打开数据库的写连接
public SQLiteDatabase openWriteLink() {
if (mDB == null || !mDB.isOpen()) {
mDB = mHelper.getWritableDatabase();
}
return mDB;
}
// 关闭数据库连接
public void closeLink() {
if (mDB != null && mDB.isOpen()) {
mDB.close();
mDB = null;
}
}
// 创建数据库,执行建表语句
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
Log.d(TAG, "onCreate");
String drop_sql = "DROP TABLE IF EXISTS " + TABLE_NAME + ";";
Log.d(TAG, "drop_sql:" + drop_sql);
sqLiteDatabase.execSQL(drop_sql);
String create_sql = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " ("
+ "_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
+ "name VARCHAR NOT NULL," + "age INTEGER NOT NULL,"
+ "height INTEGER NOT NULL," + "weight FLOAT NOT NULL,"
+ "married INTEGER NOT NULL," + "update_time VARCHAR NOT NULL"
//演示数据库升级时要先把下面这行注释
+ ",phone VARCHAR" + ",password VARCHAR"
+ ");";
Log.d(TAG, "create_sql:" + create_sql);
sqLiteDatabase.execSQL(create_sql); // 执行完整的SQL语句
}
// 升级数据库,执行表结构变更语句
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
Log.d(TAG, "onUpgrade oldVersion=" + i + ", newVersion=" + i1);
if (i1 > 1) {
//Android的ALTER命令不支持一次添加多列,只能分多次添加
String alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "phone VARCHAR;";
Log.d(TAG, "alter_sql:" + alter_sql);
sqLiteDatabase.execSQL(alter_sql);
alter_sql = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + "password VARCHAR;";
Log.d(TAG, "alter_sql:" + alter_sql);
sqLiteDatabase.execSQL(alter_sql); // 执行完整的SQL语句
}
}
// 根据指定条件删除表记录
public int delete(String condition) {
// 执行删除记录动作,该语句返回删除记录的数目
return mDB.delete(TABLE_NAME, condition, null);
}
// 删除该表的所有记录
public int deleteAll() {
// 执行删除记录动作,该语句返回删除记录的数目
return mDB.delete(TABLE_NAME, "1=1", null);
}
// 往该表添加一条记录
public long insert(UserInfo info) {
List<UserInfo> infoList = new ArrayList<UserInfo>();
infoList.add(info);
return insert(infoList);
}
// 往该表添加多条记录
public long insert(List<UserInfo> infoList) {
long result = -1;
for (int i = 0; i < infoList.size(); i++) {
UserInfo info = infoList.get(i);
List<UserInfo> tempList = new ArrayList<UserInfo>();
// 如果存在同名记录,则更新记录
// 注意条件语句的等号后面要用单引号括起来
if (info.name != null && info.name.length() > 0) {
String condition = String.format("name='%s'", info.name);
tempList = query(condition);
if (tempList.size() > 0) {
update(info, condition);
result = tempList.get(0).rowid;
continue;
}
}
// 如果存在同样的手机号码,则更新记录
if (info.phone != null && info.phone.length() > 0) {
String condition = String.format("phone='%s'", info.phone);
tempList = query(condition);
if (tempList.size() > 0) {
update(info, condition);
result = tempList.get(0).rowid;
continue;
}
}
// 不存在唯一性重复的记录,则插入新记录
ContentValues cv = new ContentValues();
cv.put("name", info.name);
cv.put("age", info.age);
cv.put("height", info.height);
cv.put("weight", info.weight);
cv.put("married", info.married);
cv.put("update_time", info.update_time);
cv.put("phone", info.phone);
cv.put("password", info.password);
// 执行插入记录动作,该语句返回插入记录的行号
result = mDB.insert(TABLE_NAME, "", cv);
if (result == -1) { // 添加成功则返回行号,添加失败则返回-1
return result;
}
}
return result;
}
// 根据条件更新指定的表记录
public int update(UserInfo info, String condition) {
ContentValues cv = new ContentValues();
cv.put("name", info.name);
cv.put("age", info.age);
cv.put("height", info.height);
cv.put("weight", info.weight);
cv.put("married", info.married);
cv.put("update_time", info.update_time);
cv.put("phone", info.phone);
cv.put("password", info.password);
// 执行更新记录动作,该语句返回更新的记录数量
return mDB.update(TABLE_NAME, cv, condition, null);
}
public int update(UserInfo info) {
// 执行更新记录动作,该语句返回更新的记录数量
return update(info, "rowid=" + info.rowid);
}
// 根据指定条件查询记录,并返回结果数据列表
public List<UserInfo> query(String condition) {
String sql = String.format("select rowid,_id,name,age,height," +
"weight,married,update_time,phone,password " +
"from %s where %s;", TABLE_NAME, condition);
Log.d(TAG, "query sql: " + sql);
List<UserInfo> infoList = new ArrayList<UserInfo>();
// 执行记录查询动作,该语句返回结果集的游标
Cursor cursor = mDB.rawQuery(sql, null);
// 循环取出游标指向的每条记录
while (cursor.moveToNext()) {
UserInfo info = new UserInfo();
info.rowid = cursor.getLong(0); // 取出长整型数
info.xuhao = cursor.getInt(1); // 取出整型数
info.name = cursor.getString(2); // 取出字符串
info.age = cursor.getInt(3); // 取出整型数
info.height = cursor.getLong(4); // 取出长整型数
info.weight = cursor.getFloat(5); // 取出浮点数
//SQLite没有布尔型,用0表示false,用1表示true
info.married = (cursor.getInt(6) == 0) ? false : true;
info.update_time = cursor.getString(7); // 取出字符串
info.phone = cursor.getString(8); // 取出字符串
info.password = cursor.getString(9); // 取出字符串
infoList.add(info);
}
cursor.close(); // 查询完毕,关闭数据库游标
return infoList;
}
// 根据手机号码查询指定记录
public UserInfo queryByPhone(String phone) {
UserInfo info = null;
List<UserInfo> infoList = query(String.format("phone='%s'", phone));
if (infoList.size() > 0) { // 存在该号码的登录信息
info = infoList.get(0);
}
return info;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:padding="5dp"
tools:context=".SQLiteWriteActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="56dp">
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="姓名:"
android:gravity="center"
android:textSize="17sp"/>
<EditText
android:id="@+id/et_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_name"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入姓名"
android:inputType="text"
android:maxLength="12"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_age"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="年龄:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_age"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_age"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入年龄"
android:inputType="number"
android:maxLength="2"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_height"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="身高:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_height"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_height"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入身高"
android:inputType="number"
android:maxLength="3"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<TextView
android:id="@+id/tv_weight"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="体重:"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_weight"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="3dp"
android:layout_marginTop="3dp"
android:layout_toRightOf="@+id/tv_weight"
android:background="@drawable/editext_selector"
android:gravity="left|center"
android:hint="请输入体重"
android:inputType="numberDecimal"
android:maxLength="5"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="40dp" >
<CheckBox
android:id="@+id/ck_married"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:checked="false"
android:text="已婚"
android:textColor="@color/black"
android:textSize="17sp" />
</RelativeLayout>
<Button
android:id="@+id/btn_save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存到数据库"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_jump"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="跳转到数据库"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
package com.example.datastorage;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Toast;
public class SQLiteWriteActivity extends AppCompatActivity implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
private UserDBHelper mHelper; // 声明一个用户数据库帮助器的对象
private EditText et_name; // 声明一个编辑框对象
private EditText et_age; // 声明一个编辑框对象
private EditText et_height; // 声明一个编辑框对象
private EditText et_weight; // 声明一个编辑框对象
private boolean isMarried = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_write);
et_name = findViewById(R.id.et_name);
et_age = findViewById(R.id.et_age);
et_height = findViewById(R.id.et_height);
et_weight = findViewById(R.id.et_weight);
CheckBox ck_married = findViewById(R.id.ck_married);
ck_married.setOnCheckedChangeListener(this);
findViewById(R.id.btn_save).setOnClickListener(this);
findViewById(R.id.btn_jump).setOnClickListener(this);
}
@Override
protected void onStart() {
super.onStart();
// 获得数据库帮助器的实例
mHelper = UserDBHelper.getInstance(this,1);
mHelper.openWriteLink();// 打开数据库帮助器的写连接
}
@Override
protected void onStop() {
super.onStop();
mHelper.closeLink();// 关闭数据库连接
}
@Override
public void onClick(View view) {
if (view.getId()==R.id.btn_save){
String name = et_name.getText().toString();
String age = et_age.getText().toString();
String height = et_height.getText().toString();
String weight = et_weight.getText().toString();
if (TextUtils.isEmpty(name)) {
Toast.makeText(this, "请先填写姓名",Toast.LENGTH_LONG).show();
return;
} else if (TextUtils.isEmpty(age)) {
Toast.makeText(this, "请先填写年龄",Toast.LENGTH_LONG).show();
return;
} else if (TextUtils.isEmpty(height)) {
Toast.makeText(this, "请先填写身高",Toast.LENGTH_LONG).show();
return;
} else if (TextUtils.isEmpty(weight)) {
Toast.makeText(this, "请先填写体重",Toast.LENGTH_LONG).show();
return;
}
// 以下声明一个用户信息对象,并填写它的各字段值
UserInfo info = new UserInfo();
info.name = name;
info.age = Integer.parseInt(age);
info.weight = Float.parseFloat(weight);
info.married = isMarried;
info.update_time = DateUtil.getNowDateTime("yyyy-MM-dd HH:mm:ss");
mHelper.insert(info);// 执行数据库帮助器的插入操作
Toast.makeText(this, "数据已写入SQLite数据库",Toast.LENGTH_LONG).show();
} else if (view.getId()==R.id.btn_jump) {
Intent intent = new Intent(this, SQLiteReadActivity.class);
startActivity(intent);
}
}
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
isMarried = b;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".SQLiteReadActivity">
<Button
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="删除所有记录"
android:textSize="17sp"/>
<TextView
android:id="@+id/tv_sqlite"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
package com.example.datastorage;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
public class SQLiteReadActivity extends AppCompatActivity implements View.OnClickListener {
private UserDBHelper mHelper; // 声明一个用户数据库帮助器的对象
private TextView tv_sqlite; // 声明一个文本视图对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sqlite_read);
tv_sqlite = findViewById(R.id.tv_sqlite);
findViewById(R.id.btn_delete).setOnClickListener(this);
}
@Override
protected void onStart() {
super.onStart();
// 获得数据库帮助器的实例
mHelper = UserDBHelper.getInstance(this, 1);
mHelper.openReadLink(); // 打开数据库帮助器的读连接
readSQLite(); // 读取数据库中保存的所有用户记录
}
@Override
protected void onStop() {
super.onStop();
mHelper.closeLink(); // 关闭数据库连接
}
// 读取数据库中保存的所有用户记录
private void readSQLite() {
if (mHelper == null) {
Toast.makeText(this, "数据库连接为空", Toast.LENGTH_SHORT).show();
return;
}
// 执行数据库帮助器的查询操作
List<UserInfo> userList = mHelper.query("1=1");
String desc = String.format("数据库查询到%d条记录,详情如下:", userList.size());
for (int i = 0; i < userList.size(); i++) {
UserInfo info = userList.get(i);
desc = String.format("%s\n第%d条记录信息如下:", desc, i + 1);
desc = String.format("%s\n 姓名为%s", desc, info.name);
desc = String.format("%s\n 年龄为%d", desc, info.age);
desc = String.format("%s\n 身高为%d", desc, info.height);
desc = String.format("%s\n 体重为%f", desc, info.weight);
desc = String.format("%s\n 婚否为%b", desc, info.married);
desc = String.format("%s\n 更新时间为%s", desc, info.update_time);
}
if (userList.size() <= 0) {
desc = "数据库查询到的记录为空";
}
tv_sqlite.setText(desc);
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_delete) {
mHelper.closeLink(); // 关闭数据库连接
mHelper.openWriteLink(); // 打开数据库帮助器的写连接
mHelper.deleteAll(); // 删除所有记录
mHelper.closeLink(); // 关闭数据库连接
mHelper.openReadLink(); // 打开数据库帮助器的读连接
readSQLite(); // 读取数据库中保存的所有用户记录
Toast.makeText(this, "已删除所有记录", Toast.LENGTH_SHORT).show();
}
}
}
运行测试App,先打开记录保存页面,依次录入信息并将两个用户的注册信息保存至数据库,如图所示。