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. 深入理解 JavaScript 事件循环(一)— event loop

    引言 相信所有学过 JavaScript 都知道它是一门单线程的语言,这也就意味着 JS 无法进行多线程编程,但是 JS 当中却有着无处不在的异步概念 .在初期许多人会把异步理解成类似多线程的编程模式 ...

  2. 使用 Socket.IO 开发聊天室

    前言 Socket.IO 是一个用来实现实时双向通信的框架,其本质是基于 WebSocket 技术. 我们首先来聊聊 WebSocket 技术,先设想这么一个场景: · 用户小A,打开了某个网站的充值 ...

  3. chrome谷歌浏览器-DevTool开发者工具-详细总结

    目录: 一.概述 1.官方文档 2.打开方法: 3.前言: 二.九个模块: 1.设备模式Device Mode 2.元素面板Elements 3.控制台面板Console 4.源代码面板Sources ...

  4. 【Android Developers Training】 2. 运行你的应用

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. 【Android Developers Training】 96. 运行一个同步适配器

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  6. 安装wamp的方法及过程

    地址栏输入:http://www.xiazaiba.com/html/27954.html下载wamp2.5版本,根据过程安装,其中会安装一些垃圾软件把不该勾选的去掉就可以.点击next,下一步下一步 ...

  7. ReadTeacherObj

    package JBJADV003;import java.io.*;public class ReadTeacherObj { /** * @param args */ public static ...

  8. 跟我一起读postgresql源码(六)——Executor(查询执行模块之——查询执行策略)

    时光荏苒,岁月如梭.楼主已经很久没有更新了.之前说好的一周一更的没有做到.实在是事出有因,没能静下心来好好看代码.当然这不能作为我不更新的理由,时间挤挤还是有的,拖了这么久,该再写点东西了,不然人就怠 ...

  9. ES6中的Set、Map数据结构

    Map.Set都是ES6新的数据结构,他们都是新的内置构造函数.也就是说typeof的结果,多了两个. 他们是什么:  Set是不能重复的数组.    Map是可以任何东西当做键的对象: ES6 提供 ...

  10. 自画一张linux基础架构学习框架图

    草图一张,仅供参考,学习并掌握下面这张图的技能只是个开始--