暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Android中的数据存取 | 实战演练——订单处理

663

“欢乐购商城”项目中订单页面、购物车页面的订单数据是在用户将商品加入购物车的时候将商品数据保存在数据库中,当打开订单页面和购物车页面的时候从数据库读取商品数据,如图7-13所示。


■ 图7-13使用SQLite加载数据


前两篇介绍了 如何使用文件存储SharedPreferences存储数据 ,但这两种方式只适合存储一些简单的数据。如果想存储结构复杂的数据,需要用到数据库。在Android平台上,嵌入了一个轻量级的关系型数据库SQLite,它可以存储应用程序中的大量数据,包含操作本地数据的所有功能,简单易用、反应快。


1

SQLite数据库简介

SQLite内部只支持NULL、INTEGER、REAL (浮点数)、TEXT(字符文本)和BLOB(二进制对象)这五种数据类型,但实际上,SQLite也接受varchar(n)、char(n)、decimal(p,s)等数据类型,只不过在运算或保存时会转成上面对应的数据类型。

SQLite最大的特点是可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如,可以把字符串类型的值存入INTEGER类型字段中,或者在布尔型字段中存放数值类型等。但有一种情况例外: 定义为INTEGER PRIMARYKEY的字段只能存储64位整数,当向这种字段保存除整数以外的数据时,SQLite会产生错误。由于SQLite允许存入数据时忽略底层数据列实际的数据类型,因此SQLite在解析建表语句时,会忽略建表语句中跟在字段名后面的数据类型信息。因此在编写建表语句时可以省略数据列后面的类型声明。SQLite允许开发者使用SQL语句操作数据库中的数据,并且SQLite数据库不需要安装、启动服务进程,其底层只是一个数据库文件。本质上,SQLite的操作方式只是一种更为便捷的文件操作。


2

SQLite数据库的创建


在Android系统中提供了相关的类帮助我们创建数据库,其中,SQLiteOpenHelper是Android提供的管理数据的工具类,主要用于数据库的创建、打开和版本更新。一般用法是创建SQLiteOpenHelper类的子类,并重写父类的onCreate()和onUpgrade()方法(这两个方法是抽象的,必须重写)。

SQLiteOpenHelper包含如下常用方法。

(1) abstract void onCreate(SQLiteDatabase db): 当数据库第一次被创建的时候调用该方法。

(2) abstract void onUpgrade(SQL iDatabre db, int oldVersion, int newVersion): 当数据库需要更新的时候调用该方法。

(3) void onOpen(SQLiteDatabase db): 当数据库打开时调用该方法。

(4) SQLiteDatabase getWritableDatabase(): 以写的方式打开数据库对应的SQLiteDatabase对象,一旦打开成功,将会缓存该数据库对象。

(5) SQLiteDatabase getReadableDatabase(): 以读写的方式打开数据库对应的SQLiteDatabase对象,该方法内部调用getWritableDatabase()方法,返回对象与getWritableDatabase()返回对象一致,除非数据库的磁盘空间满了,此时getWritableDatabase()打开数据库就会出错,当打开失败后getReadableDatabase()方法会继续尝试以只读方式打开数据库。

(6) 当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获取 SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,然后调用onCreate()方法,在onCreate()方法中可以初始化表结构。onUpgrade()方法在数据库版本号发生变化时会被调用,一般在软件升级需要修改表结构的时候需要升级数据库版本号,假设数据库原有版本是1,当要修改表结构时就需要升级版本号,此时可以设置数据库版本号2,并在onUpgrade()方法中实现表结构的更新。onUpgrade()方法在数据库版本号增加时会被调用,并做出相应表结构和数据的更新,如果版本号不增加,则该方法不会被调用。

在项目中新建类SQLiteHelper,用于生成数据库,示例代码如下。

程序清单7-9: chart0705\app\src\main\java\com\example\administrator\chart0705sqliteSQLiteHelperjava

1  public class SQLiteHelper extends SQLiteOpenHelper {

2      public SQLiteHelper(Context context) {

3  
4          super(context, "userInfo.db", null, 1);
5      }
6  
7      @Override
8      public void onCreate(SQLiteDatabase db) {

9          db.execSQL("create table if not exists" + " userinfo" + "(" + "_id integer primary key " +
10                  "autoincrement," + "user_name varchar," + "user_pwd varchar," + "user_nick varchar"
11                  + ")");

12      }
13   public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

15  }


