现在的位置: 首页 > 综合 > 正文

45_SQLite数据库存储

2013年04月07日 ⁄ 综合 ⁄ 共 10614字 ⁄ 字号 评论关闭

一、SQLite数据库简介

  SQLite一个关系型数据库,SQLite3支持NULL、INTEGER、REAAL(浮点数字)、TEXT(字符串文本)、和BLOB(二进制对象),虽然它只支持这5种类型,但是实际上它也接受varchar(n)、char(n)、decimal(p, s)等数据类型,只是在运算或保存时会转换成对应的5种数据类型。

  SQLite是Android所带的一个标准的数据库,它是一个轻量级的嵌入式数据库,SQLite数据库的特点:更加适用于嵌入式系统,嵌入使用它的应用程序中;占用内存非常少,运行高效可靠,可移植性好;提供了零配置(zero-configuration)运行模式。SQLite数据库不仅提高了运行效率,而且屏蔽了数据库使用和管理的复杂性,程序仅需要进行最基本的数据操作,其他操作可以交给程序内部的数据库引擎完成。SQLite核心大约有3万行标准C代码,并且其源代码开放。

  SQLite数据库的模块化设计,由8个独立的模构成,这些独立模块又构成了三个主要的子系统:编译器、核心模块及后端,模块将复杂的查询过程分解为细小的工作进行处理。其模块结构图如下:

  接口由SQLite C API组成,该API简单易用,无论是应用程序、脚本,还是库文件,最终都是通过接口与SQLite交互的。图中的编译器由分词器、分析器和代码生成器组成。分词器和分析器对SQL语句进行语法检查分析后,将SQL语句转化为对于底层来说能够更方便处理的分层的数据结构,这种分层的数据结构称为“语法树”,生成的语法树将传递给代码生成器,由代码生成器进行处理,生成一种针对SQLite的汇编代码,该部分代码最后交与虚拟机执行。

  SQLite数据库体系结构中最核心的部分是虚拟机,也称为虚拟数据库引擎(Virtual Database engine, VDBE)。与Java虚拟类似,虚拟机是用来解释执行字节码的。虚拟机的字节码由128个操作码组成,这些操作码主要用于操作数据库,每一条指令都可以完成特定的数据库操作,或以特定的方式处理栈的内容。

  后端由B-树、页缓存和操作系统接口组成。B-树的主要功能就是索引,它维护着各个页面之间的复杂关系,便于快速找到所需数据;页缓存主要就是通过操作系统接口在B-树和磁盘之间传递页面;B-树和页缓存共同对数据进行管理。

 

二、SQLite的常用类及程序案例

  在Android系统中,如果要进行SQLite数据库的操作,则主要使用以下几个类和接口:

(1)android.database.sqlite.SQLiteDatabase:完成数据的CRUD操作及事务处理

(2)android.database.sqlite.SQLiteOpenHelper:定义数据库的创建及更新操作

(3)android.database.Cursor:保存所有的查询结果

(4)android.content.ContentValues:对传递的数值进行封装

  在Android系统中,每一个android.database.sqlite.SQLiteDatabase类的实例都代表了一个SQLite数据库的操作,通过SQLiteDatabase类可以执行SQL语句,以完成对数据表的增删改查操作,或者进行数据库的事务处理,此类中定义了基本的数据库执行SQL语句的操作方法以及一些操作模式常量。在进行开发时,一般不用创建SQLiteDatabase类对象,往往会由一个辅助的工具类SQLiteOpenHelper行操作的管理。SQLiteOpenHelper为中定义了3个回调方法:

(1)onCreate():在第一次使用数据库时会调用此方法生成相应的数据库表,但是此方法并不是在实例化SQLiteOpenHelper类的对象时调用,而是通过对象调用了getReadableDatabase()或getWritableDatabase()方法时才会调用。

(2)onUpgrade():当数据库需要进行升级时会调用此方法,一般可以在此方法中将数据表删除,并在删除表后调用onCreate()方法重新创建新的数据表。

(3)onopen():当数据库打开时会调用此方法,但是一般情况下用户不需要覆写此方法。

  在SQLiteDatabase类中,也为用户提供了insert()、update()、delete()、query()等方法,只是在使用这些方法进行数据库操作时,所有的数据必须使用ContentValues类进行封装。android.content.ContentValues的功能与HashMap类的功能类似,都是采用“key=value”的形式保存数据,唯一不同的是,在ContentValues类中所设置的key都是String型的数据,而所设置的value都是基本数据类型的包装类。

  当Android程序需要进行数据检索操作时,需要保存全部的查询结果,而保存查询结果可以使用android.database.Cursor接口完成,并且可以完成对结果集随机读写访问的操作。利用Cursor接口提供的一些常用方法,从前到后依次取得全部数据可按如下过程来进行:

(1)moveToFirst():将结果集的指针放在第一行数据。

(2)isAfterLast():判断是否还有数据,如果有,则取出。

(3)moveToNext():将指针向下移动,并继续使用isAfterLast()方法判断。

