本文主要是介绍Android原生SQLite操作以及greenDao框架操作SQLite,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
本文分为2个大的方面来讲SQLite操作:原生操作,框架操作
原生操作
首先我们要创建一个数据库打开帮助类 ,目的是创建数据库和表,升级数据库
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;public class DBOpenHelper extends SQLiteOpenHelper {private static final int VERSION = 1;// 定义数据库版本号private static final String DBNAME = "goods.db";// 定义数据库名public DBOpenHelper(Context context){// 定义构造函数super(context, DBNAME, null, VERSION);// 重写基类的构造函数//参数 上下文 数据库名称 cosor工厂 版本号}@Overridepublic void onCreate(SQLiteDatabase db){// 创建数据库db.execSQL("create table tb_outgoods (id varchar(10) primary key,name varchar(200),money varchar(10),many varchar(10),time varchar(10),"+ "type varchar(10),handler varchar(10),mark varchar(200))");// 创建支出信息表db.execSQL("create table tb_ingoods (id varchar(10) primary key,name varchar(200),money varchar(10),many varchar(10),time varchar(10),"+"type varchar(10),handler varchar(10),mark varchar(200))");// 创建收入信息表db.execSQL("create table tb_pwd (account varchar(20) primary key,password varchar(20))");// 创建密码表db.execSQL("create table tb_flag (id varchar(10) primary key,name varchar(20),time varchar(10),flag varchar(200))");// 创建便签信息表}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)// 覆写基类的onUpgrade方法,以便数据库版本更新{//本方法主要用于更新数据库 通过对当前版本的判断 实现数据库的更新switch (oldVersion) { case 1://创建新表,注意createTable()是静态方法// 加入新字段// db.execSQL("ALTER TABLE 'moments' ADD 'audio_path' TEXT;");// TODObreak;}}
}
上面的代码就是创建数据库了,以及数据库的升级
但是光有这个还不够 我们还不能对数据库进行增删改查等操作,所以还需要一个数据库工具类
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.yq.accountsoft.model.Tb_ingoods;
import java.util.ArrayList;
import java.util.List;
/*** Created by Administrator on 2015/12/9.*/
public class IngoodsDAO {private DBOpenHelper helper;// 创建DBOpenHelper对象private SQLiteDatabase db;// 创建SQLiteDatabase对象public IngoodsDAO(Context context)// 定义构造函数{helper = new DBOpenHelper(context);// 初始化DBOpenHelper对象}public void add(Tb_ingoods tb_ingoods) {db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象// 执行添加收入信息操作db.execSQL("insert into tb_ingoods (id,name,money,many,time,type,handler,mark) values (?,?,?,?,?,?,?,?)",new Object[] {tb_ingoods.getId(),tb_ingoods.getName(),tb_ingoods.getMoney(),tb_ingoods.getMany(),tb_ingoods.getTime(),tb_ingoods.getType(),tb_ingoods.getHandler(),tb_ingoods.getMark(),});}public void update(Tb_ingoods tb_ingoods) {db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象// 执行修改收入信息操作db.execSQL("update tb_ingoods set name=?,money = ?,many=?,time = ?,type = ?,handler = ?,mark = ? where id= ?",new Object[] {tb_ingoods.getName(),tb_ingoods.getMoney(),tb_ingoods.getMany(), tb_ingoods.getTime(),tb_ingoods.getType(), tb_ingoods.getHandler(),tb_ingoods.getMark(),tb_ingoods.getId()});}public Tb_ingoods find(int id) {db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象Cursor cursor = db.rawQuery("select id,name,money,many,time,type,handler,mark from tb_ingoods where id= ?",new String[] { String.valueOf(id) });// 根据编号查找收入信息,并存储到Cursor类中if (cursor.moveToNext())// 遍历查找到的收入信息{// 将遍历到的收入信息存储到Tb_inaccount类中return new Tb_ingoods(cursor.getString(cursor.getColumnIndex("id")),cursor.getString(cursor.getColumnIndex("name")),cursor.getString(cursor.getColumnIndex("money")),cursor.getString(cursor.getColumnIndex("many")),cursor.getString(cursor.getColumnIndex("time")),cursor.getString(cursor.getColumnIndex("type")),cursor.getString(cursor.getColumnIndex("handler")),cursor.getString(cursor.getColumnIndex("mark"))//cursor.getDouble(cursor.getColumnIndex("amount")));}return null;// 如果没有信息,则返回null}/*** 刪除收入信息** @param ids*/public void detele(Integer... ids) {if (ids.length > 0)// 判断是否存在要删除的id{StringBuffer sb = new StringBuffer();// 创建StringBuffer对象for (int i = 0; i < ids.length; i++)// 遍历要删除的id集合{sb.append('?').append(',');// 将删除条件添加到StringBuffer对象中}sb.deleteCharAt(sb.length() - 1);// 去掉最后一个“,“字符db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象// 执行删除收入信息操作db.execSQL("delete from tb_ingoods where id in (" + sb + ")",(Object[]) ids);}}/*** 获取收入信息** @param start* 起始位置* @param count* 每页显示数量* @return*/public List<Tb_ingoods> getScrollData(int start, int count) {List<Tb_ingoods> tb_ingoods = new ArrayList<Tb_ingoods>();// 创建集合对象db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象// 获取所有收入信息Cursor cursor = db.rawQuery("select * from tb_ingoods limit ?,?",new String[] { String.valueOf(start), String.valueOf(count) });while (cursor.moveToNext())// 遍历所有的收入信息{// 将遍历到的收入信息添加到集合中tb_ingoods.add(new Tb_ingoods(cursor.getString(cursor.getColumnIndex("id")),cursor.getString(cursor.getColumnIndex("name")),cursor.getString(cursor.getColumnIndex("money")),cursor.getString(cursor.getColumnIndex("many")),cursor.getString(cursor.getColumnIndex("time")),cursor.getString(cursor.getColumnIndex("type")),cursor.getString(cursor.getColumnIndex("handler")),cursor.getString(cursor.getColumnIndex("mark"))));}return tb_ingoods;// 返回集合}/*** 获取总记录数** @return*/public long getCount() {db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象Cursor cursor = db.rawQuery("select count(id) from tb_ingoods", null);// 获取收入信息的记录数if (cursor.moveToNext())// 判断Cursor中是否有数据{return cursor.getLong(0);// 返回总记录数}return 0;// 如果没有数据,则返回0}/*** 获取收入最大编号** @return*/public int getMaxId() {db = helper.getWritableDatabase();// 初始化SQLiteDatabase对象Cursor cursor = db.rawQuery("select max(id) from tb_ingoods", null);// 获取收入信息表中的最大编号while (cursor.moveToLast()) {// 访问Cursor中的最后一条数据return cursor.getInt(0);// 获取访问到的数据,即最大编号}return 0;// 如果没有数据,则返回0}
}
上面就是其中一个数据库工具类,在一个项目中肯定不止一个
接下来的工作就是在activity或者service里面去使用工具类(当然操作数据库比较费时,建议放进新线程里面去操作比较好)
原生的数据库操作大概就是这样了,说明一点 在创建数据库帮助类里面 SQL语句不规范,严格来说 语句应该为大写
用这种方式操作数据库,要自己写语句 操作类 下面来看看使用数据库框架来操作,目前开源的数据库框架有ORMlite greenDao 等 前一种是基于注释的框架,后一种是数Java语法 这里主要介绍后一种并且IDE是AS
GreenDao框架
GREENDAO 设计的主要目标
一个精简的库性能最大化内存开销最小化易于使用的 APIs对 Android 进行高度优化
GREENDAO 设计的主要特点
greenDAO 性能远远高于同类的 ORMLite,具体测试结果可见官网greenDAO 支持 protocol buffer(protobuf) 协议数据的直接存储,如果你通过 protobuf 协议与服务器交互,将不需要任何的映射。与 ORMLite 等使用注解方式的 ORM 框架不同,greenDAO 使用「Code generation」的方式,这也是其性能能大幅提升的原因。
这是其核心概念:为了在我们的 Android 工程中使用 greenDAO ,我们需要另建一个纯 Java Project,用于自动生成后继 Android 工程中需要使用到的 Bean、DAO、DaoMaster、DaoSession 等类。
在AndoridStudio中引入GreenDAO
开始
GreenDAO整个运行的逻辑是通过配置其提供的JavaSE代码,自动在一个文件夹下生成需要Bean、DAO、DaoMaster、DaoSession;然后在Android代码中通过自动生成的DaoSession来操作数据库,具体方法如下:
在./src/main目录下创建一个与java同层级的java-gen文件夹.(需要切换到project模式下选中main右键new >directory)
打开build.gradle,引入sourceSets
sourceSets {main {java.srcDirs = ['src/main/java', 'src/main/java-gen']}
}
注意该脚本语句块需要放到android{…………上面的脚本……..}
这句话的意思是把java-gen文件夹下的java文件也归入srcDir中,主要是因为GreenDAO的逻辑是通过其提供的一套Java SE代码配置后自动在java-gen文件夹下生成对应的*DAO.java、DaoMaster.java、DaoSession.java文件。在Android代码中通过调用这几个类来操作数据库。
添加依赖
dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])testCompile 'junit:junit:4.12'compile 'com.android.support:appcompat-v7:24.0.0'compile 'org.greenrobot:greendao:3.0.1'
}
2 下载实例工程(Java工程,用来自动生成文件)
下载地址https://github.com/SureCase/GreenDaoForAndroidStudio
下载这个Demo的原因是要引入这个示例工程来实现自动创建需要的DaoMaster、DaoSession等等,GreenDao上手麻烦的原因就在于一开始以为只要gradle随便引入一个库之后就可以使用了,没想到最关键的是需要引入这个JavaSE工程来实现关键代码的自动创建。
这个Demo工程也不用怎么仔细的去看,下载解压之后Copy里面的MyDaoGenerator文件夹
把这个文件夹拷贝到你的工程跟目录中,就是与app文件夹同层级的目录里。`
将MyDaoGenerator include到工程中:
打开settings.gradle,修改内容
include ':app', ':MyDaoGenerator'
打开MyDaoGenerator的build.gradle文件,配置outputDir,路径就是存放自动生成DaoMaster和其他代码的地方。这里给个示例:
project(':MyDaoGenerator') {apply plugin: 'application'apply plugin: 'java'// edit output direction这里需要配置Java-gen地址 如果不清楚可以在资源管理器找到项目的目录,不建议直接写绝对目录def outputDir = "../MyGreenDao/app/src/main/java-gen"mainClassName = "pl.surecase.eu.MyDaoGenerator"dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])compile('de.greenrobot:DaoGenerator:1.3.0')}task createDocs {def docs = file(outputDir)docs.mkdirs()}run {args outputDir}
}
注意’def‘,GitHub上的这个Demo掉了’def‘导致我一直编译错误。
3.如果一切顺利,这个时候就完整的配置好了整个GreenDAO环境
接下来需要创建DaoMaster.java等
打开MyDaoGenerator工程中的MyDaoGenerator.java
配置里面的代码,如示例:
public class MyDaoGenerator {public static void main(String args[]) throws Exception {// 1: 数据库版本号// com.xxx.bean:自动生成的Bean对象会放到/java-gen/com/xxx/bean中Schema schema = new Schema(1, "com.vivo.bean");// DaoMaster.java、DaoSession.java、BeanDao.java会放到/java-gen/com/xxx/dao中schema.setDefaultJavaPackageDao("com.vivo.dao");initUserBean(schema);//初始化bean// // 最后我们将使用 DAOGenerator 类的 generateAll() 方法自动生成代码new DaoGenerator().generateAll(schema,"E:\\Android workplace\\MyGreenDAO\\app\\src\\main\\java-gen");//在下面的args参数处写上输出目录,可以在builder.gfade中配置页可以直接写,我这里直接写的绝对目录//new DaoGenerator().generateAll(schema, args[0]);//自动创建}private static void initUserBean(Schema schema) {// 一个实体(类)就关联到数据库中的一张表,此处表名为「Note」(既类名)Entity note = schema.addEntity("Note");// 你也可以重新给表命名// note.setTableName("NODE");// greenDAO 会自动根据实体类的属性值来创建表字段,并赋予默认值// 接下来你便可以设置表中的字段,还可以为字段设置主键,是否为空note.addIdProperty();note.addStringProperty("text").notNull();// 与在 Java 中使用驼峰命名法不同,默认数据库中的命名是使用大写和下划线来分割单词的。// For example, a property called “creationDate” will become a database column “CREATION_DATE”.note.addStringProperty("comment");note.addDateProperty("date");}
}
这里只需要关注一个问题,就是如果数据库要升级,这里是修改代码后全量重新创建。
假设要给user表增加一个”age”属性,并且增加school表的做法是:
把数据库版本号修改为2在initUserBean方法中加入userBean.addStringProperty("age");新建initSchoolBean方法,模仿上面进行设置
下面我们来运行这个Java文件,直接选中 右键 run
执行完成之后就会在java-gen文件夹下生成需要的代码了。DaoMaster、DaoSession、NoteDao、Note(依据你设置的表名)一共四个
如果没有生成,请检查是不是哪里配置出问题了
下面介绍一下这几个类的作用:
DaoMaster 数据库操作类的管理类,从这个类可以依次得到DaoSession,NoteDao,
DaoSession 数据库连接类
NoteDao note表格操作类,如果创建多个表格会对应多个这样的类,在这里面包含许多数据表的增删改查 入口
下面我们来创建一个数据库帮助类 主要在原来OpenHelper(DaoMaster的内部类)进行升级
public class DevOpenHelper extends DaoMaster.OpenHelper {//重写DaoMaster.OpenHelper这个抽象类 实现数据库升级的操作public DevOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory) {super(context, name, factory);}@Override//i代表旧的版本 i1代表新的版本public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {switch (i) {case 1://创建新表,注意createTable()是静态方法// SchoolDao.createTable(db, true);// 加入新字段// db.execSQL("ALTER TABLE 'moments' ADD 'audio_path' TEXT;");// TODObreak;}}
}
我贴一下OpenHelper类的代码
public static abstract class OpenHelper extends SQLiteOpenHelper {public OpenHelper(Context context, String name, CursorFactory factory) {super(context, name, factory, SCHEMA_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);createAllTables(db, false);}}
熟悉吧 这个类是继承自SQLiteOpenHelper,我们用原生数据库操作是也是继承自这个类,但是这个框架里没有写版本升级的方法(设置成抽象类,没有全部实现父类方法)
下面看一下完整的
public class DaoMaster extends AbstractDaoMaster {public static final int SCHEMA_VERSION = 1;/** Creates underlying database table using DAOs. */public static void createAllTables(SQLiteDatabase db, boolean ifNotExists) {NoteDao.createTable(db, ifNotExists);}/** Drops underlying database table using DAOs. */public static void dropAllTables(SQLiteDatabase db, boolean ifExists) {NoteDao.dropTable(db, ifExists);}public static abstract class OpenHelper extends SQLiteOpenHelper {public OpenHelper(Context context, String name, CursorFactory factory) {super(context, name, factory, SCHEMA_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {Log.i("greenDAO", "Creating tables for schema version " + SCHEMA_VERSION);createAllTables(db, false);}}/** WARNING: Drops all table on Upgrade! Use only during development. */public static class DevOpenHelper extends OpenHelper {public DevOpenHelper(Context context, String name, CursorFactory factory) {super(context, name, factory);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {Log.i("greenDAO", "Upgrading schema from version " + oldVersion + " to " + newVersion + " by dropping all tables");dropAllTables(db, true);onCreate(db);}}public DaoMaster(SQLiteDatabase db) {super(db, SCHEMA_VERSION);registerDaoClass(NoteDao.class);}public DaoSession newSession() {return new DaoSession(db, IdentityScopeType.Session, daoConfigMap);}public DaoSession newSession(IdentityScopeType type) {return new DaoSession(db, type, daoConfigMap);}
}
可以看到DaoMaster的构造方法是 SQLiteDatabase db
为了更好的操作数据表 我们扩展了NoteDao (这里并没有继承NoteDao) ,在扩展之前我们定义一个接口 用来规范 扩展类的包含的方法(接口经常用来规范类方法,另外监听器也是一个接口,接口的功能还有很多)
public interface DaoHelperInterface {public <T> void addData(T t);public void deleteData(String id);public <T> T getDataById(String id);public List getAllData();public boolean hasKey(String id);public long getTotalCount();public void deleteAll();
}
可以看到这是定义的接口,包含了添加,删除,得到数据等,可以自己新增或者修改,最重要的是该接口实现了泛型(泛型的好处就在于可以接受的数据类型广,object就行)
下面我们来实现这个接口
public class NoteDaoHelper implements DaoHelperInterface {private NoteDao noteDao;private DaoMaster daoMaster;private DaoSession daoSession;
//构造函数 参数是SQLiteDatabase 需要从OpenHelper(在该类的构造方法需要设置数据库名,版本,)得到一个可读写的数据源public NoteDaoHelper(SQLiteDatabase db) {if (db != null) {daoMaster = new DaoMaster(db);daoSession = daoMaster.newSession();noteDao = daoSession.getNoteDao();}}@Overridepublic <T> void addData(T t) {if (noteDao != null && t != null)noteDao.insertOrReplace((Note) t);}public NoteDao getNoteDao() {return noteDao;}@Overridepublic void deleteData(String id) {if (noteDao != null && !TextUtils.isEmpty(id))noteDao.deleteByKey(Long.valueOf(id));}@Overridepublic <T> T getDataById(String id) {if (noteDao != null && !TextUtils.isEmpty(id))return (T) noteDao.load(Long.valueOf(id));return null;}@Overridepublic List getAllData() {if (noteDao != null)return noteDao.loadAll();return null;}@Overridepublic boolean hasKey(String id) {if (noteDao != null || TextUtils.isEmpty(id))return false;QueryBuilder<Note> qb = noteDao.queryBuilder();qb.where(NoteDao.Properties.Id.eq(id));long count = qb.buildCount().count();return count > 0 ? true : false;}@Overridepublic long getTotalCount() {if (noteDao == null)return 0;QueryBuilder<Note> qb = noteDao.queryBuilder();return qb.buildCount().count();}@Overridepublic void deleteAll() {if (noteDao != null)noteDao.deleteAll();}
}
接下来就是在activity里使用了,这里参考了一个项目主布局文件如下,就是一个文本输入框(添加的类容,以及搜索ID用),一个添加按钮,一个搜索按钮,还有一个用来显示添加类容的listview
<?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"><LinearLayout
android:id="@+id/linearLayout1"android:layout_width="fill_parent"android:layout_height="wrap_content"android:orientation="horizontal"><EditText
android:id="@+id/editTextNote"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"android:hint="Enter new note"android:inputType="text"></EditText><Button
android:id="@+id/buttonAdd"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onMyButtonClick"android:text="Add"></Button><Button
android:id="@+id/buttonSearch"android:layout_width="wrap_content"android:layout_height="wrap_content"android:onClick="onMyButtonClick"android:text="Search"></Button></LinearLayout><ListView
android:id="@android:id/list"android:layout_width="fill_parent"android:layout_height="wrap_content"></ListView>
</LinearLayout>
activity代码
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.Toast;
import com.vivo.bean.Note;
import com.vivo.dao.DevOpenHelper;
import com.vivo.dao.NoteDao;
import com.vivo.dao.NoteDaoHelper;
import java.text.DateFormat;
import java.util.Date;public class MainActivity extends AppCompatActivity implements View.OnClickListener {private SQLiteDatabase db;private EditText editText;Button add;Button search;ListView list;private NoteDao noteDao;private Cursor cursor;NoteDaoHelper daoHelper;
int i=0;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);bindView();setupDatabase();//从数据库操作类的属性集合里(Properties是NoteDap的一个静态内部类,包含表的属性集合)Text就是表的一个字段,这里获取了列的名称String textColumn = NoteDao.Properties.Text.columnName;//用来排序 String orderBy = textColumn + " COLLATE LOCALIZED ASC";//从数据源中查询数据并返回一个游标(游标就好比一个顺序写满数据的尺子)query是个多态方法 接受的参数类型多,具体可以查看源码//这里使用的 参数分别为 数据表名(查什么表就写什么表名),所有的数据列 后面就是一些 筛选条件如排序,分组,where等cursor = db.query(noteDao.getTablename(), noteDao.getAllColumns(), null, null, null, null,null);//from数据表 列文本 表属性等...String[] from = {textColumn, NoteDao.Properties.Comment.columnName};//下面的我目前也没看懂 应该是android自带R文件的文本值int[] to = {android.R.id.text1, android.R.id.text2};//下面是新建简单游标适配器 (已经过时,这里不管了)SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, cursor, from, to);list.setAdapter(adapter);list.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {@Overridepublic boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {//这里的删除ID其实有问题,不应该用item的位置来删除,因为数据表的ID字段不一定是顺序排列的,这里只是示范一下daoHelper.deleteData(String.valueOf(i));cursor.requery();//重新查询 相当于更新Toast.makeText(getApplicationContext(), "delete success", Toast.LENGTH_SHORT).show();return false;}});}private void bindView() {editText = (EditText) findViewById(R.id.editTextNote);add = (Button) findViewById(R.id.buttonAdd);search = (Button) findViewById(R.id.buttonSearch);list = (ListView) findViewById(R.id.list);add.setOnClickListener(this);search.setOnClickListener(this);}public void setupDatabase() {//在我们扩展的数据库打开类传入上下文 数据库名,CursorFactory游标工厂 (一般null就行)//如果要更改数据库版本 字段信息需要到我们的JavaSE文件中去修改DevOpenHelper helper = new DevOpenHelper(getApplicationContext(), "my-db", null);db = helper.getWritableDatabase();//得到一个可读写的数据源daoHelper = new NoteDaoHelper(db);noteDao=daoHelper.getNoteDao();//其实NoteDao在这里主要是用来读写数据库表里的字段信息}private void search() {String str = editText.getText().toString();editText.setText("");if (str == null || str.equals(""))return;Note note = daoHelper.getDataById(str);Toast.makeText(getApplicationContext(), note.getText(), Toast.LENGTH_SHORT).show();cursor.requery();}private void addNote() {String str = editText.getText().toString();editText.setText("");//下面是获取时间 后面的参数是 date的style time的styleDateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);String comment = "Add on " + dateFormat.format(new Date());if (str == null || str.equals(""))return;Note note = new Note((long) i, str, comment, new Date());daoHelper.addData(note);cursor.requery();i++;}@Overridepublic void onClick(View view) {switch (view.getId()) {case R.id.buttonAdd:addNote();break;case R.id.buttonSearch:search();break;default:break;}}
}
在写这篇文章之前,我给自己留了一个问题,那就是数据库的升级该如何操作呢?下面我就来说一说数据库升级(这里讲的是原生的数据库升级,至于使用框架进行数据库升级,还有待研究)
数据库升级
我们都知道在软件版本迭代中,数据库的版本也需要升级(当然不是每次软件升级数据库都需要变动),既然数据库版本改变,那么数据库表格结构肯定有了变化,或者数据数据表的数据需要进行修改,添加等 如果数据库表的结构或者内容都没有改变,那么我们就没有升级数据库的必要(也就是我们就没有必要进行数据库版本号的改变)
数据库升级的原则
1 数据库的原有数据不能丢失
2 要兼容所有用户的APP(就是说要考虑到用户的使用情况)
3 旧版本(数据库版本)升级要尽量顺序升级(也就是说要让距离新版本比较远的用户要经历后续版本的每一次升级,而不是让用户一步到位升级到最新版本)
4 数据库升级或者创建 语句逻辑要清晰,代码要简单易懂,容易后期维护
数据库升级如何触发以及运行流程
当一个数据库创建完毕会将当前数据库的版本号保存在数据库结构中,当我们需要使用数据库时第一步执行的操作,不是创建也不是升级(也就是说第一次不是执行onCreate()或者onUpdate())而是读取数据库管理系统DDMS保存的数据库版本号,这里就会有两种情况:APP是第一次安装,那么读取出来的数据库版本号就是null;如果之前已经安装过APP,那么就会有数据库版本号
既然得到了数据库的版本号,那么数据库的创建和升级是如何进行操作的呢
我们都知道在数据库帮助类SQLiteOpenHelper中提供了3种方法,分别是
1 构造方法 public DataBaseOpenHelper(Context context,String name,CursorFactory factory,int version) {super(context, name, factory, version);}因为我们使用数据库类的时候都会去继承系统给我们提供的数据库打开类,在类加载的时候会执行类的构造方法,构造器的参数是:上下文,数据库名称,游标,这一次的数据库版本这构造方法里,我将我们这里传进来的参数交给父类构造器,去保存以便在需要的时候执行
2 public void onCreate(SQLiteDatabase db) {
这个方法是用来创建数据库表,当从数据库管理系统中获取了数据库的版本号后这个方法就可能会执行
}
3 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
这个方法是用来数据库升级处理,当得知数据库版本号后这个方法也可能执行
}
上面我列举了数据库打开帮助类中主要的三中方法,我们大楷知道我们需要在那个方法里写什么,上面我说 后两个方法在数据库版本号获取后可能会执行,既然两种方法都说可能执行,那么就有可能两种方法都执行,或者两种方法都不执行,或者两种方法中任选其一来执行(这样看起来好麻烦)—-但真的是这样吗?
这下我们来分析这三种方法在什么时候触发(我们假设最新数据库版本为4)
1 构造方法是在这个类加载的时候就触发,这个很好理解 在这里我们只需要传入数据库名字(当然如果我们需要创建多个数据库,就可以多继承几个数据库帮助类)最新版本号(当然如果本次数据库不需要改变,数据库版本号也就没有改的必要),后面的游标什么的默认null
2 创建和升级这两个方法: 前面说构造方法一定会触发,但是创建和升级这两个方法呢,下面我分为几种情况
第一:用户第一次安装这个APP(安装后卸载也算,未卸载但删除了APP里面的缓存数据也算)。这具体代表什么呢,首先用户手机里根本就没有这个数据库,也就没有这个数据库对应的表了(为什么要说这个数据库对应的表,因为我们不排除这个APP里不止一个数据库),当获取数据库版本得到的是null,在这种情况下onCreate()一定会执行,没有数据表就创建表,这个也很好理解(这里提示一点:在写数据库语句的时候,最好将语句字符串写在方法的外面,在创建的时候直接执行创建命令将字符串变量传入即可)但是这里要注意一点,这个时候用户创建数据库表是直接创建第3版本的数据库表,所以这里执行的语句必须达到的效果是保证该方法执行后,数据库表是最新的,这个方法的参数就只有数据源,然后往数据源里写命令
第二:当前用户数据库版本号是1或者2或者3,当他需要升级到4版本 这时由于检测版本号的时候发现这个用户手机里数据库版本号和当前指定的数据库版本不一样,这说明数据库要升级,这时onCreate()方法就不会执行,而是去执行onUpgrade()方法,所以我们就需要在这个方法里去对数据库表的结构或者内容进行换血,我们看到这个方法传递进来的参数有 从数据库管理系统获取来的用户当前数据库版本号,本次需要升级到的版本号,以及数据源。这里需要考虑的情况是有可能是:
1>4
2>4
3>4
但是我们希望从不同版本过来的用户都持续升级到最新版本(也就是4),希望的情况是这样的:
1>4:1>2>3>4
2>4:2>3>4
3>4这个是顺序升级
(为什么要这样,从1直接到4不是更快吗,不是这样的 因为每个版本,会设置默认数据,或者其他,如果直接一步到位会丢失数据,最重要的一点是当数据库版本越来越高时维护就显得简单),既然明白了过程 ,如何在代码删体现就是接下来的问题了
在这里我们通常使用程序设计中典型的结构:switch结构(其实这个结构就是一个循环判断的结构,使用其他循环判断也可以实现,比如for(),do while(),while())大概是下面这样
switch(oldVersion)
{
case 1:
//当前用户的数据库版本号是1从这里开始执行
//具体的操作语句
case 2:
//当前用户数据库版本是2就从这里开始执行
//具体的操作语句
case 3:
//当前用户的数据库版本是3从这里开始执行
//具体的操作语句
default:
break;
}
细心的你一定发现了在case 1,2,3 的时候没有在语句结束部分写beak; 这种写法会有什么效果呢,用户版本是1的话 他会从case1 一直执行到case3 ,这就保证了用户可以从版本1持续升级到版本4,至于每个case后面的语句怎么写,就需要根据具体情况分析了,不过case x一定表示需要将数据库结构从x调整到x+1,比如case3表示当前版本需要从3升级到4(也就是最新),case1表示当前用户版本是1 在这个case里面需要做的就是将版本从1升级到2,至于后续的升级交给后面的case去执行,这种做法其实也有迭代的意思,在后续版本越来越高时,需要改动的代码也只需再写一个case 将数据库版本从上一个版本升级到下一个版本(就是最新版本)所需要的操作,而不是在每个case里面都要写上升级到最新版本所需要的操作,这样有利于代码的维护
SQLite语句举例
前面把数据库创建,数据库升级原理,执行流程都梳理了,下面来讲一些常用的语法该怎么写
到这里已经把数据库操作的工作梳理完了,跟多的使用请参考框架官网,我这里也有一个疑问,数据库的升级 在Javase代码里面应该怎么改,如果用原生的方法,升级操作又该怎么写 如果用户本来的数据库版本是1,现在最新的APP数据库版本是3,用户升级APP后需要将数据库升级到3 但是又不想让用户直接从1到3 而是一步一步过度到3 ,以及更复杂的问题有待解决
本文章部分参考网友的代码,写的比较粗略,欢迎留言
这篇关于Android原生SQLite操作以及greenDao框架操作SQLite的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!