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. CSS都有哪些选择器?

    派生选择器(用HTML标签申明) id选择器(用DOM的ID申明) 类选择器(用一个样式类名申明) 属性选择器(用DOM的属性申明,属于CSS2,IE6不支持,不常用,不知道就算了) 除了前3种基本选 ...

  2. MiniProfiler使用点滴记录-2017年6月23日11:08:23

    1.看似针对同样一段查询表ef达式,重复执行却没有被记录下来.其实这是正常情况,因为ef并没有重复去执行 相同sql查询. 2.MiniProfiler结合MVC过滤器进行 拦截记录Sql,示例代码: ...

  3. CSS Why

    前面的话 在CSS学习目录中,已经详细地介绍了CSS如何使用.知其然,还要知其所以然.本文将介绍CSS各部分出现的原因,仅限个人理解,如有不妥,欢迎交流 Why CSS 早期的大多数网站标记几乎完全由 ...

  4. JAVA基础——变量和常量

    JAVA的变量和常量知识总结 一.认识java标识符 标识符就是用于给 Java 程序中变量.类.方法等命名的符号. 使用标识符时,需要遵守几条规则: 1.  标识符可以由字母.数字.下划线(_).美 ...

  5. win7-x64安装mysql5.7.11(官方zip版)

    1.下载官方安装包(http://www.mysql.com/downloads/),此zip包是没有安装器的(*.msi),也没有辅助配置的自动程序. 2.解压zip包,将文件放入指定目录,如:D: ...

  6. 流行框架angular

    ---恢复内容开始--- 一.angular是什么 一款非常优秀的前端高级js框架,由谷歌团队负责开发 angular是通过新的属性和表达扩展了html angular可以构建一个单一页面应用程序(s ...

  7. discuz 6.1.0F前台getshell(据说通用6.x , 7.x)

    EXP: 执行phpinfo()语句: GLOBALS[_DCACHE][smilies][searcharray]=/.*/eui; GLOBALS[_DCACHE][smilies][replac ...

  8. oracle预定义角色

    角色是相关权限的集合,使用角色能够简化权限的管理.简而言之就是oracle可以事先把一系列权限集中在一起(角色),打包赋予给用户,那么用户就具有了角色的一系列权限. oracle预定义角色有25种,它 ...

  9. "HK"日常之制作一只QQ刷屏

    刷屏器是什么?可以吃吗?如果可以吃它好吃吗? um. 刷屏器就是可以定时发生信息的东西 刷屏器可以应用于很多方面,例如别人不理你了或者在QQ斗图的时候.警告:本教程仅作为学习研究,禁止其他用途!--- ...

  10. XML文件的创建和解析笔记

    解析XML的四种方法 XML现在已经成为一种通用的数据交换格式,它的平台无关性,语言无关性,系统无关性,给数据集成与交互带来了极大的方便.对于XML本身的语法知识与技术细节,需要阅读相关的技术文献,这 ...