Android数据库

什么情况下我们才用数据库做数据存储?

大量数据结构相同的数据需要存储时。Android内置了sqlite,轻量级。

创建数据库的方法

  1. 创建一个类继承SqliteOpenHelper,需要添加一个构造方法,实现两个方法oncreate ,onupgrade。
package com.example.databasedemo;

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

public class MyDatabaseOpenHelper extends SQLiteOpenHelper {

    public static final String CREATE_BOOK = "create table book ("
            + "_id integer primary key autoincrement, name varchar(20), telephone varchar(11))";

    public static final String NEW_TABLE = "create table book ("
            + "_id integer primary key autoincrement, price real, pages integer)";
    private Context mContext;

    /**
     *
     * @param context 上下文
     * @param name    数据库的名称
     * @param factory 用来创建cursor对象,填入null使用默认的
     * @param version version:数据库的版本号,从1开始,如果发生改变,onUpgrade方法将会调用,4.0之后只能升不能降
     */
    public MyDatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }
    // 调用getReadableDatabase()或者getWritableDatabase()时会调用该方法
    // 第一次创建数据库时才能执行该方法,特别适合做表结构的初始化
    // 传入的参数db可以用来执行sql语句
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(NEW_TABLE); // 另外一个表
    }

    // version改变时,调用这个方法,version只能升不能降
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 先删除,不删除就onCreate发现表存在会报错。
      // 若是表被删除了,或者oncreate里面又新建一个表。因为之前已经创建了aa.db,onCreate()方法不会得到执行。则不能创建成功。所以这里需要删除后再重建
        db.execSQL("drop table if exists book");
        db.execSQL("drop table if exists people");
      // 强制执行onCreate
        onCreate(db);
        db.execSQL("alter table book add author varchar(20)");
        db.execSQL("alter table people add age integer");

    }
}
 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        // 1.创建一个帮助类的对象,调用getReadableDatabase方法,返回一个SqliteDatebase对象
        MyDatabaseOpenHelper dbHelper = new MyDatabaseOpenHelper(mContext, "demo.db", null, 15);
        // 创建数据库,有则打开,没有则create
        dbHelper.getReadableDatabase();
    }

帮助类对象中的getWritableDatabase 和 getReadableDatabase都可以帮助我们获取一个数据库操作对象SqliteDatabase。

区别:

  • getReadableDatabase: 先尝试以读写方式打开数据库,如果磁盘空间满了,他会重新尝试以只读方式打开数据库。
  • getWritableDatabase: 直接以读写方式打开数据库,如果磁盘空间满了,就直接报错。

数据库的CURD - 1

  • 对上面建的book表进行CURD

  • 使用上面的MyDatabaseOpenHelper创建数据库和表。
  • 封装一个InfoBean来存储表的信息。
  • 封装一个InfoDao来返回一个MyDatabaseOpenHelper,以及执行增删改查操作。

bean用来封装表的数据。

package com.example.databasedemo.dao;
// 这些变量名和建表时候字段对应
public class InfoBean {
    public int _id;
    public String name;
    public int age;
    public String telephone;
}

封装好的执行增删改查的类,注意db不要随便db.close,容易引发错误。

package  com.example.databasedemo.dao;

import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.example.databasedemo.MyDatabaseOpenHelper;

public class InfoDao {

    private MyDatabaseOpenHelper myDatabaseOpenHelper;
    private SQLiteDatabase db;

    public InfoDao(Context context ,String dbName , int version){
        //创建一个帮助类对象
        myDatabaseOpenHelper = new MyDatabaseOpenHelper(context, dbName, null, version);
        db = myDatabaseOpenHelper.getReadableDatabase();
    }

    public void add(InfoBean bean){
        //sql:sql语句,  bindArgs:sql语句中占位符的值
        db.execSQL("insert into people(name,telephone) values(?,?);", new Object[]{bean.name,bean.telephone});
    }

