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

来源:互联网 发布:淘宝小号用什么注册 编辑:程序博客网 时间:2024/05/22 01:34

续上一博文(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.dbtables  查看所有的表  eg: tableschema  查看查看库中所有表的DDL语句 eg: schema help    查看帮助  eg: helpheaders on/off  显示表头 默认off eg: headers onmode list|column|insert|line|tabs|tcl|csv 改变输出格式 eg: mode columnnullValue NULL空值数据显示问题 eg: nullValue NULLdump    <表名> 生成形成表的SQL脚本 eg: dump persondump    生成整个数据库的SQL脚本 eg: dumpexit    退出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