由上述代码中,首先创建了一个SQLiteHelper 类继承自SQLiteOpenHelper,并重写该类的构造方法 SQLiteHelper(),在该方法中通过super()调用父类SQLiteOpenHelper的构造方法,并传入4个参数,分别表示上下文对象、数据库名称、游标工厂(通常是null)、数据库版本。然后重写了onCreate() 和onUpgrade()方法,其中,onCreate()方法是在数据库第一次创建时调用,该方法通常用于初始化表结构。MainActivity.java中的代码如下。

程序清单7-10: chart0705\app\src\main\java\com\example\administratorchart0705MainActivityjava

1  public class MainActivity extends AppCompatActivity {

2      @Override  
3      protected void onCreate(Bundle savedInstanceState) {

4          super.onCreate(savedInstanceState);
5          setContentView(R.layout.activity_main);
6          SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
7          SQLiteDatabase database = sqLiteHelper.getWritableDatabase();
8      }
9  }


在上述代码中,首先生成SQLiteHelper 对象,调用该对象的getWritableDatabase()生成数据库。程序运行后,在Device File Explorer视图中找到数据库文件所在的路径data/data/com.example.administrator.chart0705/databases/userInfo.db,如图7-14所示。


■ 图图7-14数据库文件存储的位置


右键单击数据库文件userInfo.db,选择Save as可以将数据库文件保存在本地磁盘上。如果要查看数据库中的数据,可以使用SQLite Expert Personal可视化工具。可在官网http://www.sqliteexpert.com/download.html下载SQLite Expert Personal工具并进行安装,安装完成运行程序,结果如图7-15所示。


■ 图7-15SQLite Expert Personal界面


在SQLite Expert Personal工具中单击File→Open Database选项,选择需要查看的数据库文件,结果如图7-16所示。


■ 图7-16userinfo表结构


由图7-16可知,创建的数据库userInfo.db中的各个字段已经展示出来,当数据库中有新添加的数据时,也可通过SQLite Expert Personal可视化工具可以进行查看。


3

SQLite数据库的升级


创建好数据库之后,如果需要在数据库中添加一张订单表orderInfo,可以在SQLiteHelper类中的onCreate()中添加以下代码。

public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table if not exists" + " orderInfo" + "(" + "_id integer primary key " +"autoincrement," + "goodsName varchar(50)," + "goodsPrice varchar(10)," + "goodsLocation varchar(50)," + "shopName varchar(50),"+ "dateTime varchar(50)" + ")");
}


重新运行程序的时候,发现orderInfo表并没有创建成功,原因在于再次运行程序的时候userInfo数据库已经存在。再次运行程序的时候onCreate()并不会再次执行,因此新添加的表orderInfo就不会创建成功。

如果在数据库已经存在的情况下需要更新表时,就可以用onUpgrade()方法,实例代码如下。

程序清单7-11: chart0705\app\src\main\java\com\example\administrator\chart0705sqliteSQLiteHelper.java

1  public class SQLiteHelper extends SQLiteOpenHelper {

2      public SQLiteHelper(Context context) {

3          super(context, "userInfo.db", null, 2);
4      }
5      
6      @Override
7      public void onCreate(SQLiteDatabase db) {

8          db.execSQL("create table if not exists" + " userinfo" + "(" + "_id integer primary key " +
9                  "autoincrement," + "user_name varchar(20)," + "user_pwd varchar(20)," +
10                  "user_nick varchar(20) " + ")");

11          db.execSQL("create table if not exists" + " orderInfo" + "(" + "_id integer primary key "
12                  + "autoincrement," + "goodsName varchar(50)," + "goodsPrice varchar(10)," +
13                  "goodsLocation varchar(50)," + "shopName varchar(50)," + "dateTime varchar(50)" +
14                  ")");

15      }
16      @Override
17      public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

18          db.execSQL("drop table if exists userinfo");
19          db.execSQL("drop table if exists orderInfo");
20          onCreate(db);
21      }
22  }


