数据存储——SQLite 数据库存储


存储大量复杂的关系型数据

数据库的增(insert)删(delete)改(update)查(select)


创建数据库

数据库文件存放在/data/data/<package name>/databases

使用SQLiteOpenHelper类对数据库进行创建和升级。

两个抽象方法,onCreate()和onUpgrade(),在自己的帮助类里面重写这两个方法,然后分别在这两个方法中去实现创建、升级数据库的逻辑

两个实例方法, getReadableDatabase() 和getWritableDatabase():
这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。区别是,当数据库不可写入的时候(如磁盘空间已满)getReadableDatabase()方法返回的对象将以只读的方式去打开数据库,而getWritableDatabase()方法则将出现异常。

两个构造方法,使用少参的那个:

Public MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
    super(context, name, factory, version);
}
context:上下文,必须有才能对数据库进行操作
name:数据库的名称
factory:数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null。
version:当前数据库的版本号, 可用于对数据库进行升级操作。

建表语句:    //integer 表示整型,real 表示浮点型,text 表示文本类型,blob 表示二进制类型

create table Book (id integer primary key autoincrement, author text, price real, pages integer, name text)

create table Book (
    id integer primary key autoincrement, //id(主键),使用primary key 将id 列设为主键,autoincrement关键字表示id列是自增长的
    author text,                                              //作者
    price real,                                                //价格
    pages integer,                                         //页数
    name text)                                               //书名


例子A:

布局:一个Button 创建数据库


1. 创建MyDatabaseHelper类

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table book("    //建表语句
            + "id integer primary key autoincrement,"
            + "author text,"
            + "price real,"
            + "pages integer,"
            + "name text)";
    private Context context;

    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {    //构造方法
        super(context, name, factory, version);
        this.context = context;
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {    //当数据库存在了就不会再执行
        sqLiteDatabase.execSQL(CREATE_BOOK);    //执行建表语句
        Toast.makeText(context, "创建成功", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

2. MainActivity

public class MainActivity extends AppCompatActivity {
    MyDatabaseHelper dbHelper;    

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
        Button btn_create = (Button) findViewById(R.id.btn_create);
        btn_create.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                dbHelper.getWritableDatabase();    //创建数据库
            }
        });
    }
}

升级数据库

要求:添加一个Category 表用于记录书籍的分类

create table Category (
    id integer primary key autoincrement,
    category_name text,
    category_code integer)

接着例子A,作为例子B

MyDatabaseHelper的

onCreate()方法下添加:

public void onCreate(SQLiteDatabase sqLiteDatabase) {
    sqLiteDatabase.execSQL(CREATE_BOOK);
    sqLiteDatabase.execSQL(CREATE_CATEGORY);
    Toast.makeText(context, "创建成功", Toast.LENGTH_SHORT).show();   //只有修改了MainActivity中的版本号才会重新创建成功
}

onUpgrade()方法下:

public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    sqLiteDatabase.execSQL("drop table if exists Book");      //如果存在Book表,则废掉该表 //如果不废除Book表,那么在创建表时发现这张表已经存在了,就会报错。
    sqLiteDatabase.execSQL("drop table if exists Category");  //如果存在Category表,则废掉该表
    onCreate(sqLiteDatabase);    //创建表
}

在MainActivity中
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);    //此处1变为2,即升级版本号(大于1均可)

添加数据

调用SQLiteOpenHelper 的getReadableDatabase()或getWritableDatabase()方法可以用于创建和升级数据库。同时这两个方法还都会返回一个SQLiteDatabase
对象,借助这个对象可以对数据进行增删改查操作。

insert(String table, String nullColumnHack, ContentValues values);
table:表名
nullColumnHack:如果给有些项没赋值,就自动赋值NULL(一般都为null)
values:ContentValues 对象,它提供了一系列的通过put()方法重载,向ContentValues 中添加数据

接着例子A,作为例子C

添加一个Button "添加数据"

Button btn_add = (Button) findViewById(R.id.btn_add);
btn_add.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db = dbHelper.getWritableDatabase();    //创建数据库
        ContentValues values = new ContentValues();    //创建数据内容对象
        values.put("name", "红楼梦");    //put()方法写入数据
        values.put("author", "曹雪芹");
        values.put("pages", 800);
        values.put("price", 59.99);
        db.insert("Book", null, values);    //insert()方法向数据库添加数据
        values.clear();
        values.put("name", "三国演义");
        values.put("author", "罗贯中");
        values.put("pages", 600);
        values.put("price", 49.99);
        db.insert("Book", null, values);
    }
});
Tip:同一个数据库做操作需要修改版本号


更新数据

update(String table, ContentValues values, String whereClause, String[] whereArgs);
table:表名
values:ContentValues 对象,它提供了一系列的通过put()方法重载,把更新的数据在这里组装进去
第三、第四个参数用于去约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行。

接着例子C

添加一个Button "更新数据"    

Button btn_update = (Button) findViewById(R.id.btn_update);
btn_update.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db2 = dbHelper.getWritableDatabase();
        ContentValues values = new ContentValues();
        values.put("price", 44.99);
        db2.update("Book", values, "name = ?", new String[]{"三国演义"});    //第三个参数表示修改name列,第四个参数表示name的值
    }
});

(显示的BUG,为什么修改之后数据库就空了)


删除数据

delete(String table, String whereClause, String[] whereArgs);    //参数说明如上

接着例子C
添加一个Button "删除数据" 

Button btn_delete = (Button) findViewById(R.id.btn_delete);
btn_delete.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
         SQLiteDatabase db3 = dbHelper.getWritableDatabase();
        db3.delete("Book", "pages > ?", new String[]{"750"});    //删除pages>750的数据
    }
});

查询数据

query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy);