即:for(result.moveToFirst(); !result.isAfterLast(); result.moveToNext()){循环体;} 

  在进行数据查询时,分页显示的功能能是很常用的,Android系统通过ListView来实现滑动分页数据的读取,当用户将ListView滑动到底部时,系统可以自动从数据表中向下加载剩余的部分数据。要实现这种滚动刷新的操作,就需要一个滚动监听接口(OnScrollListener)的事件支持,此接口定义如下:

public interface OnScrollListener {
	//The user had previously been scrolling using touch and had performed a fling.
	public static int SCROLL_STATE_FLING;
	//The view is not scrolling.
	public static int SCROLL_STATE_IDLE;
	//The user is scrolling using touch, and their finger is still on the screen
	public static int SCROLL_STATE_TOUCH_SCROLL;

	/**
	 * Callback method to be invoked when the list or grid has been scrolled. 
	 * This will be called after the scroll has completed
	 * 
	 * @param view : The view whose scroll state is being reported
	 * @param firstVisibleItem : the index of the first visible cell (ignore if visibleItemCount == 0)
	 * @param visibleItemCount : the number of visible cells
	 * @param totalItemCount : the number of items in the list adaptor
	 */
	public abstract void onScroll(AbsListView view, int firstVisibleItem, 
			int visibleItemCount, int totalItemCount);
	
	/**
	 * Callback method to be invoked while the list view or grid view is being scrolled. 
	 * If the view is being scrolled, this method will be called before the next frame of 
	 * the scroll is rendered. In particular, it will be called before any calls to 
	 * getView(int, View, ViewGroup).
	 * 
	 * @param view : The view whose scroll state is being reported
	 * @param scrollState : The current scroll state. One of SCROLL_STATE_IDLE, 
	 * SCROLL_STATE_TOUCH_SCROLL or SCROLL_STATE_IDLE.
	 */
	public abstract void onScrollStateChanged(AbsListView view, int scrollState);
}

例 1 下面就通过一个实例来演示一下ListView分页操作的实现。程序运行效果截图

该程序代码清单如下:

(1)DatabaseHelper.java:负责取得数据库的连接对象,并创建数据表

(2)Cursor.java:完成分页数据查询的操作类,除返回查询数据之外,也返回全部记录数

(3)MainActivity.java:程序操作的Activity主类

(4)main.xml:程序的主体布局管理器,为MainActivity.java类提供布局支持

(5)tab_info.xml:ListView列表显示所需要的布局管理器

以下是详细源码:

(1)DatabaseHelper.java

package org.lion.sqlitetest;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {
	private static final String DATABASENAME = "runninglion.db";
	private static final int DATABASEVERSION = 1;
	private static final String TABLENAME = "tablion";

	public DatabaseHelper(Context context) {
		super(context, DATABASENAME, null, DATABASEVERSION);		
	}

	@Override
	public void onCreate(SQLiteDatabase db) {   //创建数据表
		String sql = "create table " + TABLENAME + "("   //sql语句
				 + "id integer primary key,"
				 + "name varchar(50) not null,"
				 + "birthday date not null)";
		db.execSQL(sql);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		String sql = "drop table if exists " + TABLENAME;
		db.execSQL(sql);
		this.onCreate(db);
	}
}

(2)Cursor.java

package org.lion.sqlitetest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class TabCursor {
	private static final String TABLENAME = "tablion";
	private SQLiteDatabase db = null;
	public TabCursor(SQLiteDatabase db){
		this.db = db;
	}
	/**
	 * 查询出所有满足条件的记录数,使用count()函数,可以用于总页数的计算
	 * @return
	 */
	public int getCount(){
		int count = 0;
		//String sql = "select count(id) from" + TABLENAME;
		String sql = "select * from " + TABLENAME;
		Cursor result = db.rawQuery(sql, null);
		for(result.moveToFirst(); !result.isAfterLast(); result.moveToNext()){
			count = result.getInt(0);
		}
		return count;
	}
	
	/**
	 * 根据用户给出的两个分页参数,查询出所有的相关数据,并将数据设置到List<Map<String, Object>>
	 * 集合中,以在ListView中显示
	 * @param currentPage
	 * @param lineSize
	 * @return
	 */
	public List<Map<String, Object>> query(int currentPage, int lineSize){//查询
		List<Map<String, Object>> all = new ArrayList<Map<String, Object>>();
		String sql = "select id,name,birthday from " + TABLENAME + " limit ?,?";
		String selectionArgs[] = new String[]{   //设置查询参数
				String.valueOf((currentPage-1) * lineSize),
				String.valueOf(lineSize)};
		Cursor result = db.rawQuery(sql, selectionArgs);
		for(result.moveToFirst(); !result.isAfterLast(); result.moveToNext()){
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("id", result.getInt(0));
			map.put("name", result.getString(1));
			map.put("birthday", result.getString(2));
			all.add(map);   //向集合中保存
		}
		this.db.close();   //关闭数据连接
		return all;
	}
}

(3)MainActivity.java

package org.lion.sqlitetest;

import java.util.List;
import java.util.Map;
import org.lion.jsontest.R;
import android.app.Activity;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

