SQLite

https://blog.csdn.net/cangchen/article/details/44063597

https://blog.csdn.net/cangchen/article/list/6

       SQLite采用动态数据类型,当某个值插入到数据库时,SQLite将会检查它的类型,如果该类型与关联的列不匹配,SQLite则会尝试将该值转换成该列的类型,如果不能转换,则该值将作为本身的类型存储,SQLite称这为“弱类型”。但有一个特例,如果是INTEGER PRIMARY KEY,则其他类型不会被转换,会报一个“datatype missmatch”的错误。SQLite支持NULL、INTEGER、REAL、TEXT和BLOB数据类型,分别代表空值、整型值、浮点值、字符串文本、二进制对象。

Activity为我们提供了startManagingCursor(Cursor cursor)方法,它会根据Activity的生命周期去管理当前的Cursor对象。当Activity停止时他会自动调用Cursor的deactivate方法,禁用游标,当Activity重新回到屏幕时它会调用Cursor的requery方法再次查询,当Activity摧毁时,被管理的Cursor都会自动关闭释放。

SQLiteOpenHelper 类

  SQLiteOpenHelper是SQLiteDatabse的一个帮助类,用来管理数据的创建和版本更新。一般的用法是定义一个类继承SQLiteOpenHelper,并实现两个回调方法,OnCreate(SQLiteDatabase db)和onUpgrade(SQLiteDatabse, int oldVersion, int newVersion)来创建和更新数据库,

SQLiteOpenHelper 类中的 getWritableDatabase()getReadableDatabase()方法可以获得数据库的引用。

  为了实现对数据库版本进行管理,SQLiteOpenHelper 类提供了两个重要的方法,分别是 onCreate()和 onUpgrade(),前者用于初次使用软件时生成数据库表,后者用于升级软件时更新数据库表结构。

  当调用SQLiteOpenHelper的getWritableDatabase()或者getReadableDatabase()方法获取用于操作数据库的SQLiteDatabase实例的时候,如果数据库不存在,Android系统会自动生成一个数据库,接着调用onCreate()方法。

  onCreate()方法在初次生成数据库时才会被调用,在onCreate()方法里可以生成数据库表结构及添加一些应用使用到的初始化数据。

  onUpgrade()方法在数据库的版本发生变化时会被调用,一般在软件升级时才需改变版本号,而数据库的版本是由程序员控制的。

  假设数据库现在的版本是1,由于业务的变更,修改了数据库表结构,这时候就需要升级软件,升级软件时希望更新用户手机里的数据库表结构,为了实现这一目的,可以把原来的数据库版本设置为2,并且在onUpgrade()方法里面实现表结构的更新。

  当软件的版本升级次数比较多,这时在onUpgrade()方法里面可以根据原版号和目标版本号进行判断,然后做出相应的表结构及数据更新。

 

SQLiteDatabase类

  Android提供了一个名为 SQLiteDatabase的类(SQLiteOpenHelper 类中的 getWritableDatabase()getReadableDatabase()方法返回这个类的对象)。

  SQLiteDatabase类封装了一些操作数据库的API,使用该类可以完成对数据进行添加(Create)、查询(Retrieve)、更新(Update)和删除(Delete)操作(这些操作简称为CRUD)。

  SQLiteDatabase的学习,应该重点掌握execSQL()和rawQuery()方法。

  execSQL()方法可以执行insert、delete、update和CREATE TABLE之类有更改行为的SQL语句;

  rawQuery()方法用于执行select语句。

  android sqlite 升级数据库 修改表名, 增加字段,修改字段类型

注意:修改数据库后,一定要记得增加数据库版本号 +1 . 否则不会走onUpgrade方法.

最残暴的方法:

[html]  view plain  copy
  1. @Override    
  2. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    
  3.     db.execSQL("drop table if exists Book");   
  4.     onCreate(db);    
  5. }   

直接删表, 数据全都没了.在正常的产品项目中是不可能这么干的。

正确的处理方法以及各种情况的处理:

一,增加一个表

oncreate 增加一个创表语句, onupgrade 也要增加老版本升级创建语句逻辑

[html]  view plain  copy
  1. @Override    
  2. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    
  3.     db.execSQL("drop table if exists Book");    
  4.     db.execSQL("drop table if exists Category");    // 增加一个表,创建表语句.  
  5.     onCreate(db);    
  6. }   
[html]  view plain  copy
  1. @Override    
  2. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {    
  3.     switch (oldVersion) {    
  4.     case 1:    
  5.         db.execSQL(CREATE_CATEGORY);   // 判断版本号.如果是老版本, 走upgrade逻辑,创建新的表  
  6.     default:    
  7.     }    
  8. }   
