美文网首页Android进阶Android开发android基础
android数据库的简单Demo(原生版+Google版)

android数据库的简单Demo(原生版+Google版)

作者: 阿敏其人 | 来源:发表于2015-11-13 11:46 被阅读6530次

    本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。

    android和ios的数据库都是用SQLite来实现。
    在安卓里面数据库怎么用呢,简单来说可用分为以下三步:
    1、新建一个数据库帮助类,继承自SQLiteOpenHelper,复写onCreate() 和 onUpgrade()
    2、新建一个数据里操作类(dao类),利用 数据库帮助类 得到数据库的实例,然后在dao类里面编写 增删改查 的方法
    3、在Activity里面实例化数据库操作类(dao类),调用对应的 增删查改方法。
    (对数据进行的操作也就无非四种,即CRUD。其中C代表添加(Create),R代表查询(Retrieve),U代表更新(Update),D代表删除(Delete)。有人叫增删改查,有人叫增删查改)

    来图片最直接

    增删改查演示.gif

    结构简图

    数据库demo结构简图.png

    一、SQLite数据库简介

    • 轻量级 : SQLite数据库是一个轻量级的数据库, 适用于少量数据的CURD;
    • 文件本质 : SQLite数据库支持大部分SQL语法, 允许使用SQL语句操作数据库, 其本质是一个文件, 不需要安装启动;
    • 数据读写 : SQLite数据库打开只是打开了一个文件的读写流, 如果有大数据量读写, 需要高并发存储, 那么就不应该使用SQLite;

    二、安卓数据如何使用

    1、编写数据库帮助类

    • 新建一个数据库帮助类,继承自SQLiteOpenHelper
    • 编写构造函数
    • 复写onCreate() 和 onUpgrade()

    代码如下:

    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    /**
     * 数据库Helper类,必须继承自 SQLiteOpenHelper
     * 当一个继承自 SQLiteOpenHelper 后需要复写两个方法,分别是 onCreate()  和 onUpgrade()
     * onCreate(): onCreate是在数据库创建的时候调用的,主要用来初始化数据表结构和插入数据初始化的记录
     * onUpgrade():onUpGrade是在数据库版本升级的时候调用的,主要用来改变表结构
     *
     *
     *  数据库帮助类要做的事情特别简单:
     *  1、复写onCreate()  和 onUpgrade()方法
     *  2、在这两个方法里面填写相关的sql语句
     *
     *
     */
    public class MyDBHelper extends SQLiteOpenHelper{
    
        public MyDBHelper(Context context) {
            /**
             * 参数说明:
             *
             * 第一个参数: 上下文
             * 第二个参数:数据库的名称
             * 第三个参数:null代表的是默认的游标工厂
             * 第四个参数:是数据库的版本号  数据库只能升级,不能降级,版本号只能变大不能变小
             */
            super(context, "mintest.db", null, 2);
        }
    
    
        /**
         * onCreate是在数据库创建的时候调用的,主要用来初始化数据表结构和插入数据初始化的记录
         *
         * 当数据库第一次被创建的时候调用的方法,适合在这个方法里面把数据库的表结构定义出来.
         * 所以只有程序第一次运行的时候才会执行
         * 如果想再看到这个函数执行,必须写在程序然后重新安装这个app
         */
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table contactinfo (id integer primary key autoincrement, name varchar(20), phone varchar(20))");
        }
    
    
        /**
         * 当数据库更新的时候调用的方法
         * 这个要显示出来得在上面的super语句里面版本号发生改变时才会 打印  (super(context, "itheima.db", null, 2); )
         * 注意,数据库的版本号只可以变大,不能变小,假设我们当前写的版本号是3,运行,然后又改成1,运行则报错。不能变小
         * @param db
         * @param oldVersion
         * @param newVersion
         */
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("alter table contactinfo add account varchar(20)");
        }
    }
    
    

    2、数据库操作类(dao类)

    dao类在这里做得事情特别简单:

    • 1、定义一个构造方法,利用这个方法去实例化一个 数据库帮助类
    • 2、编写dao类的对应的 增删改查 方法。

    代码如下:

    
    package amqr.com.dbanddao.dao;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    
    import amqr.com.dbanddao.db.MyDBHelper;
    
    /**
     *  ContactInjfoDao  数据库操作类  dao后缀的都是数据库操作类
     *
     *  我们这里的每一个 增删改查 的方法都通过getWritableDatabase()去实例化了一个数据库,这里必须这么做
     *  不客气抽取 成为一个成员变量, 否则报错,若是觉得麻烦可以通过定义方法来置为null和重新赋值
     *
     *  —— 其实dao类在这里做得事情特别简单:
     *  1、定义一个构造方法,利用这个方法去实例化一个  数据库帮助类
     *  2、编写dao类的对应的 增删改查 方法。
     *
     */
    public class ContactInjfoDao {
    
        private MyDBHelper mMyDBHelper;
    
        /**
         * dao类需要实例化数据库Help类,只有得到帮助类的对象我们才可以实例化 SQLiteDatabase
         * @param context
         */
        public ContactInjfoDao(Context context) {
            mMyDBHelper=new MyDBHelper(context);
        }
    
        // 将数据库打开帮帮助类实例化,然后利用这个对象
        // 调用谷歌的api去进行增删改查
    
        // 增加的方法吗,返回的的是一个long值
        public long addDate(String name,String phone){
            // 增删改查每一个方法都要得到数据库,然后操作完成后一定要关闭
            // getWritableDatabase(); 执行后数据库文件才会生成
            // 数据库文件利用DDMS可以查看,在 data/data/包名/databases 目录下即可查看
            SQLiteDatabase sqLiteDatabase =  mMyDBHelper.getWritableDatabase();
            ContentValues contentValues=new ContentValues();
    
            contentValues.put("name",name);
            contentValues.put("phone", phone);
            // 返回,显示数据添加在第几行
            // 加了现在连续添加了3行数据,突然删掉第三行,然后再添加一条数据返回的是4不是3
            // 因为自增长
            long rowid=sqLiteDatabase.insert("contactinfo",null,contentValues);
    
            sqLiteDatabase.close();
            return rowid;
        }
    
    
        // 删除的方法,返回值是int
        public int deleteDate(String name){
            SQLiteDatabase sqLiteDatabase = mMyDBHelper.getWritableDatabase();
            int deleteResult = sqLiteDatabase.delete("contactinfo", "name=?", new String[]{name});
            sqLiteDatabase.close();
            return deleteResult;
        }
    
        /**
         * 修改的方法
         * @param name
         * @param newPhone
         * @return
         */
        public int updateData(String name,String newPhone){
            SQLiteDatabase sqLiteDatabase = mMyDBHelper.getWritableDatabase();
            ContentValues contentValues =new ContentValues();
            contentValues.put("phone", newPhone);
            int updateResult = sqLiteDatabase.update("contactinfo", contentValues, "name=?", new String[]{name});
            sqLiteDatabase.close();
            return updateResult;
        }
    
        /**
         * 查询的方法(查找电话)
         * @param name
         * @return
         */
        public String alterDate(String name){
            String phone = null;
    
            SQLiteDatabase readableDatabase = mMyDBHelper.getReadableDatabase();
            // 查询比较特别,涉及到 cursor
            Cursor cursor = readableDatabase.query("contactinfo", new String[]{"phone"}, "name=?", new String[]{name}, null, null, null);
            if(cursor.moveToNext()){
                phone=cursor.getString(0);
            }
            cursor.close(); // 记得关闭 corsor
            readableDatabase.close(); // 关闭数据库
            return phone;
        }
    
    
    }
    
    
    

    3、在Activity里面利用dao类操作数据库

    xml布局文件

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  xmlns:tools="http://schemas.android.com/tools"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:orientation="vertical"
                  tools:context=".MainActivity" >
        <EditText
            android:id="@+id/mEtName"
            android:hint="请输入联系人的姓名"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            >
        </EditText>
        <EditText
            android:id="@+id/mEtPhone"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="请输入联系人的电话"
            android:inputType="phone" />
        <Button
            android:onClick="add"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="添加" />
        <Button
            android:onClick="delete"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="删除" />
        <Button
            android:onClick="update"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="修改" />
        <Button
            android:onClick="query"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="查询" />
    </LinearLayout>
    

    4、Activity代码:

    package amqr.com.dbanddao;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.text.TextUtils;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.Toast;
    
    import amqr.com.dbanddao.dao.ContactInjfoDao;
    
    public class MainActivity extends AppCompatActivity {
       private EditText mEtName;
       private EditText mEtPhone;
       private ContactInjfoDao mDao;
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
    
           mDao=new ContactInjfoDao(MainActivity.this);
           mEtName= (EditText) findViewById(R.id.mEtName);
           mEtPhone= (EditText) findViewById(R.id.mEtPhone);
    
       }
    
       public void add(View view){
    
           String name=mEtName.getText().toString().trim();
           String phone=mEtPhone.getText().toString().trim();
           if(TextUtils.isEmpty(name)||TextUtils.isEmpty(phone)){
               Toast.makeText(this,"填写不完整",Toast.LENGTH_SHORT).show();
               return;
           }else{
               long addLong = mDao.addDate(name, phone);
               if(addLong==-1){
                   Toast.makeText(this,"添加失败",Toast.LENGTH_SHORT).show();
               }else{
                   Toast.makeText(this,"数据添加在第  "+addLong+"   行",Toast.LENGTH_SHORT).show();
               }
    
           }
       }
    
       public void delete(View view){
           String name=mEtName.getText().toString().trim();
    
    
           if(TextUtils.isEmpty(name)){
               Toast.makeText(this,"填写不完整",Toast.LENGTH_SHORT).show();
               return;
           }else{
               int deleteDate = mDao.deleteDate(name);
               if(deleteDate==-1){
                   Toast.makeText(this,"删除失败",Toast.LENGTH_SHORT).show();
               }else{
                   Toast.makeText(this,"成功删除  "+deleteDate+"   条数据",Toast.LENGTH_SHORT).show();
               }
    
           }
    
       }
    
    
       public void update(View view){
    
           String name=mEtName.getText().toString().trim();
           String phone=mEtPhone.getText().toString().trim();
           if(TextUtils.isEmpty(name)||TextUtils.isEmpty(phone)){
               Toast.makeText(this,"填写不完整",Toast.LENGTH_SHORT).show();
               return;
           }else{
               int count=mDao.updateData(name, phone);
               if(count==-1){
                   Toast.makeText(this,"更新失败",Toast.LENGTH_SHORT).show();
               }else{
                   Toast.makeText(this,"数据更新了  "+count+"   行",Toast.LENGTH_SHORT).show();
               }
    
           }
       }
    
    
       public void query(View view){
    
           String name=mEtName.getText().toString().trim();
           
           if(TextUtils.isEmpty(name)){
               Toast.makeText(this,"填写不完整",Toast.LENGTH_SHORT).show();
               return;
           }else{
               String phoneResult = mDao.alterDate(name);
    
               Toast.makeText(this,"手机号码为:    "+phoneResult,Toast.LENGTH_SHORT).show();
    
    
           }
       }
    
    }
    
    

    至此完成。

    三、数据库文件在那里

    当我们调用dao里面的方法,dao里面的方法就都会实例化数据库,比如
    JAVA SQLiteDatabase sqLiteDatabase = mMyDBHelper.getWritableDatabase();
    当这句代码的getWritableDatabase();执行后生成数据库文件

    具体位置在data/data/包名/databases路径下

    数据库文件.png

    四、简单的数据库语句知识

    在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型。
    虽然它支持的类型只有五种,但实际上sqlite3也接受varchar(n)、char(n)、decimal(p,s) 等数据类型,只不过在运算或保存时会转成对应的五种数据类型。
    SQLite最大的特点是你可以把各种类型的数据保存到任何字段中,而不用关心字段声明的数据类型是什么。例如:可以在Integer类型的字段中存放字符串,或者在布尔型字段中存放浮点数,或者在字符型字段中存放日期型值。 但有一种情况例外:定义为INTEGER PRIMARY KEY的字段只能存储64位整数, 当向这种字段保存除整数以外的数据时,将会产生错误。 另外,在编写CREATE TABLE 语句时,你可以省略跟在字段名称后面的数据类型信息,如下面语句你可以省略 name字段的类型信息:

    CREATE TABLE person (personid integer primary key autoincrement, name varchar(20))
    

    .

    SQLite可以解析大部分标准SQL语句,如:

    • 查询语句:select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句

    如: select * from person
    select * from person order by id desc
    select name from person group by name having count(*)>1

    分页SQL与mysql类似,下面SQL语句获取5条记录,跳过前面3条记录

    select * from Account limit 5 offset 3 或者 select * from Account limit 3,5

    • 插入语句:insert into 表名(字段列表) values(值列表)。如: insert into person(name, age) values(‘张三’,3)

    • 更新语句:update 表名 set 字段名=值 where 条件子句。如:update person set name=‘张三‘ where id=10

    • 删除语句:delete from 表名 where 条件子句。如:delete from person where id=10

    获取添加记录后自增长的ID值:SELECT last_insert_rowid()


    五、谷歌专版的增删改查

    在上面的代码中,我们的dao类的里面的增删改查的每一个方法都需要编写你sql语句,sql语句比较麻烦,容易出错,所以google封装了一下sql语句,方便我们调用。

    SQLiteDatabase中有几个方法,可用于对数据库进行增删查改的操作。

    1、SQLiteDatabase 增,insert()

    • 增 insert()
      insert()方法,这个方法就是专门用于添加数据的。

    它接收三个参数,
    第一个参数是表名,我们希望向哪张表里添加数据,这里就传入该表的名字。
    第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可。第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可。

        /**
         * 添加一条记录
         * @param name 联系人姓名
         * @param phone 联系人电话
         * @return 返回的是添加后在数据库的行号  -1代表添加失败
         */
        public long add(String name, String phone){
            SQLiteDatabase db = helper.getWritableDatabase();
            //db.execSQL("insert into contactinfo (name,phone) values (?,?)", new Object[]{name,phone});
            ContentValues values = new ContentValues();
            values.put("name", name);
            values.put("phone", phone);
            //内部是组拼sql语句实现的.
            long rowid = db.insert("contactinfo", null, values);
            //记得释放数据库资源
            db.close();
            return rowid;
        }
    

    2、SQLiteDatabase 删,delete()

    • 删 delete()

    delete()方法专门用于删除数据
    这个方法接收三个参数,
    第一个参数是表名,
    第二、第三个参数又是用于去约束删除某一行或某几行的数据,不指定的话默认就是删除所有行。

    
    /**
         * 根据姓名删除一条记录
         * @param name 要删除的联系人的姓名
         * @return 返回0代表的是没有删除任何的记录 返回整数int值代表删除了几条数据
         */
        public int delete(String name){
            //判断这个数据是否存在.
            SQLiteDatabase db = helper.getWritableDatabase();
            //db.execSQL("delete from contactinfo where name=?", new Object[]{name});
            int rowcount = db.delete("contactinfo", "name=?", new String[]{name});
            db.close();
            //再从数据库里面查询一遍,看name是否还在
            return rowcount;
        }
    

    2、SQLiteDatabase 改,update()

    • 改 update()方法用于对数据进行更新。
      这个方法接收四个参数,
      第一个参数是表名,在这里指定去更新哪张表里的数据。
      第二个参数是ContentValues对象,要把更新数据在这里组装进去。
      第三、第四个参数用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。
    
    /**
         * 修改联系人电话号码
         * @param newphone 新的电话号码
         * @param name 要修改的联系人姓名
         * @return 0代表一行也没有更新成功, >0 整数代表的是更新了多少行记录
         */
        public int update(String newphone , String name){
            SQLiteDatabase db = helper.getWritableDatabase();
            //db.execSQL("update contactinfo set phone =? where name=?", new Object[]{newphone,name});
            ContentValues values = new ContentValues();
            values.put("phone", newphone);
            int rowcount =  db.update("contactinfo", values, "name=?", new String[]{name});
            db.close();
            return rowcount;
        }
    

    4、SQLiteDatabase 查, query()

    • 查询 query()

    查询是数据库的重头戏,SQL的全称是Structured Query Language,翻译成中文就是结构化查询语言。它的大部功能都是体现在“查”这个字上的,而“增删改”只是其中的一小部分功能。

    SQLiteDatabase中还提供了一个query()方法用于对数据进行查询。这个方法的参数非常复杂,最短的一个方法重载也需要传入七个参数。

    query()方法参数 对应SQL部分 描述
    table from table_name 指定查询的表名
    columns select column1, column2 指定查询的列名
    selection where column = value 指定where的约束条件
    selectionArgs - 为where中的占位符提供具体的值
    groupBy group by column 指定需要group by的列
    having having column = value 对group by后的结果进一步约束
    orderBy order by column1, column2 指定查询结果的排序方式
    /**
         * 查询联系人的电话号码
         * @param name 要查询的联系人
         * @return 电话号码
         */
        public String getPhoneNumber(String name){
            String phone = null;
            SQLiteDatabase db = helper.getReadableDatabase();
            //Cursor  cursor = db.rawQuery("select phone from contactinfo where name=?", new String[]{name});
            Cursor  cursor =  db.query("contactinfo", new String[]{"phone"}, "name=?", new String[]{name}, null, null, null);
            if(cursor.moveToNext()){//如果光标可以移动到下一位,代表就是查询到了数据
                phone = cursor.getString(0);
            }
            cursor.close();//关闭掉游标,释放资源
            db.close();//关闭数据库,释放资源
            return phone;
        }
    

    5 完整的代码示例

    下面贴入一个使用google的SQLiteDatabase提供的insert等四个方法进行增删查改的dao类(上面的布局文件,打开帮助类不变)的完整代码:

    package com.amqr.test.googledbdao.dao;
    
    import android.content.ContentValues;
    import android.content.Context;
    import android.database.Cursor;
    import android.database.sqlite.SQLiteDatabase;
    
    import com.amqr.test.googledbdao.db.MyDBHelper;
    
    
    /**
    * 联系人数据库表的访问类
    */
    public class ContactInfoDao {
       /**
        * 数据库打开的帮助类
        */
       private MyDBHelper helper;
    
       /**
        * 在构造方法里面完成 必须要用的类的初始化
        * @param context
        */
       public ContactInfoDao(Context context) {
           helper = new MyDBHelper(context);
       }
    
       /**
        * 添加一条记录
        * @param name 联系人姓名
        * @param phone 联系人电话
        * @return 返回的是添加后在数据库的行号  -1代表添加失败
        */
       public long add(String name, String phone){
           SQLiteDatabase db = helper.getWritableDatabase();
           //db.execSQL("insert into contactinfo (name,phone) values (?,?)", new Object[]{name,phone});
           ContentValues values = new ContentValues();
           values.put("name", name);
           values.put("phone", phone);
           //内部是组拼sql语句实现的.
           long rowid = db.insert("contactinfo", null, values);
           //记得释放数据库资源
           db.close();
           return rowid;
       }
       /**
        * 根据姓名删除一条记录
        * @param name 要删除的联系人的姓名
        * @return 返回0代表的是没有删除任何的记录 返回整数int值代表删除了几条数据
        */
       public int delete(String name){
           //判断这个数据是否存在.
           SQLiteDatabase db = helper.getWritableDatabase();
           //db.execSQL("delete from contactinfo where name=?", new Object[]{name});
           int rowcount = db.delete("contactinfo", "name=?", new String[]{name});
           db.close();
           //再从数据库里面查询一遍,看name是否还在
           return rowcount;
       }
       /**
        * 修改联系人电话号码
        * @param newphone 新的电话号码
        * @param name 要修改的联系人姓名
        * @return 0代表一行也没有更新成功, >0 整数代表的是更新了多少行记录
        */
       public int update(String newphone , String name){
           SQLiteDatabase db = helper.getWritableDatabase();
           //db.execSQL("update contactinfo set phone =? where name=?", new Object[]{newphone,name});
           ContentValues values = new ContentValues();
           values.put("phone", newphone);
           int rowcount =  db.update("contactinfo", values, "name=?", new String[]{name});
           db.close();
           return rowcount;
       }
       /**
        * 查询联系人的电话号码
        * @param name 要查询的联系人
        * @return 电话号码
        */
       public String getPhoneNumber(String name){
           String phone = null;
           SQLiteDatabase db = helper.getReadableDatabase();
           //Cursor  cursor = db.rawQuery("select phone from contactinfo where name=?", new String[]{name});
           Cursor  cursor =  db.query("contactinfo", new String[]{"phone"}, "name=?", new String[]{name}, null, null, null);
           if(cursor.moveToNext()){//如果光标可以移动到下一位,代表就是查询到了数据
               phone = cursor.getString(0);
           }
           cursor.close();//关闭掉游标,释放资源
           db.close();//关闭数据库,释放资源
           return phone;
       }
    }
    
    
    

    .

    6、源码下载

    源码分成了两个module,一个是原生版的,一个是android提供的api版本的。

    下载源码(Android Studio格式)

    以下是两个module的说明图片


    compare.png

    六、数据库的事务

    • 数据库的事务有什么作用? —— 保证某些操作要么全部成功,要么都不成功。
    • 数据库是什么时候用? 比如金额交易,数据的批量删除和存储等。比如现在要进行银行转账,不可能扣了A的钱,但是B没有收到,然后A的钱就不知道哪里去了,再比如,现在要备份短信,要么成功备份到磁盘,要么备份失败,而不可能在没有备份成功的情况就把本地给删除了。
    • 数据库的事务怎么用?下面展示一份例子:
            btn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    SQLiteDatabase db = dbHelper.getWritableDatabase();
                    db.beginTransaction(); // 开启事务
                    try {
                        // 执行删除操作  db.delete(..........)
                        if (true) {
                            // 在这里手动抛出一个异常,让事务失败
                            throw new NullPointerException();
                        }
                        // 执行添加操作
                        db.setTransactionSuccessful(); // 事务已经执行成功
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        db.endTransaction(); // 结束事务
                    }
                }
            });
    
    

    1、调用SQLiteDatabase的 beginTransaction() 方法来开启一个事务,然后在一个异常捕获的代码块中去执行具体的数据库操作,
    2、当所有的操作都完成之后,调用 setTransactionSuccessful() 表示事务已经执行成功了
    3、最后在finally代码块中调用 endTransaction() 来结束事务。

    本篇完。

    相关文章

      网友评论

      • 哦呀比:新手。请问楼主Activity里面的button点击事件怎么写的啊
      • navy_legend:文章不错,感谢楼主

      本文标题:android数据库的简单Demo(原生版+Google版)

      本文链接:https://www.haomeiwen.com/subject/cvzhhttx.html