public class MainActivity extends Activity {
	private SQLiteOpenHelper helper = null;
	private LinearLayout layout = null;
	private ListView listView = null;
	private int currentPage = 1;   //当前程序所在页
	private int lineSize = 15;   //每页显示的记录长度
	private int allRecorders = 0;   //保存全部记录数
	private int pageSize = 1;   //计算当前显示的总页数
	private int lastItem = 0;   //保存最后一个记录点
	private SimpleAdapter simpleAdapter = null;
	private LinearLayout loadLayout = null;
	private TextView loadInfo = null;   //定义提示文本
	private List<Map<String, Object>> all = null;
	private LayoutParams layoutParams = new LinearLayout.LayoutParams(
			LinearLayout.LayoutParams.MATCH_PARENT,
			LinearLayout.LayoutParams.WRAP_CONTENT);
	
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		super.setContentView(R.layout.main);	
		layout = (LinearLayout)findViewById(R.id.mainlayout);
		loadLayout = new LinearLayout(this);
		helper = new DatabaseHelper(this);   //定义数据库辅助类
		loadInfo = new TextView(this);
		loadInfo.setText("Loading data...");
		loadInfo.setGravity(Gravity.CENTER);
		loadInfo.setTextSize(25.0f);
		loadLayout.addView(this.loadInfo, this.layoutParams);
		loadLayout.setGravity(Gravity.CENTER);
		this.showAllData();   //显示数据
		pageSize = (allRecorders + currentPage - 1)/lineSize;   //求出总页数
	}
	private void showAllData(){
		helper = new DatabaseHelper(MainActivity.this);
		listView = new ListView(MainActivity.this);
		TabCursor cursor = new TabCursor(helper.getReadableDatabase());
		allRecorders = cursor.getCount();
		all = cursor.query(currentPage, lineSize);
		simpleAdapter = new SimpleAdapter(this,
				all,   //将数据包装
				R.layout.tab_info,   //每行显示一条数据
				new String[]{"id","name","birthday"},   //设置组件的字段
				new int[]{R.id.id,R.id.name,R.id.birthday});   //实例化适配器对象
		listView.addFooterView(loadLayout);   //增加一个标注
		listView.setAdapter(simpleAdapter);   //设置显示数据
		listView.setOnScrollListener(new OnScrollListenerImpl());
		layout.addView(listView);   //追加组件
	}
	private class OnScrollListenerImpl implements OnScrollListener{
		@Override
		public void onScroll(AbsListView view, int firstVisibleItem,
				int visibleItemCount, int totalItemCount){
			lastItem = firstVisibleItem + visibleItemCount - 1;
		}
		@Override
		public void onScrollStateChanged(AbsListView view, int scrollState){
			if(lastItem == simpleAdapter.getCount()	&& currentPage<pageSize 
					&& scrollState == OnScrollListener.SCROLL_STATE_IDLE){
				currentPage++;   //修改当前所在页
				listView.setSelection(lastItem);   //设置显示位置
				appendData();   //刷新显示数据
			}
		}
	}
	private void appendData(){
		TabCursor cursor = new TabCursor(helper.getReadableDatabase());
		List<Map<String, Object>> newdata = cursor.query(currentPage, lineSize);
		all.addAll(newdata);   //追加数据
		simpleAdapter.notifyDataSetChanged();   //更新记录通知
	}
}

(4)main.xml

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout  
    xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/mainlayout"
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"  
    android:background="#00ff00"  
    android:orientation="vertical">  
</LinearLayout>

(5)tab_info.xml

<?xml version="1.0" encoding="utf-8"?>  
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="fill_parent"
    android:layout_height="wrap_content">
    <TableRow>
        <TextView
            android:id="@+id/id"
            android:textSize="20px"
            android:layout_height="wrap_content"
            android:layout_width="30px"/>
        <TextView
            android:id="@+id/name"
            android:textSize="20px"
            android:layout_height="wrap_content"
            android:layout_width="130px"/>
        <TextView
            android:id="@+id/birthday"
            android:textSize="20px"
            android:layout_height="wrap_content"
            android:layout_width="160px"/>       
    </TableRow>
</TableLayout>

三、SQLite的事务处理

在SQLite操作中也支持事务处理的,事务处理主要使用android.database.sqlite.SQLiteDatabase为中的以下3个方法:

(1)开始事务:public void begin Transaction()

(2)提交更新或回滚事务:public void setTransactionSuccessful()

(3)结束事务:public void endTransaction()

例 2 事务处理程序案例:

import android.database.sqlite.SQLiteDatabase;

public class TransactionTest {
	private static final String TABLENAME = "tablion";
	private SQLiteDatabase db = null;
	public TransactionTest(SQLiteDatabase db){
		this.db = db;
	}
	public void insertBatch(){
		db.beginTransaction();   //开始事务
		try{
			db.execSQL("insert into " + TABLENAME + "(id,name,birthday)values(?, ?)",
					new Object[]{"lion", "1991-02-03"});
			//其它操作......
			db.setTransactionSuccessful();   //如果不能正确执行,则回滚操作
		}catch(Exception e){		
			
		}finally{
			db.endTransaction();   //结束事务
		}
		db.close();
	}
}

抱歉!评论已关闭.