二. 表中增加一个字段:

比如建表语句中增加了一个字段

[html]  view plain  copy
  1. public static final String CREATE_BOOK = "create table Book ("  
  2. + "id integer primary key autoincrement, "  
  3. + "author text, "  
  4. + "price real, "  
  5. + "pages integer, "  
  6. + "name text, "  
  7. + "<span style="color:#FF0000;"><strong>category_id integer</strong></span>)";  
[html]  view plain  copy
  1. @Override  
  2. public void onCreate(SQLiteDatabase db) {  
  3. db.execSQL(CREATE_BOOK);  
  4. db.execSQL(CREATE_CATEGORY);  
  5. }  
  6. @Override  
  7. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {  
  8. switch (oldVersion) {  
  9. case 1:  
  10. db.execSQL(CREATE_CATEGORY);  
  11. case 2:  
  12. db.execSQL("alter table Book add column category_id integer");  
  13. default:  
  14. }  
  15. }  

在upgrade 方法中增加一个case 2:   使用sqlite 语句,增加一个字段

alter table Book add column category_id integer

Book 是表名

category_id  是字段名称

integer 是字段类型

三 表中删除一个字段:

处理方法如 增加一个字段.

sqlite 语句:

alter table Book drop column category_id 

使用drop 语句删除字段

Book 为表名

category_id 为字段名

四.修改一个字段类型

注意:  alter table Book alter column category_id  text

这么做是错误的! SQLite 目前还不支持直接修改字段。

笨方法一.

增加一个字段 (这个新字段名可定和老的不一样),然后修改app用到此字段的代码.使用新的字段.


方法二.

-- 把原表改成另外一个名字作为暂存表 
ALTER TABLE old_table_name RENAME TO temp_table_name; 

-- 如果需要,可以删除原表的索引 
DROP INDEX ix_name; 

-- 用原表的名字创建新表 
CREATE TABLE old_table_name ( field_name INTEGER PRIMARY KEY AUTOINCREMENT ,  other_field_name text notnull); 

-- 如果需要,可以创建新表的索引 
CREATE INDEX ix_name ON old_table_name(field_name); 

-- 将暂存表数据写入到新表,很方便的是不需要去理会自动增长的 ID 
INSERT INTO old_table_name SELECT * FROM  temp_table_name 

-- 删除暂存表 
DROP TABLE temp_table_name;

程序实例

  首先是一个DatabaseHelper类,它继承了SQLiteOpenHelper,实现了onCreate和onUpgrade方法。

  虽然数据库的名字和版本都是在该类的构造函数中传入,但是数据库实际被创建是在该类的getWritableDatabase()或getReadableDatabase()方法第一次被调用时。

  第一次创建之后接着会调用onCreate方法(我们在这里创建了数据表),之后onCreate就不再被调用。

  DatabaseHelper类:

复制代码
package com.example.hellodatabases;

import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

//参考:http://blog.csdn.net/liuhe688/article/details/6715983

public class DatabaseHelper extends SQLiteOpenHelper// 继承SQLiteOpenHelper类
{

    // 数据库版本号
    private static final int DATABASE_VERSION = 1;
    // 数据库名
    private static final String DATABASE_NAME = "TestDB.db";

    // 数据表名,一个数据库中可以有多个表(虽然本例中只建立了一个表)
    public static final String TABLE_NAME = "PersonTable";

    // 构造函数,调用父类SQLiteOpenHelper的构造函数
    public DatabaseHelper(Context context, String name, CursorFactory factory,
            int version, DatabaseErrorHandler errorHandler)
    {
        super(context, name, factory, version, errorHandler);

    }

    public DatabaseHelper(Context context, String name, CursorFactory factory,
            int version)
    {
        super(context, name, factory, version);
        // SQLiteOpenHelper的构造函数参数:
        // context:上下文环境
        // name:数据库名字
        // factory:游标工厂(可选)
        // version:数据库模型版本号
    }

    public DatabaseHelper(Context context)
    {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);