在上述代码中,第17~21行代码onUpgrade()方法中,如果发现数据库中已经存在userinfo表和orderInfo表就将这两张表删除,之后再调用onCreate()方法创建这两张表。此外,如果想要onUpgrade()能够执行,在SQLiteHelper构造函数中要增加版本号,原来的版本号为1,所以修改SQLiteHelper构造函数中版本号为2,表示对数据库的升级。

再次运行程序,将数据库文件导出到本地磁盘,使用SQLite Expert Personal可视化工具查看数据库中的表,发现orderInfo表创建成功,如图7-17所示。

■ 图7-17orderInfo表结构


4

SQLite数据库的基本操作


前面介绍了如何创建SQLite数据库,接下来针对SQLite数据的增、删、改、查操作进行详细的讲解。实现对数据的增删改查主要使用SQLiteDatabase类。该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作。在该类中重点掌握 execSQL()和rawQuery()方法。execSQL()方法可以执行insert、delete、update和create table之类有更改行为的SQL语句,而rawQuery()方法用于执行查询语句。

(1) execSQL(String sql,Object[] bindArgs): 执行带占位符的SQL语句,如果SQL中没有占位符,则第二个参数可传null。

(2) execSQL(String sql): 执行SQL语句。

(3) rawQuery(String sql,String[] selectionArgs): 执行带占位符的SQL查询。

除了execSQL()和rawQuery()方法,SQLiteDatabase还专门提供了对应于添加、删除、更新、查询的操作方法insert()、delete()、update()和query()。实际上,这些方法内部也是执行SQL语句,由底层用StringBuilder根据这些参数拼接成完整的SQL语句。

例如: Cursor query(String table, String[] columns, String selection, String[] slectionArgs, String groupBy, String having, String orderBy, String limit)方法各参数的含义如下。

table: 表名,相当于select语句from关键字后面的部分。

columns: 要查询的列名,可以是多列,相当于select语句select关键字后面的部分。

selection: 查询条件子句,相当于select语句where关键字后面的部分,在条件子句中允许使用占位符“?”。

selectionArgs: 对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常。

groupBy: 相当于select语句group by关键字后面的部分。

having: 相当于select语句having关键字后面的部分。

orderBy: 相当于select语句order by关键字后面的部分。

limit: 指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分。

Cursor接口主要用于存放查询记录的接口,Cursor是结果集游标,用于对结果集进行访问。该接口提供了如下方法来移动查询结果的记录指针。

move(int offset): 将记录指针向上或向下移动指定的行数。offset 为正数就向下移动,为负数就向上移动。

moveToNext(): 可以将游标从当前记录移动到下一记录,如果已经移过了结果集的最后一条记录,返回结果为false, 否则为true。

moveToPrevious(): 用于将游标从当前记录移动到上一记录,如果已经移过了结果集的第一条记录,返回值为false,否则为true。

moveToFirst(): 用于将游标移动到结果集的第一条记录,如果结果集为空,返回值为false, 否则为true。

moveToLast(): 用于将游标移动到结果集的最后一条记录,如果结果集为空,返回值为false,否则为true。

使用SQLiteDatabase进行数据库操作的步骤如下。

(1) 定义一个数据库操作辅助类,从SQLiteOpenHelper继承,重写onCreate()和 onUpdate()方法,在onCreate()方法中执行建表语句和初始化数据。

(2) 创建SQLiteOpenHelper类对象,指定数据库的名称和版本后,调用该类的 getReadableDatabase()或者getWritableDatabase()方法,获取SQLiteDatabase对象,该对象代表了与数据库的连接。

(3) 调用SQLiteDatabase对象的相关方法来执行增、删、查、改操作。

(4) 对数据库操作的结果进行处理,例如,判断是否插入、删除或者更新成功,将查询结果记录转换成列表显示等。

(5) 关闭数据库连接,回收资源。

1. 添加数据

接下来以userInfo表为例,介绍如何使用SQLiteDatabase对象的insert()方法向表中插入一条数据,实例代码如下。

程序清单7-12: chart0705\app\src\main\java\com\example\administratorchart0705MainActivityjava

1  public void insert(String name, String pwd, String nick) {

2      SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
3      SQLiteDatabase database = sqLiteHelper.getWritableDatabase();
4      ContentValues values = new ContentValues();
5      values.put("user_name", name);
6      values.put("user_pwd", pwd);
7      values.put("user_nick", nick);
8      long id = database.insert("userinfo", null, values);
9      database.close();
10  }