query()方法的参数说明
query()方法参数对应SQL部分描述
tablefrom table_name指定查询的表名
columnsselect column1, column2指定查询的列名
selectionwhere column = value指定where 的约束条件
selectionArgs-为where 中的占位符提供具体的值
groupBygroup by column指定需要group by 的列
havinghaving column = value对group by 后的结果进一步约束
orderByorder by column1, column2指定查询结果的排序方式











调用query()方法后会返回一个Cursor 对象,查询到的所有数据都将从这个对象中取出。

接着例子C

Button btn_delete = (Button) findViewById(R.id.btn_delete);
btn_delete.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        SQLiteDatabase db4 = dbHelper.getWritableDatabase();
        Cursor cursor = db4.query("Book", null, null, null, null, null, null);    //调用query方法获取Cursor对象
        if (cursor.moveToFirst()) {
            do {
                String name = cursor.getString(cursor.getColumnIndex("name"));
                String author = cursor.getString(cursor.getColumnIndex("author"));
                int pages = cursor.getInt(cursor.getColumnIndex("pages"));
                double price = cursor.getDouble(cursor.getColumnIndex("price"));
                tv.setText(name + "   " + author + "   " + pages + "   " + price);    //添加一个显示数据的TextView
            } while (cursor.moveToNext());
        }
        cursor.close();
    }
});

使用SQL操作数据库

添加数据:

db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)", new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });

更新数据:

db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });

删除数据:

db.execSQL("delete from Book where pages > ?", new String[] { "500" });

查询数据:

db.rawQuery("select * from Book", null);

使用事务

SQLite 数据库是支持事务的,事务的特性可以保证让某一系列的操作要么全部完成,要么一个都不会完成。例如完全更新数据库的数据,先删除数据,再添加数据,两个操作必须都完成或者都不完成。


接着例子C:

添加一个Button "废弃旧表创建新表" 

Button btn_delete = (Button) findViewById(R.id.btn_delete);
    btn_delete.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            SQLiteDatabase db5 = dbHelper.getWritableDatabase();
            db5.beginTransaction(); //开启事物
            try {
                db5.delete("Book", null, null);
                // if (true) {    //注释之后则会执行事务
                //     throw new NullPointerException();   //手动抛出异常,让事务失败
                // }
                //我们在删除旧数据的操作完成后手动抛出了一个NullPointerException,这样添加新数据的代码就执行不到了。不过由于事务的存在,中途出现异常会导致事务的失败,此时旧数据应该是删除不掉的。
                ContentValues values3 = new ContentValues();
                values3.put("name", "诗经");
                values3.put("author", "未知");
                values3.put("pages", 400);
                values3.put("price", 38.99);
                db5.insert("Book", null, values3);
                db5.setTransactionSuccessful(); //事务执行成功
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                db5.endTransaction();   //结束事务
            }
        }
    });

此为事务Transaction的标准用法↑

1. 调用SQLiteDatabase 的beginTransaction()方法来开启一个事务;

2. 在一个异常捕获的代码块中去执行具体的数据库操作;

3. 调用setTransactionSuccessful()表示事务已经执行成功;

4. 在finally代码块中调用endTransaction()来结束事务;


升级数据库的最佳写法

用onUpgrade()方法升级是删除了当前所有表,再重新创建表。此处要做的是在升级数据库的时候让数据不会丢失

在onUpgrade()方法中使用switch(oldVersion) - case 语句,都没有break

例子说明:

MyDatabaseHelper.java

初始:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    public static final String CREATE_BOOK = "create table book("
            + "id integer primary key autoincrement,"
            + "author text,"
            + "price real,"
            + "pages integer,"
            + "name text)";
public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL(CREATE_BOOK);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
    }
}

要求1:升级数据库,向数据库 添加一张Category表

则需要添加的代码:

public static final String CREATE_CATEGORY = "create table Category ("    //Category表
        + "id integer primary key autoincrement,"
        + "category_name text,"
        + "category_code integer)";


onCreate()方法中添加:
sqLiteDatabase.execSQL(CREATE_CATEGORY);    //创建CATEGORY表


onUpgrade()方法中添加:
switch (oldVersion) {
    case 1:
        sqLiteDatabase.execSQL(CREATE_CATEGORY);
    default:
}


如果用户当前数据库的版本号是1,就只会创建一张Category 表。这样当用户是直接安装的第二版的程序时,就会将两张表一起创建。而当用户是使用第二版的程序覆盖安装第一版的程序时,就会进入到升级数据库的操作中,此时由于Book 表已经存在了,因此只需要创建一张Category 表即可。


要求2(继承要求1):给Book 表和Category 表之间建立关联,需要在Book 表中添加一个category_id 的字段

修改CREATE_BOOK,加入category_id字段

public static final String CREATE_BOOK = "create table book("
        + "id integer primary key autoincrement,"
        + "author text,"
        + "price real,"
        + "pages integer,"
        + "name text"
        + "category_id integer)";

修改onUpgrade()方法:

switch (oldVersion) {
    case 1:
        sqLiteDatabase.execSQL(CREATE_CATEGORY);
    case 2:
        sqLiteDatabase.execSQL("alter table Book add column category_id integer");    //当版本为2的时候,执行alter命令来为Book表新增category_id列
    default:
}


都没有break,是为了保证在跨版本升级的时候,每一次的数据库修改都能被全部执行到。比如用户当前是从第二版程序升级到第三版程序的,那么case 2 中的逻辑就会执行。而如果用户是直接从第一版程序升级到第三版程序的,那么case 1 和case 2 中的逻辑都会执行。使用这种方式来维护数据库的升级,不管版本怎样更新,都可以保证数据库的表结构是最新的,而且表中的数据也完全不会丢失了。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值