Android核心基础-5.Android 数据存储与访问-3. 使用Sqlite进行数据存储

续上一博文(Android核心基础-5.Android 数据存储与访问-2.使用SharedPreferences进行数据存储)

三、使用Sqlite进行数据存储

3.1Sqlite数据库简介

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

  • SQLite的数据类型:Typelessness(无类型), 可以保存任何类型的数据到你所想要保存的任何表的任何列中. 但它又支持常见的类型比如: NULL, VARCHAR, TEXT, INTEGER, BLOB, CLOB…等. 唯一的例外:integer primary key 此字段只能存储64位整数

3.2Android对Sqlite的支持

  • 在Android系统,提供了一个SQLiteOpenHelper抽象类,该类用于对数据库版本进行管理.该类中常用的方法:
    • onCreate 数据库创建时执行(第一次连接获取数据库对象时执行)
    • onUpgrade 数据库更新时执行(版本号改变时执行)
    • onOpen 数据库每次打开时执行(每次打开数据库时调用,在 onCreate,onUpgrade方法之后)

MyHelper.class

package net.dxs.sqlite.dao;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyHelper extends SQLiteOpenHelper {// 自定义类继承SQLiteOpenHelper

    public MyHelper(Context context) {// 由于父类没有无参构造函数, 定义一个构造函数调用父类有参的构造函数
        /*
         * 参数1: Context代表应用程序的环境, 用来确定数据库文件的位置
         * 参数2: 数据文件的名字
         * 参数3: 用来创建Cursor(结果集)的工厂, 默认传null就可以
         * 参数4: 数据库的版本, 后期用来更新数据库, 从1开始
         */
        super(context, "dxs.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {// 在数据库文件创建之后执行
        System.out.println("onCreate");
        db.execSQL("CREATE TABLE account(_id INTEGER PRIMARY KEY AUTOINCREMENT,name VARCHAR(20))");// 执行SQL语句, 创建表
        db.execSQL("ALTER TABLE account ADD balance INTEGER");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// 在数据库版本提升之后执行
        System.out.println("onUpgrade");
        db.execSQL("ALTER TABLE account ADD balance INTEGER");
    }
}

3.3Android操作Sqlite的API

  Android提供了一个名为SQLiteDatabase的类,该类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。对SQLiteDatabase的学习,我们应该重点掌握execSQL()和rawQuery()方法。 execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句; rawQuery()方法用于执行select语句。

3.3.1execSQL()方法使用

execSQL()方法的使用例子:
SQLiteDatabase db = ....;
db.execSQL("insert into person(name, age) values('深情小建', 4)");
db.close();

  执行上面SQL语句会往person表中添加进一条记录,在实际应用中, 语句中的“深情小建”这些参数值会由用户输入界面提供,如果把用户输入的内容原样组拼到上面的insert语句, 当用户输入的内容含有单引号时,组拼出来的SQL语句就会存在语法错误。要解决这个问题需要对单引号进行转义,也就是把单引号转换成两个单引号。有些时候用户往往还会输入像“ & ”这些特殊SQL符号,为保证组拼好的SQL语句语法正确,必须对SQL语句中的这些特殊SQL符号都进行转义,显然,对每条SQL语句都做这样的处理工作是比较烦琐的。 SQLiteDatabase类提供了一个重载后的execSQL(String sql, Object[] bindArgs)方法,使用这个方法可以解决前面提到的问题,因为这个方法支持使用占位符参数(?)。

使用例子如下:

SQLiteDatabase db = ....;
db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"深情小建", 4}); 
db.close();

  execSQL(String sql, Object[] bindArgs)方法的第一个参数为SQL语句,第二个参数为SQL语句中占位符参数的值,参数值在数组中的顺序要和占位符的位置对应。

3.3.2rawQuery()方法使用

SQLiteDatabase的rawQuery() 用于执行select语句,

使用例子如下:

SQLiteDatabase db = ....;
Cursor cursor = db.rawQuery(“select * from person”, null);
while (cursor.moveToNext()) {
    int personid = cursor.getInt(0); //获取第一列的值,第一列的索引从0开始
    String name = cursor.getString(1);//获取第二列的值
    int age = cursor.getInt(2);//获取第三列的值
}
cursor.close();
db.close(); 

  rawQuery()方法的第一个参数为select语句;第二个参数为select语句中占位符参数的值,如果select语句没有使用占位符,该参数可以设置为null。
  
带占位符参数的select语句使用例子如下:

Cursor cursor = db.rawQuery("select * from person where name like ? and age=?", new String[]{"%小建%", "4"});
  1. Cursor是结果集游标,用于对结果集进行随机访问,如果大家熟悉jdbc, 其实Cursor与JDBC中的ResultSet作用很相似。

  2. 使用moveToNext()方法可以将游标从当前行移动到下一行,如果已经移过了结果集的最后一行,返回结果为false,否则为true。

  3. 另外Cursor 还有常用的moveToPrevious()方法(用于将游标从当前行移动到上一行,如果已经移过了结果集的第一行,返回值为false,否则为true )。

  4. moveToFirst()方法(用于将游标移动到结果集的第一行,如果结果集为空,返回值为false,否则为true )和moveToLast()方法(用于将游标移动到结果集的最后一行,如果结果集为空,返回值为false,否则为true ) 。

3.3.3其他封装好的方法

  除了execSQL()和rawQuery()方法, SQLiteDatabase还专门提供了对应于添加、删除、更新、查询的操作方法:

insert()    增加数据
delete()    删除数据
update()    修改数据
query()     查询数据

  这些方法实际上是给那些不太了解SQL语法的开发者使用的,对于熟悉SQL语法的程序员而言,直接使用execSQL()和rawQuery()方法执行SQL语句就能完成数据的添加、删除、更新、查询操作。

3.3.4事务

  使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。

使用例子如下:

SQLiteDatabase db = ....;
db.beginTransaction();//开始事务
try {
    db.execSQL("insert into person(name, age) values(?,?)", new Object[]{"深情小建", 4});
    db.execSQL("update person set name=? where personid=?", new Object[]{"小建", 1});
    db.setTransactionSuccessful();//调用此方法会在执行到endTransaction() 时提交当前事务,如果不调用此方法会回滚事务
} finally {
    db.endTransaction();//由事务的标志决定是提交事务,还是回滚事务
} 
db.close(); 

上面两条SQL语句在同一个事务中执行。

AccountDao.class

package net.dxs.sqlite.dao;

import java.util.ArrayList;
import java.util.List;