    public void del(String name){
        //sql:sql语句,  bindArgs:sql语句中占位符的值
        db.execSQL("delete from people where name=?;", new Object[]{name});
    }
    public void update(InfoBean bean){
        //sql:sql语句,  bindArgs:sql语句中占位符的值
        db.execSQL("update people set telephone=? where name=?;", new Object[]{bean.telephone, bean.name});

    }
    public void query(String name){
        //原始查询 --> sql:sql语句,  selectionArgs:查询条件占位符的值,返回一个cursor对象
        Cursor cursor = db.rawQuery("select _id, name, telephone from people where name = ?;", new String []{name});
        //解析Cursor中的数据
        if(cursor != null && cursor.getCount() >0){//判断cursor中是否存在数据

            //循环遍历结果集,获取每一行的内容
            while(cursor.moveToNext()){ //条件,游标能否定位到下一行
                //获取数据
                int id = cursor.getInt(cursor.getColumnIndex("_id"));
                String name_str = cursor.getString(cursor.getColumnIndex("name"));
                String phone = cursor.getString(cursor.getColumnIndex("telephone"));
                Log.d("query result", "[ _id:"+id+" ,name:"+name_str+" ,phone:"+phone+" ]");
            }
            cursor.close();//关闭结果集
        }
    }
}

布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context="com.example.databasedemo.MainActivity">

    <Button
        android:id="@+id/bt_add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/add"/>
    <Button
        android:id="@+id/bt_update"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/update"/>
    <Button
        android:id="@+id/bt_del"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/del"/>
    <Button
        android:id="@+id/bt_query"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/query"/>

</LinearLayout>

MainActivity

package com.example.databasedemo;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import  com.example.databasedemo.dao.InfoBean;
import  com.example.databasedemo.dao.InfoDao;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Context mContext;
    private InfoDao infoDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        // 创建数据库,有则打开,没有则create
        infoDao = new InfoDao(mContext, "test.db", 3);

        Button btAdd = (Button) findViewById(R.id.bt_add);
        Button btDel = (Button) findViewById(R.id.bt_del);
        Button btUpdate = (Button) findViewById(R.id.bt_update);
        Button btQuery = (Button) findViewById(R.id.bt_query);

        btAdd.setOnClickListener(this);
        btDel.setOnClickListener(this);
        btUpdate.setOnClickListener(this);
        btQuery.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        InfoBean bean = null;
        InfoBean bean1 = null;
        switch (v.getId()) {
            case R.id.bt_add:

                bean = new InfoBean();
                bean.name = "张三";
                bean.telephone = "119";
                infoDao.add(bean);

                bean1 = new InfoBean();
                bean1.name = "李四";
                bean1.telephone = "120";
                infoDao.add(bean1);
                break;

            case R.id.bt_del:

                infoDao.del("张三");
                break;

            case R.id.bt_update:

                bean = new InfoBean();
                bean.name = "张三";
                bean.telephone = "110";
                infoDao.update(bean);
                break;

            case R.id.bt_query:
                infoDao.query("张三");
                infoDao.query("李四");
                break;

            default:
                break;
        }
    }
}

上面的方法基本是手写sql语句,容易写错。还有一种更便捷的方法 。

数据库的CURD -2

使用db.insert()、db.delete()、db.update()、db.query()

package dao;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.example.databasedemo.MyDatabaseOpenHelper;

public class InfoDao {

    private MyDatabaseOpenHelper myDatabaseOpenHelper;
    private SQLiteDatabase db;

    public InfoDao(Context context ,String dbName , int version){
        //创建一个帮助类对象
        myDatabaseOpenHelper = new MyDatabaseOpenHelper(context, dbName, null, version);
        db = myDatabaseOpenHelper.getReadableDatabase();
    }

    public boolean add(InfoBean bean){

        //执行sql语句需要sqliteDatabase对象
        ContentValues values = new ContentValues(); // 是用Map封装的对象
        values.put("name", bean.name);
        values.put("telephone", bean.telephone);
        // 第二个参数可以为空,返回值表示新增的行号,-1表示添加失败
        long result =  db.insert("people", null, values);

        return result != -1;
    }

    public int del(String name){
        //执行sql语句需要sqliteDatabase对象

        int count = db.delete("people", "name = ?", new String[]{name});
        return count;
    }