在上述代码中,首先通过sqLiteHelper的getWritableDatabase()获取SQLiteDatabase 对象,之后获取ContentValues对象并将数据添加到ContentValues对象中。最后调用insert()方法将数据添加到userInfo表中,返回值代表新行的ID。其中,insert()方法接收3个参数,第1个参数是表名,第2个参数表示如果发现将要插入的行为空行时,会将这个列名的值设为null,第3个参数为ContentValues对象。其中,ContentValues类类似Map类,通过键-值对的形式存入数据,这里的key表示插入数据的列名,value表示要插入的数据。

需要注意的是,使用完SQLiteDatabase对象后一定要调用close()方法关闭数据库连接,否则数据库连接会一直存在,不断消耗内存,当系统内存不足时将获取不到SQLiteDatabase对象,并且会报出数据库未关闭异常。

2. 修改数据

SQLitDatabase类中存在一个update()方法,用于修改数据库表中的数据,以userInfo表 为例,如果想要修改该表中的某一条数据时,可直接调用SQLiteDatabase对象的update()方法,示例代码如下。

程序清单7-13: chart0705\app\src\main\java\com\example\administrator\chart0705MainActivityjava

1  public int update(String name, String pwd, String nick) {

2      SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
3      SQLiteDatabase database = sqLiteHelper.getWritableDatabase();
4      ContentValues values = new ContentValues();
5      values.put("user_pwd", pwd);
6      values.put("user_nick", nick);
7      int num = database.update("userinfo", values, "user_name=?", new String[]{name});
8      database.close();
9      return num;
10  }


上述代码中,首先通过sqLiteHelper的getWritableDatabase()获取SQLiteDatabase 对象。之后调用该对象的update()方法,其中,update()方法接收3个参数,第1个参数是表名,第2个参数表示查询条件的子句,第3个参数为查询条件子句中对应的值。该方法的返回值表示受影响的行数。

3. 删除数据

SQLiteDatabase类中提供了一个delete()方法,用于删除数据库表中的数据。以userInfo表为例说明,示例代码如下。

程序清单7-14: chart0705\app\src\main\java\com\example\administratorchart0705MainActivityjava

1  public int delete(String name) {

2      SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
3      SQLiteDatabase database = sqLiteHelper.getWritableDatabase();
4      int num = database.delete("userinfo", "user_name=?", new String[]{name});
5      database.close();
6      return num;
7  }


上述代码中,首先通过sqLiteHelper的getWritableDatabase()获取SQLiteDatabase 对象。之后调用该对象的delete()方法,其中,delete()方法接收3个参数,第1个参数是表名,第2个参数表示查询条件的子句,第3个参数为查询条件子句中对应的值。该方法的返回值表示受影响的行数。

4. 查询数据

在进行数据查询时使用的是query()方法,该方法返回的是一个行数集合Cursor,Cursor是一个游标接口,提供了遍历查询结果的方法。需要注意的是,在使用完Cursor对象后,一定要及时关闭,否则会造成内存泄漏。以userInfo表为例说明,示例代码如下。

程序清单7-15: chart0705\app\src\main\java\com\example\administratorchart0705MainActivityjava

1  public List<UserInfo> query(String name) {

2      List<UserInfo> infoList = new ArrayList<UserInfo>();
3      SQLiteHelper sqLiteHelper = new SQLiteHelper(this);
4      SQLiteDatabase database = sqLiteHelper.getWritableDatabase();
5      Cursor cursor = database.query("userinfo", null, "user_name=?", new String[]{name}, null,

6              null, null, null);

7      if (cursor != null && cursor.getCount() > 0) {

8          while (cursor.moveToNext()) {

9              UserInfo info = new UserInfo();
10              info.setUser_name(cursor.getString(1));
11              info.setUser_pwd(cursor.getString(2));
12              info.setUser_nick(cursor.getString(3));
13              infoList.add(info);
14          }
15      }
16      cursor.close();
17      database.close();
18      return infoList;
19  }