import net.dxs.sqlite.bean.Account;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class AccountDao {
    private MyHelper helper;

    public AccountDao(Context context) {
        helper = new MyHelper(context);
    }

    public void insert(Account a) {
        // 获取SQLiteDatabase对象
        SQLiteDatabase db = helper.getWritableDatabase();

        // 执行一条SQL语句
        db.execSQL("insert into account(name,balance) values(?,?)", new Object[] { a.getName(), a.getBalance() });

        // 关闭
        db.close();
    }

    public void delete(int id) {
        //  获取SQLiteDatabase对象
        SQLiteDatabase db = helper.getWritableDatabase();

        //执行删除语句
        db.execSQL("delete from account where _id=?", new Object[] { id });

        //  关闭
        db.close();
    }

    public void update(Account a) {
        //  获取SQLiteDatabase对象
        SQLiteDatabase db = helper.getWritableDatabase();

        //执行删除语句
        db.execSQL("update account set name=?,balance=? where _id=?", new Object[] { a.getName(), a.getBalance(), a.getId() });

        //  关闭
        db.close();
    }

    public Account query(int id) {
        // 先调用getWriteableDatabse(), 如果出了异常, 就开一个只读的
        SQLiteDatabase db = helper.getReadableDatabase();

        // 执行查询语句, 得到结果集
        Cursor c = db.rawQuery("select name,balance from account where _id=?", new String[] { String.valueOf(id) });

        Account a = null;
        if (c.moveToNext()) {// 判断结果集是否包含下一条数据, 如果包含, 指针自动向后移动
            String name = c.getString(0);// 从结果集中获取数据(根据列的索引获取)
            int balance = c.getInt(c.getColumnIndex("balance"));// 从结果集中获取数据(先根据列名获取索引, 再根据索引获取数据)
            a = new Account(id, name, balance);// 创建对象, 把数据设置到对象中
            return a;
        }

        // 关闭
        c.close();
        db.close();
        return null;// 返回对象
    }

    public List<Account> queryAll() {
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor c = db.rawQuery("select * from account", null);// 查询表中所有数据

        List<Account> list = new ArrayList<Account>();
        while (c.moveToNext()) {
            int id = c.getInt(0);// 获取表中的第一列(索引从0开始)
            String name = c.getString(1);
            int balance = c.getInt(2);

            list.add(new Account(id, name, balance));// 把表中的数据封装成对象
        }

        c.close();
        db.close();
        return list;
    }

    public Cursor queryCursor() {
        SQLiteDatabase db = helper.getReadableDatabase();
        return db.rawQuery("select * from account order desc balance", null);
    }

    /**
     * 
     * @param pageSize 每页显示多少条
     * @param pageNum 要第几页的数据
     * @return
     */
    public List<Account> queryPage(int pageSize, int pageNum) {
        String index = (pageNum - 1) * pageSize + "";       // 翻页时的起始索引
        String count = pageSize + "";                       // 查询多少条数据
        List<Account> list = new ArrayList<Account>();
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor c = db.rawQuery("select * from account limit ?,?", new String[]{index,count});
//      Cursor c = db.query("account", null, null, null, null, null, null, index + "," + count);
        while (c.moveToNext()) {
            int id = c.getInt(0);           
            String name = c.getString(1);   
            int balance = c.getInt(2);      
            list.add(new Account(id, name, balance));   
        }
        c.close();
        db.close();
        return list;
    }

    public int queryCount() {
        SQLiteDatabase db = helper.getReadableDatabase();
        Cursor c = db.rawQuery("select count(*) from account", null);

        c.moveToNext();
        int count = c.getInt(0);
        c.close();
        db.close();
        return count;
    }

    public void remit(int fromId, int toId, int amount) {
        SQLiteDatabase db = helper.getReadableDatabase();
        try {
            db.beginTransaction(); //开启事务
            db.execSQL("update account set balance=balance-? where _id=?", new Object[] { amount, fromId });
            db.execSQL("update account set balance=balance+? where _id=?", new Object[] { amount, toId });
            db.setTransactionSuccessful();// 设置事务成功标记

        } finally {
            db.endTransaction();//结束事务
            db.close();
        }
    }
}

3.4Android sqlite3工具的使用