    public int update(InfoBean bean){
        ContentValues values = new ContentValues();
        values.put("telephone", bean.telephone);
        int count = db.update("people", values, "name = ?", new String[]{bean.name});
        return count;
    }
    public void query(String name){
        // 查询people表中的name为参数指定的"_id", "name", "telephone"字段,按照id递减
        Cursor cursor = db.query("people", new String[]{"_id", "name", "telephone"}, "name = ?", new String[]{name}, null, null, "_id desc");

        //解析Cursor中的数据
        if(cursor != null && cursor.getCount() >0){//判断cursor中是否存在数据

            //循环遍历结果集,获取每一行的内容
            while(cursor.moveToNext()){ //条件,游标能否定位到下一行
                //获取数据
                int id = cursor.getInt(cursor.getColumnIndex("_id"));
                String name_str = cursor.getString(cursor.getColumnIndex("name"));
                String phone = cursor.getString(cursor.getColumnIndex("telephone"));
                Log.d("query result", "[ _id:"+id+" ,name:"+name_str+" ,phone:"+phone+" ]");
            }
            cursor.close();//关闭结果集
        }
    }
}
@Override
public void onClick(View v) {
    InfoBean bean = null;
    int count = 0;
    switch (v.getId()) {
        case R.id.bt_add:

            bean = new InfoBean();
            bean.name = "张三";
            bean.telephone = 119;
            boolean result = infoDao.add(bean);
            if (result) {
                Toast.makeText(mContext, "添加成功", Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.bt_del:

            count = infoDao.del("张三");
            Toast.makeText(mContext, "删除"+count+"行", Toast.LENGTH_SHORT).show();
            break;

        case R.id.bt_update:
            bean = new InfoBean();
            bean.name = "张三";
            bean.telephone = 110;
            count = infoDao.update(bean);
            Toast.makeText(mContext, "更新"+count+"行", Toast.LENGTH_SHORT).show();
            break;

        case R.id.bt_query:
            infoDao.query("张三");
            break;

        default:
            break;
    }
}

使用以上方法不容易写错sql语句,而且其返回值能方便地知道数据变化了几条。

使用第二种方法更简单一些,但是不能多表查询(传参时候只能传入一个table)。而第一种手写rawQuery()的方法可以实现。可谓各有利弊。

数据库中的事务

执行多条sql语句,要么同时执行成功,要么同时执行失败。不能有的成功,有的失败。失败了则会回滚。

举个银行转账的例子。因为各种原因比如在转账过程中突然断电断网,不能使得资金流失。李四个张三转200,张三要么收到两百。要么退还给李四(回滚到未转钱的时候)。

还是需要一个继承自SQLiteOpenHelper的类,不过这次的比较简单了。

package com.example.trancaction;

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

public class MyDatabaseOpenHelper extends SQLiteOpenHelper {

    /**
     * @param context 上下文
     * @param name    数据库的名称
     * @param factory 用来创建cursor对象,填入null使用默认的
     * @param version version:数据库的版本号,从1开始,如果发生改变,onUpgrade方法将会调用,4.0之后只能升不能降
     */
    public MyDatabaseOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

  // 直接在创建表的时候就添加数据
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table account (_id integer primary key autoincrement,name varchar(20),money varchar(20))");
        db.execSQL("insert into account ('name','money') values ('张三','2000')");
        db.execSQL("insert into account ('name','money') values ('李四','5000')");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO: 2017/4/11
    }
}
package com.example.trancaction;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;

        Button btTransfer = (Button) findViewById(R.id.bt_transfer);
        btTransfer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyDatabaseOpenHelper dbHelper = new MyDatabaseOpenHelper(mContext, "account.db", null, 1);
                SQLiteDatabase db = dbHelper.getReadableDatabase();
                //3.转账,将李四的钱减200,张三加200
                db.beginTransaction();//开启一个数据库事务
                try {
                    // 如果没有事务,这里只会执行李四的钱转出,张三收不到。
                    db.execSQL("update account set money= money-200 where name=?", new String[]{"李四"});
                    int i = 100 / 0;//模拟一个异常
                    db.execSQL("update account set money= money+200 where name=?", new String[]{"张三"});

                    db.setTransactionSuccessful();//能运行到最后这儿,就标记事务中的sql语句全部成功执行
                } finally {
                    db.endTransaction();//判断事务的标记是否成功,如果不成功,回滚错误之前执行的sql语句
                }
            }
        });
    }
}

by @sunhaiyu

2017.4.13