上述代码中,封装一个查询表userInfo的方法,其中返回为一个集合,集合中每个对象的类型为UserInfo。UserInfo是根据userInfo表封装的实体类。

其中,第1~6行代码首先通过sqLiteHelper的getWritableDatabase()获取SQLiteDatabase 对象,再通过SQLiteDatabase对象的query()方法查询userInfo表中的数据,并返回Cursor对象。query()方法包含7个参数,第1个参数表示表名称,第2个参数指明查询哪几列,如果不指明,用null表示则表示查询所有列,第3个参数表示的是接收查询条件的子句,第4个参数接收查询子句对应的条件值,如果第3,4个参数不指明,用null表示则表示查询所有行,第5个参数表示分组方式,第6个参数接收having条件,即对group By之后的数据再添加过滤器,第7个参数表示排序方式,如果不指明则按照默认的排序方式。

第7~13行代码首先通过getCount()方法获取到查询结果的总数,然后循环取出每一列的值,通过moveToNext()方法不断移动游标到下一行数据,接着通过为getString()方法传入列索引获取对应的数据,并将数据存入到UserInfo对象中,最后把UserInfo对象存入到集合中,最后关闭Cursor和SQLiteDatabase对象,并将集合数据返回。


5

使用SQL操作数据库


虽然Android已经提供了非常方便的API用于操作数据库,但是对于多表连接查询的时候使用Android封装的API不是很方便,因此在操作数据库的时候还可以使用execSQL()方法通过SQL语句对数据库进行操作,示例代码如下。

1. 添加数据

database.execSQL("insert into userinfo (user_name,user_pwd,user_nick) values(?,?,?)", new
                String[]{"
小明", "123", "Jack"});


2. 修改数据

database.execSQL("update userinfo set user_nick=? where user_name=?", new
                String[]{"
James", "小明"});


3. 删除数据

database.execSQL("delete from userinfo where user_name=?", new String[]{"小明"});


4. 查询数据

Cursor cursor = database.rawQuery("select * from userinfo where user_name=?", new 
                String[]{"
小明"});


从上述代码中可知,增、删、改都是调用execSQL()方法执行SQL语句,而查询使用rawQuery()方法,因为execSQL()方法没有返回值,而查询需要返回一个结果集Cursor。采用上述方式的执行结果和使用Android提供的API执行结果相同,可以根据实际需求选择使用合适的方式



实例讲解

Android零基础入门到实战

精彩回顾

鸿蒙开发入门

开发第一个鸿蒙应用+页面跳转

Android中的数据存取

实战演练—文件存储实现自动登录
SharedPreferences保存用户名和密码




6

参考书籍


《Android零基础入门到实战(App项目开发·鸿蒙开发入门·微课视频版)》

ISBN:9787302600671

作者:赵圣鲁、胡颖辉、余燕萍、汪宗伟、吴微微

定价:59.8元

扫码微店优惠购书


内容简介

本书围绕Android初学者从零基础到实战达人的过程进行设计,采用项目教学法,以作者开发的“欢乐购商城”App为例,以一个完整的项目开发为主线,将项目开发分解为9个教学模块,分别为App应用体验、Android基础界面控件、Android高级界面控件、列表控件、页面跳转与切换、Android中的数据存取、Android客户端与服务器端交互、综合项目“欢乐购商城”实现等。读者在学习基础知识过程中将熟悉App综合项目开发流程,逐步培养独立开发综合项目的能力,并最终实现综合项目。同时本书引入鸿蒙开发入门知识,供有兴趣的读者参考学习。本书可作为应用型本科计算机专业、软件专业、高职软件技术专业及相关专业的教材,也可作为Android和鸿蒙开发爱好者以及初、中级Android应用开发人员的参考工具书。


编辑推荐

为了方便读者学习,本书配套了微课教学视频、源码、课件、试题、课程大纲等教学资源。所有配套资源均可在清华大学出版社官方网站下载。其中安卓项目案例源码,基于Java JDK1.8+Android Studio Arctic Fox+Android SDK 11.0+gradle:7.0.0-beta04开发;鸿蒙项目案例源码,基于Open JDK1.8+DevEco Studio 2.1 release+sdk(api version 5)+gradle6.3开发。


扫码京东优惠购书


7

精彩推荐


文章转载自清华计算机学堂,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论