这里提供下我百度网盘的下载链接(SQLiteExpert

sqlite3 <数据库名称> 进入数据库操作模式 eg: sqlite3 contacts.db
tables  查看所有的表  eg: table
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脚本 eg: dump person
dump    生成整个数据库的SQL脚本 eg: dump
exit    退出sqlite操作模式 eg: exit

3.5写在最后

一.SQLite数据库**
    1.SQLite数据库的特点
        安卓手机自带, 小巧, 适合在手机中使用
        不区分数据类型(主键除外)
        SQL语句和MySQL几乎相同
        SQLite不使用JDBC连接, 使用的是Android自有的API
        每个数据库对应一个文件 

  * 2.创建数据库
        定义类继承SQLiteOpenHelper, 实现onCreate(), onUpgrade()
        创建该类对象, 调用getWritableDatabse()或者getReadableDatabse()
        情况1: 数据库文件不存在, 创建文件, 打开数据库连接(得到SQLiteDatabase对象), 执行onCreate()方法 
        情况2: 数据库文件存在, 版本号没变, 打开数据库连接 
        情况3: 数据库文件存在, 版本号提升, 升级数据库, 打开数据库连接,执行onUpgrade()方法 
        情况4: 数据库文件存在, 版本号降低, 执行onDowngrade()方法, 方法中默认会抛出一个异常

  * 3.创建表或修改表
        SQLiteDatabase类的execSQL()方法可以执行一条SQL语句
        如果希望创建数据库的时候就创建一些表, 那么这个操作就可以在onCreate()方法中执行
        如果希望在数据库升级的时候做类似修改表添加表的操作, 可以在onUpgrade()方法中执行

*** 4.增删改查
        execSQL()方法可以进行增删改操作
        rawQuery()执行查询操作, 得到Cursor, 调用moveToNext()判断是否包含数据, 调用getString(), getInt()等方法获取数据
        insert(), delete(), update(), query() 四个方法内部也是调用execSQL()和rawQuery()的, 它们在ContentProvider中使用更方便(另一篇博文讲)

  * 5.事务管理
        beginTransaction() 开启事务
        setTransactionSuccessful() 设置事务成功标记
        endTransaction() 结束事务. 
        事务结束的时候, 会把最后一个成功标记之前的操作提交, 成功标记之后的操作回滚

Account.class

package net.dxs.sqlite.bean;

public class Account {
    private Integer id;
    private String name;
    private Integer balance;

    public Account() {

    }

    public Account(String name, Integer balance) {
        super();
        this.name = name;
        this.balance = balance;
    }

    public Account(Integer id, String name, Integer balance) {
        super();
        this.id = id;
        this.name = name;
        this.balance = balance;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getBalance() {
        return balance;
    }

    public void setBalance(Integer balance) {
        this.balance = balance;
    }

    @Override
    public String toString() {
        return "Account [id=" + id + ", name=" + name + ", balance=" + balance + "]";
    }
}

SQLiteTest.class

package net.dxs.sqlite.test;

import java.util.List;
import java.util.Random;

import net.dxs.sqlite.bean.Account;
import net.dxs.sqlite.dao.AccountDao;
import net.dxs.sqlite.dao.MyHelper;
import android.test.AndroidTestCase;

public class SQLiteTest extends AndroidTestCase {
    // 测试类中的Context对象是在测试类创建之后(构造函数执行之后), 虚拟机自动调用setContext()传入的, 如果在成员变量位置就getContext()则拿不到
    private AccountDao dao;

    @Override
    protected void setUp() throws Exception {// 测试方法执行之前执行
        dao = new AccountDao(getContext());
    }

    @Override
    protected void tearDown() throws Exception {// 测试方法执行之后执行

    }

    public void testCreateDB(){
        new MyHelper(getContext()).getWritableDatabase();// 获取数据库对象
        /*
         * 情况1: 数据库文件不存在, 创建文件, 打开数据库连接(得到SQLiteDatabase对象), 执行onCreate()方法 
         * 情况2: 数据库文件存在, 版本号没变, 打开数据库连接 
         * 情况3: 数据库文件存在, 版本号提升, 升级数据库, 打开数据库连接,执行onUpgrade()方法 
         * 情况4: 数据库文件存在, 版本号降低, 执行onDowngrade()方法, 方法中默认会抛出一个异常
         */
    }

    public void testInsert(){
        for (int i = 0; i < 100; i++) {
            dao.insert(new Account("深情小建"+i, new Random().nextInt(10000)));
        }
    }

    public void testDelete(){
        dao.delete(20);
    }

    public void testUpdate(){
        dao.update(new Account(1,"深情小建", 12000));
        dao.update(new Account(2,"刘德华", 12000));
        dao.update(new Account(3,"武状元", 12000));
    }

    public void testQuery(){
        System.out.println(dao.query(1));
        System.out.println(dao.query(3));
        System.out.println(dao.query(4));
        System.out.println(dao.query(5));
    }

    public void testQueryAll(){
        List<Account> list = dao.queryAll();
        for (Account account : list) {
            System.out.println(account);
        }
    }

    public void testQueryPage(){
        List<Account> list = dao.queryPage(20, 1);

        for (Account account : list) {
            System.out.println(account);
        }

    }

    public void testQueryCount(){
        System.out.println(dao.queryCount());
    }

    public void testRemit(){
        dao.remit(2, 1, 200);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值