Android数据库的更多相关文章

  1. 深入解析Sqlite的完美替代者,android数据库新王者——Realm

    写在前面: 又到一年一度七夕虐狗节,看着大家忍受着各种朋友圈和QQ空间还有现实生活中的轮番轰炸,我实在不忍心再在这里给大家补刀,所以我觉得今天不虐狗,继续给大家分享有用的. 如果你比较关心androi ...

  2. [Android Pro] 完美Android Cursor使用例子(Android数据库操作)

    reference to : http://www.ablanxue.com/prone_10575_1.html 完美 Android Cursor使用例子(Android数据库操作),Androi ...

  3. Android 数据库框架OrmLite的使用(一)

    在这里记录下最基本的用法,官网上可了解相关的介绍. 1.下载OrmLite jar 在下载android的:ormlite-android-4.48.jar和ormlite-core-4.48.jar ...

  4. Android数据库升级

    随着Android应用版本的迭代,经常遇到数据库表结构发生改变,或者一些指定的表数据需要更新.这也就引出一个问题Android数据库的更新问题. Android数据库升级分类 Android数据库更新 ...

  5. 优雅的处理Android数据库升级的问题

    原始完成于:2015-04-27 19:28:22 提供一种思路,优雅的处理Android数据库升级的问题,直接上代码: 1 package com.example.databaseissuetest ...

  6. Android数据库之SQLite数据库

    Android数据库之SQLite数据库 导出查看数据库文件 在android中,为某个应用程序创建的数据库,只有它可以访问,其它应用程序是不能访问的,数据库位于Android设备/data/data ...

  7. Android 数据库读取数据显示 [5]

    2016-12-1 课程内容 昨天学了Android数据库升级.降级.创建 今天把数据库表里面的数据读取出来显示到手机屏幕上 下面代码是MainActivity.java 的代码 package co ...

  8. Android数据库信息显示在listview上

    Key Points: 1.使用SimpleCursorAdapter将Android数据库信息显示在listview上 adapter = new SimpleCursorAdapter(this, ...

  9. android数据库持久化框架

    android数据库持久化框架

  10. Android数据库高手秘籍(一)——SQLite命令

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/38461239 要想熟练地操作不论什么一个数据库.最最主要的要求就是要懂SQL语言, ...

随机推荐

  1. 【踩坑记录】记一次MySQL主从复制延迟的坑

    最近开发中遇到的一个MySQL主从延迟的坑,记录并总结,避免再次犯同样的错误. 情景 一个活动信息需要审批,审批之后才能生效.因为之后活动要编辑,编辑后也可能触发审批,审批中展示的是编辑前的活动内容, ...

  2. VMware-VCSA-6.5安装过程

    1.新建虚拟机 2.选择从OVF或OVA文件导入 3.给虚拟机命名,并选择OVF文件. 4.选择虚拟机的存储位置.这里没有配置共享存储宿,这里选择的宿主机的存储. 5.许可协议同意就OK了. 6.部署 ...

  3. 如何在非 React 项目中使用 Redux

    本文作者:胡子大哈 原文链接:https://scriptoj.com/topic/178/如何在非-react-项目中使用-redux 转载请注明出处,保留原文链接和作者信息. 目录 1.前言 2. ...

  4. 10.application对象

    1.application对象实现了用户数据的共享,可存放全局变量 2.application开始于服务器的启动,终止于服务器的关闭. 3.在用户的前后连接或不同用户之间的连接中,可以对applica ...

  5. Mathematica学习笔记1

    非常喜欢Mathematica的"自动草稿纸"的理念,Magic Paper, 唔哈~ Mathematica的矩阵操作和Matlab的风格十分不同. [ ] 是一个Part运算符 ...

  6. selenium 环境搭建

    使用selenium + python来搭建环境的步骤: 1. 下载 python 的版本,常用到的有 2.7 和 3.6 2. 下载 selenium 的版本,通过命令进行下载. pip insta ...

  7. java-bootstrap

    先来看一段每一个项目都要写的BASH脚本. #!/usr/bin/env bash in start ) ;; stop ) ;; restart ) shift "$0" sto ...

  8. MySQL(三)--函数与谓词

    前文已有涉及,这里作为总结重新整理一下. 一.函数 1.算术函数 NUMERIC 是大多数 DBMS 都支持的一种数据类型,通过 NUMBERIC ( 全体位数, 小数位数 ) 的形式来指定数值的大小 ...

  9. JavaScript 语言中的 this

    JavaScript 语言中的 this 由于其运行期绑定的特性,JavaScript 中的 this 含义要丰富得多,它可以是全局对象.当前对象或者任意对象,这完全取决于函数的调用方式.JavaSc ...

  10. 虚拟硬盘格式vdi、vhd、vmdk相互转换

    Windows7的引导程序能够引导vhd格式的虚拟硬盘,而VirtualBox创建的虚拟硬盘文件是vdi格式的,怎么办呢? 以前要借助其他软件才能实现,但是VirtualBox早就悄悄为我们带来了一个 ...