        // 数据库实际被创建是在getWritableDatabase()或getReadableDatabase()方法调用时
        Log.d(AppConstants.LOG_TAG, "DatabaseHelper Constructor");
        // CursorFactory设置为null,使用系统默认的工厂类
    }

    // 继承SQLiteOpenHelper类,必须要覆写的三个方法:onCreate(),onUpgrade(),onOpen()
    @Override
    public void onCreate(SQLiteDatabase db)
    {
        // 调用时间:数据库第一次创建时onCreate()方法会被调用

        // onCreate方法有一个 SQLiteDatabase对象作为参数,根据需要对这个对象填充表和初始化数据
        // 这个方法中主要完成创建数据库后对数据库的操作

        Log.d(AppConstants.LOG_TAG, "DatabaseHelper onCreate");

        // 构建创建表的SQL语句(可以从SQLite Expert工具的DDL粘贴过来加进StringBuffer中)
        StringBuffer sBuffer = new StringBuffer();

        sBuffer.append("CREATE TABLE [" + TABLE_NAME + "] (");
        sBuffer.append("[_id] INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, ");
        sBuffer.append("[name] TEXT,");
        sBuffer.append("[age] INTEGER,");
        sBuffer.append("[info] TEXT)");

        // 执行创建表的SQL语句
        db.execSQL(sBuffer.toString());

        // 即便程序修改重新运行,只要数据库已经创建过,就不会再进入这个onCreate方法

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
    {
        // 调用时间:如果DATABASE_VERSION值被改为别的数,系统发现现有数据库版本不同,即会调用onUpgrade

        // onUpgrade方法的三个参数,一个 SQLiteDatabase对象,一个旧的版本号和一个新的版本号
        // 这样就可以把一个数据库从旧的模型转变到新的模型
        // 这个方法中主要完成更改数据库版本的操作

        Log.d(AppConstants.LOG_TAG, "DatabaseHelper onUpgrade");

        db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
        // 上述做法简单来说就是,通过检查常量值来决定如何,升级时删除旧表,然后调用onCreate来创建新表
        // 一般在实际项目中是不能这么做的,正确的做法是在更新数据表结构时,还要考虑用户存放于数据库中的数据不丢失

    }

    @Override
    public void onOpen(SQLiteDatabase db)
    {
        super.onOpen(db);
        // 每次打开数据库之后首先被执行

        Log.d(AppConstants.LOG_TAG, "DatabaseHelper onOpen");
    }

}
复制代码

  定义一个Person类作为测试数据:

复制代码
package com.example.hellodatabases;

public class Person
{
    public int _id;
    public String name;
    public int age;
    public String info;

    public Person()
    {
    }

    public Person(String name, int age, String info)
    {
        this.name = name;
        this.age = age;
        this.info = info;
    }

}
复制代码

  之后是一个管理类,持有数据库对象,对各种操作进行了封装:

复制代码
package com.example.hellodatabases;

import java.util.ArrayList;
import java.util.List;

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

//参考:http://blog.csdn.net/liuhe688/article/details/6715983
public class DBManager
{
    private DatabaseHelper helper;
    private SQLiteDatabase db;

    public DBManager(Context context)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> Constructor");
        helper = new DatabaseHelper(context);
        // 因为getWritableDatabase内部调用了mContext.openOrCreateDatabase(mName, 0,
        // mFactory);
        // 所以要确保context已初始化,我们可以把实例化DBManager的步骤放在Activity的onCreate里
        db = helper.getWritableDatabase();
    }

    /**
     * add persons
     * 
     * @param persons
     */
    public void add(List<Person> persons)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> add");
        // 采用事务处理,确保数据完整性
        db.beginTransaction(); // 开始事务
        try
        {
            for (Person person : persons)
            {
                db.execSQL("INSERT INTO " + DatabaseHelper.TABLE_NAME
                        + " VALUES(null, ?, ?, ?)", new Object[] { person.name,
                        person.age, person.info });
                // 带两个参数的execSQL()方法,采用占位符参数?,把参数值放在后面,顺序对应
                // 一个参数的execSQL()方法中,用户输入特殊字符时需要转义
                // 使用占位符有效区分了这种情况
            }
            db.setTransactionSuccessful(); // 设置事务成功完成
        }
        finally
        {
            db.endTransaction(); // 结束事务
        }
    }

    /**
     * update person's age
     * 
     * @param person
     */
    public void updateAge(Person person)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> updateAge");
        ContentValues cv = new ContentValues();
        cv.put("age", person.age);
        db.update(DatabaseHelper.TABLE_NAME, cv, "name = ?",
                new String[] { person.name });
    }

    /**
     * delete old person
     * 
     * @param person
     */
    public void deleteOldPerson(Person person)
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> deleteOldPerson");
        db.delete(DatabaseHelper.TABLE_NAME, "age >= ?",
                new String[] { String.valueOf(person.age) });
    }

    /**
     * query all persons, return list
     * 
     * @return List<Person>
     */
    public List<Person> query()
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> query");
        ArrayList<Person> persons = new ArrayList<Person>();
        Cursor c = queryTheCursor();
        while (c.moveToNext())
        {
            Person person = new Person();
            person._id = c.getInt(c.getColumnIndex("_id"));
            person.name = c.getString(c.getColumnIndex("name"));
            person.age = c.getInt(c.getColumnIndex("age"));
            person.info = c.getString(c.getColumnIndex("info"));
            persons.add(person);
        }
        c.close();
        return persons;
    }

    /**
     * query all persons, return cursor
     * 
     * @return Cursor
     */
    public Cursor queryTheCursor()
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> queryTheCursor");
        Cursor c = db.rawQuery("SELECT * FROM " + DatabaseHelper.TABLE_NAME,
                null);
        return c;
    }

    /**
     * close database
     */
    public void closeDB()
    {
        Log.d(AppConstants.LOG_TAG, "DBManager --> closeDB");
        // 释放数据库资源
        db.close();
    }

}
复制代码

  主要的Activity:

