zoukankan      html  css  js  c++  java
  • android菜鸟学习笔记20----Android数据存储(四))Android数据库操作

    Android内置了一个名为SQLite的关系型数据库,这是一款轻量型的数据库,操作十分简便。SQLite与别的数据库不同的是,它没有数据类型。可以保存任何类型的数据到你所想要保存的任何表的任何列中。但它又支持常见的类型比如: NULL, VARCHAR, TEXT, INTEGER, BLOB, CLOB...等。

    唯一的例外是:integer primary key 此字段只能存储64位整数。

    在JAVA项目中,要使用JDBC操作数据库需要加载数据库驱动,连接数据库等操作。Android简化了我们的数据库操作,无需由我们进行数据库驱动加载、连接等操作。

    Android中进行数据库操作,需要涉及到如下几个类:

    1)SQLiteOpenHelper:

    在android.database.sqlite包下,这是一个抽象的帮助类,用于管理数据库的创建及版本维护操作。

    我们在需要获取该类的对象时,需要自定义类继承自SQLiteOpenHelper类,并实现其中的onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int),可以选择性地实现 onOpen(SQLiteDatabase)。这个类会自动帮助我们在需要时打开数据库,在不存在时创建数据库,在必要时更新数据库。

    常用方法:

     

    一般,在自定义的子类中调用父类中第一个构造方法即可。

    构造函数的参数说明:

    context:应用的上下文对象

    name:要操作的数据库的名称

    factory:cursor工厂类对象,一般指定为null

    version:数据库的版本号,必须大于等于1,由于控制数据库的升级。

     

    注意到,只有onCreate()和onUpgrade()是抽象方法,所以自定义子类继承SQLiteOpenHelper时,一定要实现这两个方法。

    其中:

    onCreate()方法会在数据库不存在,第一次创建时调用,所以数据库中的初始化操作,如创建表等操作需要在该方法中完成。

    onUpgrade()方法,新的版本号比原来有提升时,调用,用以完成数据库的升级操作,如新版本的app中,需要添加一张表,或者修改某个表,就需要在新版本的app创建SQLiteOpenHelper对象时,向其构造函数传入一个更大的版本号,这个版本号会被newVersion接收。

     

    getReadaleDatabase()方法,创建或打开一个数据库,返回代表该数据库的只读的SQLiteDatabase对象

    getWritableDatabase()方法,创建或打开一个数据库,返回代表该数据库的可读可写的SQLiteDatabase对象。

     

    close()方法,用于关闭打开的数据库对象。

    2)SQLiteDatabase:

    通过SQLiteOpenHelper对象获取SQLiteDatabase对象后,便可以调用SQLiteDatabase类的相关方法进行数据库的增删改查操作了。

    该类的常用方法有:

     

    execSQL()方法用于执行SQL语句,可以用于执行不需要返回值的一些数据库操作。

     

    rawQuery()方法一般被用于执行需要返回值的查询操作,查询的结果保存在Cursor对象中。

    除了直接执行SQL语句进行数据库操作的方法之外,该类还封装几个更易用的增删改查方法。

     

    insert()方法,用于向数据库中插入数据,参数说明:

    table:指定要插入数据的表明

    nullColumnHack:一般指定为null即可

    注意:当values也为null,表示想向数据库中插入一条空记录时,该方法实际执行的SQL语句为insert into table(null) values(null);这样一条sql语句是没法执行的,所以,需要将nullColumnHack指定为任意一个可以为空的字段的字段名。如:nullColumnHack指定为”name”,values为null,此时SQL语句为insert into table(name) values(null);则可以正常执行了。

    values:是一个ContentValues对象,用于存放要插入的各个字段名与值的对应关系。

     

    delete()方法,用于删除表中的记录,参数说明:

    table指定要操作的表名

    whereClause指定where字句,可以包含占位符”?”,如“name = ?”,实际执行时,占位符会被第三个参数中对应索引的值替换

    whereArgs:指定where字句中,占位符对应的值,如new String[]{“zhangsan”}

    实际执行的语句就是delete from table where name = “zhangsan”

     

    update()方法,用于执行表中记录的更新操作,参数说明:

    table,whereClause,whereArgs与delete相同含义

    values用于指定对应字段名要更新的值的映射关系。

     

    query()记录查询方法,最简单的一个重载形式也有七个参数,都是select中的各个字句部分,如where子句部分,group by子句部分,having,order by等。参数说明:

    table指定要查询的表名

    colmns指定要查询的字段

    selection:可以带占位符的where子句部分

    slectionArgs:where子句占位符对应的值

    groupBy:group by子句部分

    having:having子句部分

    orderBy:order by 子句部分

    等等

     

    isOpen()判断数据库是否已打开。

     

    beginTransaction()用于开启事务操作

     

    setTransactionSuccessful()方法用于标记事务操作成功

     

    endTransaction()方法用于关闭事务,若事务标记成功,则提交事务操作,否则,则回滚失败的事务操作。

    其他方法,在需要时,可以查询API帮助手册。

    3)Cursor:

    Cursor是一个接口,其实现类用于存放SQL语句查询的结果集。SQLiteDatabase的rawQuery()及query()方法均会返回该接口的对象,用以存储操作查询返回的结果集。

    Cursor对象维持一个游标,默认指向结果集第一条记录之前的位置,可以通过下面几个方法,来移动游标,从而取得需要的记录。当游标已在第一条记录之前,调用moveToPreious(),或者已指向最后一条记录,调用moveToNext()方法时,均会返回false()标识移动失败。

     

     

    getShort()、getString()、getInt()等getXXX()方法,用于根据当前记录的字段的索引获取字段的值。

     

    getColumnName()用于根据索引获取字段名

    getColumnNames()用于获取所有的字段名,其顺序与在Cursor中保存的顺序相同。

    getCount()用于获取当前Cursor对象中保存的记录数。

     

    getColumnCount()方法用于获取记录的字段总数

    getColumnIndex()用于根据字段名获取其索引,不存在时返回-1。

     

    close()方法,用于关闭当前Cursor对象。

    4)ContentValues:

    该类用于存放键值对的数据,在数据库的插入更新等操作中,可以使用字段名作为键,使用要插入或更新的字段值作为值。

    常用的是put()方法,向ContentValues对象中存储数据。

    如:put(“name”,”zhangsan”);等

    以上便是数据库操作所涉及的几个主要的类和接口。

    下面通过一个具体的代码,来学习使用这些个API:

    第一步:

    自定义类继承SQLiteOpenHelper类,实现onCreate()和onUpgrade()方法:

     1 public class MySqliteHelper extends SQLiteOpenHelper {
     2 
     3       public static final String TAG = "MYSQLITEHELPER";
     4 
     5       public static final String CREATE_STUDENT = "create table t_student (" +
     6 
     7                  "id integer primary key, name varchar(20), gender varchar(10), age integer)";
     8 
     9       public MySqliteHelper(Context context, String name, CursorFactory factory,
    10 
    11                  int version) {
    12 
    13            super(context, name, factory, version);
    14 
    15       }
    16 
    17       @Override
    18 
    19       public void onOpen(SQLiteDatabase db) {
    20 
    21            Log.i(TAG,"open db");
    22 
    23            super.onOpen(db);
    24 
    25       }
    26 
    27       @Override
    28 
    29       public void onCreate(SQLiteDatabase db) {
    30 
    31            Log.i(TAG,"create db");
    32 
    33            Log.i(TAG,"before excSql");
    34 
    35            db.execSQL(CREATE_STUDENT);
    36 
    37            Log.i(TAG,"after excSql");
    38 
    39       }
    40 
    41       @Override
    42 
    43       public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    44 
    45 
    46       }
    47 
    48 }

    第二步,定义实体类:

     1 package cn.csc.sqlite.bean;
     2 
     3  
     4 
     5 public class Student {
     6 
     7       private int id;
     8 
     9       private String name;
    10 
    11       private String gender;
    12 
    13       private int age;
    14 
    15  
    16 
    17       public int getId() {
    18 
    19            return id;
    20 
    21       }
    22 
    23       public void setId(int id) {
    24 
    25            this.id = id;
    26 
    27       }
    28 
    29       public String getName() {
    30 
    31            return name;
    32 
    33       }
    34 
    35       public void setName(String name) {
    36 
    37            this.name = name;
    38 
    39       }
    40 
    41       public String getGender() {
    42 
    43            return gender;
    44 
    45       }
    46 
    47       public void setGender(String gender) {
    48 
    49            this.gender = gender;
    50 
    51       }
    52 
    53       public int getAge() {
    54 
    55            return age;
    56 
    57       }
    58 
    59       public void setAge(int age) {
    60 
    61            this.age = age;
    62 
    63       }
    64 
    65      
    66 
    67       public Student() {
    68 
    69            super();
    70 
    71       }
    72 
    73       public Student(int id, String name, String gender, int age) {
    74 
    75            super();
    76 
    77            this.id = id;
    78 
    79            this.name = name;
    80 
    81            this.gender = gender;
    82 
    83            this.age = age;
    84 
    85       }
    86 
    87       @Override
    88 
    89       public String toString() {
    90 
    91            return "Student [id=" + id + ", name=" + name + ", gender=" + gender
    92 
    93                       + ", age=" + age + "]";
    94 
    95       }
    96 
    97 }

    第三步,定义数据库操作类:

     1 public class StudentDao {
     2 
     3       private SQLiteOpenHelper helper;
     4 
     5       private Context context;
     6 
     7       public StudentDao(Context context){
     8 
     9            this.context = context;
    10 
    11       }
    12 
    13       public void insert(Student stu){
    14 
    15            helper = new MySqliteHelper(context,"students.db", null, 1);
    16 
    17            Log.i("MYSQLITEHELPER","before get db");
    18 
    19            SQLiteDatabase db = helper.getWritableDatabase();
    20 
    21            Log.i("MYSQLITEHELPER","after get db");
    22 
    23            db.execSQL("insert into t_student(name, gender, age) values(?,?,?)" , new Object[]{stu.getName(),stu.getGender(),stu.getAge()});
    24 
    25            db.close();
    26 
    27       }
    28 
    29 }

    第四步,定义测试类,测试StudentDao的insert()方法:

     1 public class TestDao extends AndroidTestCase {
     2 
     3       public void testInsert(){
     4 
     5            StudentDao dao = new StudentDao(getContext());
     6 
     7            dao.insert(new Student(0,"zhangsan", "male", 23));
     8 
     9       }
    10 
    11 }

    运行该测试方法,运行结果如下:

     

    注意到,定义SQLiteOpenHelper对象,并不会创建数据库,只有调用getWritableDatabase()或者getReadableDatabase()才会调用onCreate()方法,onCreate()内部执行了execSQL()方法,但是并没有调用onOpen()方法。onCreate()方法执行完成后,才接着调用了onOpen()方法。

     

    从File Explorer中可以看出,students.db存放在/data/data/应用包名/databases/下。

    再次运行,由于数据库已经存在,onCreate()方法就没再被调用。

    可以通过sqlite3命令来查看数据库内容:

     

    sqlite的简单使用说明:

    adb shell挂载虚拟机控制台

    cd /data/data/cn.csc.sqlite/databases进入数据库文件所在目录

    sqlite3 数据库文件名: 管理数据库文件名所指定的数据库,如sqlite3  students.db,然后光标变为sqlite>,表示进入sqlite操作模式。

    .tables 查看数据库中所有的表

    可以直接输入sql语句并执行

    .exit 退出sqlite操作模式

    .schema 查看查看库中所有表的DDL语句

    .mode list|column|insert|line|tabs|tcl|csv 改变输出格式

     

    若觉得命令行用着不习惯,可以从File Explorer中把数据库文件导出到电脑中,然后使用图形化工具,如sqlite expert来查看管理数据库。

    下载地址:http://pan.baidu.com/s/1jG8G7QY

    StudentDao中添加getAllStudents()方法:

     1 public List<Student> getAllStudents(){
     2 
     3            List<Student> list = new ArrayList<Student>();
     4 
     5            helper = new MySqliteHelper(context,"students.db", null, 1);
     6 
     7            SQLiteDatabase db = helper.getWritableDatabase();
     8 
     9            Cursor cursor = db.rawQuery("select id,name,gender,age from t_student", null);
    10 
    11            if(cursor == null){
    12 
    13                  return null;
    14 
    15            }
    16 
    17            while(cursor.moveToNext()){
    18 
    19                  Student stu = new Student(cursor.getInt(0),cursor.getString(1),cursor.getString(2),cursor.getInt(3));
    20 
    21                  Log.i("MYSQLITEHELPER",stu.toString());
    22 
    23                  list.add(stu);
    24 
    25            }
    26 
    27           
    28 
    29            return list;
    30 
    31 }

    测试,输出:

     

    上面用的都是直接写完整的SQL语句,下面添加几个方法,调用SQLiteDatabase封装的几个简单操作的API:

    update()方法的使用

    StudentDao类添加:修改指定id的学生的姓名:

     1 public void updateNameById(int id, String newName){
     2 
     3            helper = new MySqliteHelper(context,"students.db", null, 1);
     4 
     5            SQLiteDatabase db = helper.getWritableDatabase();
     6 
     7            ContentValues values = new ContentValues();
     8 
     9            values.put("name", newName);
    10 
    11            db.update("t_student", values, "id=?", new String[]{id+""});
    12 
    13 }

    测试代码:

    1 public void testUpdate(){
    2 
    3            StudentDao dao = new StudentDao(getContext());
    4 
    5            dao.updateNameById(1, "dqrcsc");
    6 
    7 }

    运行前后:

     

    delete()方法的使用

    StudentDao类添加:删除指定id的学生

    1 public void deleteById(int id){
    2 
    3            helper = new MySqliteHelper(context,"students.db", null, 1);
    4 
    5            SQLiteDatabase db = helper.getWritableDatabase();
    6 
    7            db.delete("t_student", "id=?", new String[]{id+""});
    8 
    9 }

    测试代码:

    1 public void testDelete(){
    2 
    3            StudentDao dao = new StudentDao(getContext());
    4 
    5            dao.deleteById(2);
    6 
    7 }

     运行前后:

     

    insert()方法的使用

    StudentDao类中添加如下方法:

     1 public void addStudent(Student stu){
     2 
     3            helper = new MySqliteHelper(context,"students.db", null, 1);
     4 
     5            SQLiteDatabase db = helper.getWritableDatabase();
     6 
     7            ContentValues values = new ContentValues();
     8 
     9            values.put("name", stu.getName());
    10 
    11            values.put("gender", stu.getGender());
    12 
    13            values.put("age", stu.getAge());
    14 
    15            db.insert("t_student", null, values);
    16 
    17 }

    测试代码:

    1 public void testAddStudent(){
    2 
    3            StudentDao dao = new StudentDao(getContext());
    4 
    5            dao.addStudent(new Student(0,"csc","male",24));
    6 
    7 }

    运行前后:

     

    query()方法的使用

     1 public Student getStudentById(int id){
     2 
     3            Student stu = null;
     4 
     5            helper = new MySqliteHelper(context,"students.db", null, 1);
     6 
     7            SQLiteDatabase db = helper.getWritableDatabase();
     8 
     9            Cursor cursor = db.query("t_student", new String[]{"id","name","gender","age"}, "id=?", new String[]{id+""}, null, null, null);
    10 
    11            if(cursor == null){
    12 
    13                  return null;
    14 
    15            }
    16 
    17            if(cursor.moveToFirst()){
    18 
    19                  stu = new Student(cursor.getInt(0),cursor.getString(1),cursor.getString(2),cursor.getInt(3));
    20 
    21            }
    22 
    23            return stu;
    24 
    25 }

    测试代码:

    1 public void testGetStudentById(){
    2 
    3            StudentDao dao = new StudentDao(getContext());
    4 
    5            Student stu = dao.getStudentById(1);
    6 
    7            Log.i("MYSQLITEHELPER",stu.toString());
    8 
    9 }

    输出:

     

     关于事务的操作

    被用烂来的例子,就是银行转账问题,一个账户转出,一个账户转入,两个操作要么同时成功,要么同时失败。

    这里懒得再建一张表了,就转年龄吧,其实换汤不换药,原理完全一样。

    假设年龄可以在学生之间转换,我要把自己的年龄转10岁给lisi这个同学,要保证这整个操作的原子性,就需要用到事务。

    在StudentDao中添加转账年龄的方法:

     1 public void transAge(){
     2 
     3            helper = new MySqliteHelper(context,"students.db", null, 1);
     4 
     5            SQLiteDatabase db = helper.getWritableDatabase();
     6 
     7            db.execSQL("update t_student set age = age - 10 where name = ?", new String[]{"dqrcsc"});
     8 
     9            int i = 1/0;
    10 
    11            db.execSQL("update t_student set age = age + 10 where name = ?", new String[]{"lisi"});
    12 
    13 }

    注意两条update语句之间有个1/0的操作,会导致程序异常终止,只有第一条被执行。

    执行前后:

     

    可以看到,我的年龄减去10岁,而lisi的年龄并没有加上10岁。

    修改代码,改用事务处理:

     1 public void transAge(){
     2 
     3            helper = new MySqliteHelper(context,"students.db", null, 1);
     4 
     5            SQLiteDatabase db = helper.getWritableDatabase();
     6 
     7            db.beginTransaction();
     8 
     9            try{
    10 
    11                  db.execSQL("update t_student set age = age - 10 where name = ?", new String[]{"dqrcsc"});
    12 
    13                  int i = 1/0;
    14 
    15                  db.execSQL("update t_student set age = age + 10 where name = ?", new String[]{"lisi"});
    16 
    17                  db.setTransactionSuccessful();
    18 
    19            }finally{
    20 
    21                  db.endTransaction();
    22 
    23            }
    24 
    25 }

    这一次,我的年龄没有减少,lisi的年龄也没有增加,保证了一致性。

    关于更新数据库版本的简单示例

    如,由于业务需要,更新了APP,新版本的APP在数据库中增加了一张教师表,现在修改onCreate()方法:

     1 public static final String CREATE_TEACHER = "create table t_teacher(id integer primary key, name varchar(20))";
     2 
     3 public void onCreate(SQLiteDatabase db) {
     4 
     5            // TODO Auto-generated method stub
     6 
     7            db.execSQL(CREATE_STUDENT);
     8 
     9            db.execSQL(CREATE_TEACHER);
    10 
    11 }

    运行,发现根本没有增加t_teacher这张表,因为数据库已存在,onCreate()方法不会被运行,这时,就需要用到onUpgrade()方法了:

     1 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
     2 
     3            // TODO Auto-generated method stub
     4 
     5            if(oldVersion == 1 && newVersion == 2){
     6 
     7                  db.execSQL(CREATE_TEACHER);
     8 
     9            }
    10 
    11 }

    在StudentDao中添加addTeacher()方法:

    1 public void addTeacher(){
    2 
    3            helper = new MySqliteHelper(context,"students.db", null, 2);
    4 
    5            SQLiteDatabase db = helper.getWritableDatabase();
    6 
    7            db.execSQL("insert into t_teacher(name) values(?)",new String[]{"wanger"});
    8 
    9 }

    注意,这里指定的版本号为2。

    测试运行结果:

     

    数据库操作的学习,就简单学到这里。

  • 相关阅读:
    人工智能 有信息搜索 (启发式)
    python学习之高阶函数
    文件学习
    codeblock快捷键使用
    React项目之BrowserRouter路由方式之-------生产环境404问题
    React生产环境打包&&后台环境运行(有跨域+无跨域)
    React前台改用HashRouter并解决两个问题
    React路由基础
    React前台404组件页面+路由控制重定向
    react调用方法
  • 原文地址:https://www.cnblogs.com/dqrcsc/p/4636162.html
Copyright © 2011-2022 走看看