复制代码
package com.example.hellodatabases;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;

//参考:http://blog.csdn.net/liuhe688/article/details/6715983

public class HelloDBActivity extends Activity
{
    private DBManager dbManager;
    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_db);

        listView = (ListView) findViewById(R.id.listView);
        // 初始化DBManager
        dbManager = new DBManager(this);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.hello_db, menu);
        return true;
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        dbManager.closeDB();// 释放数据库资源
    }

    public void add(View view)
    {
        ArrayList<Person> persons = new ArrayList<Person>();

        Person person1 = new Person("Ella", 22, "lively girl");
        Person person2 = new Person("Jenny", 22, "beautiful girl");
        Person person3 = new Person("Jessica", 23, "sexy girl");
        Person person4 = new Person("Kelly", 23, "hot baby");
        Person person5 = new Person("Jane", 25, "a pretty woman");

        persons.add(person1);
        persons.add(person2);
        persons.add(person3);
        persons.add(person4);
        persons.add(person5);

        dbManager.add(persons);
    }

    public void update(View view)
    {
        // 把Jane的年龄改为30(注意更改的是数据库中的值,要查询才能刷新ListView中显示的结果)
        Person person = new Person();
        person.name = "Jane";
        person.age = 30;
        dbManager.updateAge(person);
    }

    public void delete(View view)
    {
        // 删除所有三十岁以上的人(此操作在update之后进行,Jane会被删除(因为她的年龄被改为30))
        // 同样是查询才能查看更改结果
        Person person = new Person();
        person.age = 30;
        dbManager.deleteOldPerson(person);
    }

    public void query(View view)
    {
        List<Person> persons = dbManager.query();
        ArrayList<Map<String, String>> list = new ArrayList<Map<String, String>>();
        for (Person person : persons)
        {
            HashMap<String, String> map = new HashMap<String, String>();
            map.put("name", person.name);
            map.put("info", person.age + " years old, " + person.info);
            list.add(map);
        }
        SimpleAdapter adapter = new SimpleAdapter(this, list,
                android.R.layout.simple_list_item_2, new String[] { "name",
                        "info" }, new int[] { android.R.id.text1,
                        android.R.id.text2 });
        listView.setAdapter(adapter);
    }

    @SuppressWarnings("deprecation")
    public void queryTheCursor(View view)
    {
        Cursor c = dbManager.queryTheCursor();
        startManagingCursor(c); // 托付给activity根据自己的生命周期去管理Cursor的生命周期
        CursorWrapper cursorWrapper = new CursorWrapper(c)
        {
            @Override
            public String getString(int columnIndex)
            {
                // 将简介前加上年龄
                if (getColumnName(columnIndex).equals("info"))
                {
                    int age = getInt(getColumnIndex("age"));
                    return age + " years old, " + super.getString(columnIndex);
                }
                return super.getString(columnIndex);
            }
        };
        // 确保查询结果中有"_id"列
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
                android.R.layout.simple_list_item_2, cursorWrapper,
                new String[] { "name", "info" }, new int[] {
                        android.R.id.text1, android.R.id.text2 });
        ListView listView = (ListView) findViewById(R.id.listView);
        listView.setAdapter(adapter);
    }

}
复制代码

 

  另,附上布局和其他:

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="add"
        android:text="add" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="update"
        android:text="update" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="delete"
        android:text="delete" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="query"
        android:text="query" />

    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="queryTheCursor"
        android:text="queryTheCursor" />

    <ListView
        android:id="@+id/listView"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />

</LinearLayout>
复制代码
复制代码
package com.example.hellodatabases;

public class AppConstants
{
    public static final String LOG_TAG="Hello DB";

}
复制代码

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值