Android的数据存储:Sqlite数据库存储

Sqlite数据库存储

介绍
应用运行需要保存一系列有一定结构的数据, 比如说公司员工信息
文件类型: .db
数据保存的路径: /data/data/projectPackage/databases/xxx.db
默认情况下其它应用不能访问, 当前应用可以通过ContentProvider提供其它应用操作
应用卸载时会删除此数据
SQLite (http://www.sqlite.org/),是一款轻型的关系型数据库服务器, 
移动设备的数据库存储都使用SQLite, 它的特点: 
	安装文件小: 最小只有几百K, Android系统已经安装
	支持多操作系统: Android, WP, IOS, Windows, Linux等
	支持多语言: 比如 Java 、 PHP、C#等. 
	处理速度快:  处理速度比Mysql, Oracle, SqlServer都要快(前提是数据量不是特别大)
	Sqlite中的一个数据库就是一个.db文件(本质上.db的后缀都可以不指定)
Sqlite数据库的客户端:
	1.可以直接使用SQLiteExpertPers来直接打开电脑存在的数据库文件
	2.也可以使用电脑的命令行进行进入Sqlite的数据库
		adb shell  进入系统根目录
		cd data/data//databases : 进入包含数据库文件的文件夹下
		sqlite3 contacts2.db : 使用sqlite3命令连接指定的数据库文件, 进入连接模式
		.help : 查看命令列表
		.tables : 查看所有表的列表
		执行insert/delete/update/select语句
		.exit : 退出数据库连接模式
		Ctrl + C : 直接退出sell模式
数据库常用操作
SQLite的数据类型:
	Sqlite支持的数据类型与Mysql相似, 常用的数据类型
	INT/INTEGER : 整数
	FLOAT/DOUBLE : 小数
	CHAR/VARCHAR/TEXT : 字符串文本
	BLOB : 文件
	DATE/ DATETIME : 日期/日期时间
SQLite如何建表:
	Sqlite操作数据库的sql语句基本与mysql一样,  但需要注意下面2个点:
	最大的不同在于创建表时可以不用指定字段类型, Sqlite可以适时的自动转换, 
		但除varchar类型外最好指定类型
	Sqlite中的主键最名称建议使用_id
	
	create table employee (
       _id integer primary key autoincrement, /*主键,自增长*/
       name varchar,                /*字符串*/
       salary double,                                /*小数*/
       birthday date                                /*日期, 可直接插入日期格式字符串*/
	)

表的增删改查和关系型数据库的增删改查是一样的
API
SQLiteOpenHelper: 数据库操作的抽象帮助类:
    
SQLiteOpenHelper(Context context, String name, 
   	CursorFactory factory, int version) : 构造方法, 指定数据库文件名和版本号
abstract void onCreate(SQLiteDatabase db) : 用于创建表,创建表也只是创建一次
		那么如何保证只创建一次表呢?这个onCreate方法就是在数据库创建的时候进行调用
abstract void onUpgrade() : 用于版本更新
SqliteDatabase getReadableDatabase() : 得到数据库连接,能直接执行sql语句
SqliteDatabase: 代表与数据库的连接的类里面的方法:
     long insert(): 用于执行insert SQL, 返回id值
     int update(): 用于执行update SQL
     int delete(): 用于执行delete SQL
     Cursor query(): 用于执行select SQL, 返回包含查询结果数据的Cursor
     void execSql(sql) : 执行sql语句
   
     beginTransaction(): 开启事务
     setTransactionSuccessful(): 设置事务是成功的
     endTransaction(): 结束事务, 可能提交事务或回滚事务
     SqliteDatabase.openDatabase(String path, CursorFactory factory, int flags):  得到数据库连接
Cursor : 包含所有查询结果记录的结果集对象(光标,游标)
	
    int getCount() : 匹配的总记录数
	boolean moveToNext() : 将游标移动到下一条记录的前面
	Xxx getXxx(columnIndex) : 根据字段下标得到对应值
	int getColumnIndex(columnname): 根据字段名得到对应的下标
例子
package com.example.datastorage;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class DBActivity extends Activity
{

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

	/*
	 * 创建库
	 */
	public void testCreateDB(View v)
	{
		MyDBHelper myDBHelper =new MyDBHelper(this, "jane.db",1);
		/*
		 * 获取连接,下面两种获取连接的方式都可以的,只不过
		 * getReadableDatabase获取的时候如果内存不够,就无法插入数据
		 * getWritableDatabase获取的时候如果内存不够,就会抛出异常
		 */
		myDBHelper.getReadableDatabase();
		//myDBHelper.getWritableDatabase();
		
		Toast.makeText(this, "创建数据库", 0).show();
	}

	/*
	 * 更新库
	 */
	public void testUpdateDB(View v)
	{
		MyDBHelper myDBHelper =new MyDBHelper(this, "jane.db",2);
		//获取连接
		myDBHelper.getReadableDatabase();
		Toast.makeText(this, "更新数据库", 0).show();
	}

	/*
	 * 添加记录
	 */
	public void testInsert(View v)
	{
		/*
		 * 1.得到连接
		 * 2.执行insert语句:insert into person (name,age) values ('jane',18)
		 * 3.关闭
		 * 4.提示
		 */
		MyDBHelper myDBHelper = new MyDBHelper(this, "jane.db", 2);
		SQLiteDatabase database = myDBHelper.getReadableDatabase();
		ContentValues values =new ContentValues();
		values.put("name", "mirror");
		values.put("age", 18);
		long id = database.insert("person", null, values);
		
		database.close();
		Toast.makeText(this, "插入ID"+id, 0).show();
	}

	/*
	 * 更新
	 */
	public void testUpdate(View v)
	{
		MyDBHelper myDBHelper = new MyDBHelper(this, "jane.db", 2);
		SQLiteDatabase database = myDBHelper.getReadableDatabase();
		//update person set name =feng ,age=81 where _id=4
		ContentValues values =new ContentValues();
		values.put("name", "feng");
		values.put("age", 81);
		int updateCount = database.update("person", values, "_id=?", new String[]{"1"});
		Toast.makeText(this, "updateCount"+updateCount, 0).show();
	}

	/*
	 * 删除
	 */
	public void testDelete(View v)
	{
		MyDBHelper myDBHelper = new MyDBHelper(this, "jane.db", 2);
		SQLiteDatabase database = myDBHelper.getReadableDatabase();
		//delete from person where _id = 2;
		int deleteCount = database.delete("person", "_id =2", null);
		Toast.makeText(this, "deleteCount"+deleteCount, 0).show();
	}

	/*
	 * 查询
	 */
	public void testQuery(View v)
	{
		MyDBHelper myDBHelper = new MyDBHelper(this, "jane.db", 2);
		SQLiteDatabase database = myDBHelper.getReadableDatabase();
		//select * from person
		Cursor cursor = database.query("person", null, null, null, null, null, null);
		//取出cursor中所有的数据
		while(cursor.moveToNext())
		{
			int id=cursor.getInt(0);
			String name=cursor.getString(1);
			int age=cursor.getInt(2);
			Log.e("tag", id+"-"+name+"-"+age);
		}
		cursor.close();
		database.close();
		Toast.makeText(this, "Count"+cursor.getCount(), 0).show();

	}

	/*
	 * 测试事务处理:
	 * 事务处理的3步:
	 * 1.开启事务(获取连接后需要对数据库进行操作之后)
	 * 2.设置事务成功(在全部操作正常执行后)
	 * 3.事务结束(在finally中设置)
	 */
	public void testTransaction(View v)
	{
		SQLiteDatabase database =null;
		try
		{
			MyDBHelper myDBHelper = new MyDBHelper(this, "jane.db", 2);
			database = myDBHelper.getReadableDatabase();
			//开启事务
			database.beginTransaction();
			
			//update person set age=1 where _id=1
			ContentValues values =new ContentValues();
			values.put("age", 1);
			int updateCount = database.update("person", values, "_id=?", new String[]{"1"});

			//update person set age=2 where _id=2
			ContentValues values2 =new ContentValues();
			values2.put("age", 2);
			int updateCount2 = database.update("person", values2, "_id=?", new String[]{"2"});
			//设置事务成功
			database.setTransactionSuccessful();
		} finally
		{
			if(database!=null)
			{
				database.endTransaction();
				database.close();
			}
		}
	}
}

package com.example.datastorage;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;

public class MyDBHelper extends SQLiteOpenHelper
{

	public MyDBHelper(Context context, String name,int version)
	{
		super(context, name, null, version);
	}
	/**
	 * 这个回调方法什么时候调用?
	 * 		当数据库文件创建的时候调用,只是调用一次
	 * 这个回调方法用来做什么?
	 * 		可以建表,还有可以初始化一些数据
	 * 
	 * 什么时候才会创建数据库文件?
	 * 		1.这个数据库文件不存在
	 * 		2.测试去连接数据库,发现不存在这个数据库文件就去创建
	 */
	@Override
	public void onCreate(SQLiteDatabase db)
	{
		String sql = "create table person(_id integer primary key autoincrement, name varchar,age int)";
		db.execSQL(sql);
	}
	
	/*
	 * 这个方法是当数据库需要更新的时候进行调用
	 * 当传入的版本号大于数据库的版本号时调用
	 */
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
	{
		
	}
}
<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="match_parent"
        android:layout_height="wrap_content"
        android:text="Create DB" 
        android:onClick="testCreateDB"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Update DB" 
        android:onClick="testUpdateDB"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Insert" 
        android:onClick="testInsert"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Update" 
        android:onClick="testUpdate"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Delete" 
        android:onClick="testDelete"/>

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="query" 
        android:onClick="testQuery"/>
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Test Transaction" 
        android:onClick="testTransaction"/>
</LinearLayout>

Android中的junit测试

1. 添加配置信息
	<application>
	    <! -- 使用android测试包 --> 
	    <uses-library android:name="android.test.runner" />  
	</application>
	
	<!-- android:targetPackage的值应与manifest的package的值一致-->
	<instrumentation android:name="android.test.InstrumentationTestRunner"
	    android:targetPackage=""/>
2. 编写测试类
        class Testextends AndroidTestCase
        然后里面的方法的名称就是test开头,然后加上要测试的方法的首字母大写就行了

增删改查例子

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/BackList"
        android:layout_width="fill_parent"
        android:layout_height="0dp" 
        android:layout_weight="1">
    </ListView>

    <Button
        android:id="@+id/add"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="添加" 
        android:layout_weight="0"
        android:onClick="add"/>

</LinearLayout>
package com.example.blacklist;

import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.BaseAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;

/*
 *使用ListActivity优化功能
		1. MainActivity需要extends ListActivity
		2. 布局文件中的<ListView>的id必须是系统定义的id: list
		3. 如果想在没有数据时显示一个提示文本, 可以在布局中定义 一个<TextView>(id必须为系统的empty)
		
 * 我们做一个功能主要是向这三个方面思考:
 * 	1. 内存的操作(集合)
 *	2. 存储的操作(sp/数据库/文件)
 *	3. 界面的操作(列表)
 */
public class MainActivity extends Activity
{
	public ListView BackList;
	public BlackNumberAdapter blackNumberAdapter;
	public BlackNumberDao blackNumberDao;
	public List<BlackNumber> data;
	public int position;
	
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		BackList= (ListView) findViewById(R.id.BackList);
		blackNumberAdapter =new BlackNumberAdapter();
		blackNumberDao =new BlackNumberDao(this);
		data =blackNumberDao.getAll();
		
		BackList.setAdapter(blackNumberAdapter);
		/*
		 * 	1. 显示ContextMenu
			2. 响应对item的选择
				1). 删除数据表对应的数据
				2). 删除List对应的数据
				3). 通知更新列表 
			问题: 如何得到长按的position?	
		 */
		BackList.setOnCreateContextMenuListener(this);
	}
	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo)
	{
		super.onCreateContextMenu(menu, v, menuInfo);
		menu.add(0, 1, 0, "修改");
		menu.add(0, 2, 0, "删除");
		//想得到长按的position,在ContextMenuInfo接口的实现类AdapterContextMenuInfo里面可以得到
		AdapterContextMenuInfo info =(AdapterContextMenuInfo) menuInfo;
		position = info.position;
	}
	@Override
	public boolean onContextItemSelected(MenuItem item)
	{
		//得到对应的BlackNumber对象
		BlackNumber blackNumber = data.get(position);
		switch (item.getItemId())
		{
		case 1:
			showUpdateDialog(blackNumber);
			break;
		case 2:
			blackNumberDao.deleteByID(blackNumber.getId());
			data.remove(position);
			blackNumberAdapter.notifyDataSetChanged();
			break;

		default:
			break;
		}
		return super.onContextItemSelected(item);
	}
	private void showUpdateDialog(final BlackNumber blackNumber)
	{
		final EditText editText = new EditText(this);
		editText.setHint(blackNumber.getNumber());
		new AlertDialog.Builder(this)
			.setTitle("更新黑名单")
			.setView(editText)
			.setPositiveButton("更新", new DialogInterface.OnClickListener() {
				@Override
				public void onClick(DialogInterface dialog, int which) {
					/*
					 * 1). 更新List对应的数据
					 * 	 这里需要注意的是,我们传过来的是BlackNumber对象
					 * 	 这个对象是用于界面显示的对象,我们在这里修改了这个
					 * 	 对象的值,那么在界面显示的时候也是对应地更新了值
					 * 我们做一个功能主要是向这三个方面思考:
					 * 	1. 内存的操作(集合)
					 *	2. 存储的操作(sp/数据库/文件)
					 *	3. 界面的操作(列表)
					 */
					String newNumber = editText.getText().toString();
					blackNumber.setNumber(newNumber);
					
					//2). 更新数据表对应的数据
					blackNumberDao.update(blackNumber);
					
					//3). 通知更新列表 
					blackNumberAdapter.notifyDataSetChanged();
				}
			})
			.setNegativeButton("取消", null)
			.show();
	}
	public void add(View v)
	{
		/*
		 * 1.显示添加的dialog,并且带有输入框的
		 * 2.在确定的回调方法中的操作:
		 * 		1.保存数据到表中
		 * 		2.保存数据到list
		 * 		3.通知更新列表
		 * 有两个问题:
		 * 	1.新添加的数据没有显示在第一行,
		 * 		解决:在添加进入列表的时候添加在第一行
		 * 	2.数据在初始化的时候没有将新添加的数据显示在前面
		 * 		我们想的是将新添加的数据显示在前面,旧的数据显示在后面
		 * 		解决:在dao查询的时候,按照_id进行倒序排列
		 */
		final EditText editText =new EditText(this);
		editText.setHint("请输入你的黑名单");
		new AlertDialog.Builder(this)
			.setTitle("添加黑名单")
			.setView(editText)
			.setPositiveButton("添加", new DialogInterface.OnClickListener()
			{
				@Override
				public void onClick(DialogInterface dialog, int which)
				{
					String number =editText.getText().toString();
					BlackNumber blackNumber =new BlackNumber(-1, number );
					//保存到表
					blackNumberDao.add(blackNumber );
					//保存到list
					data.add(0, blackNumber);
					//更新列表
					blackNumberAdapter.notifyDataSetChanged();
				}
			})
			.setNegativeButton("取消", null)
			.show();
	}
	class BlackNumberAdapter extends BaseAdapter
	{

		@Override
		public int getCount()
		{
			return data.size();
		}

		@Override
		public Object getItem(int position)
		{
			return data.get(position);
		}

		@Override
		public long getItemId(int position)
		{
			return 0;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent)
		{
			if(convertView==null)
			{
				convertView=View.inflate(MainActivity.this, android.R.layout.simple_list_item_1, null);
			}
			BlackNumber blackNumber = data.get(position);
			TextView textView =(TextView) convertView.findViewById(android.R.id.text1);
			textView.setText(blackNumber.getNumber());
			return convertView;
		}	
	}
}
package com.example.blacklist;

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

public class MyDbHelper extends SQLiteOpenHelper
{

	public MyDbHelper(Context context)
	{
		super(context, "black.db", null	, 1);
	}

	@Override
	public void onCreate(SQLiteDatabase db)
	{
		Log.i("TAG", "MyDbHelper onCreate()");
		//创建表
		db.execSQL("create table black_number(_id integer primary key autoincrement, number varchar)");
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
	{
	}
}
package com.example.blacklist;

/**
 * black_number表对应的实体类
 */
public class BlackNumber {

	private int id;
	private String number;

	public BlackNumber(int id, String number) {
		super();
		this.id = id;
		this.number = number;
	}

	public BlackNumber() {
		super();
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	@Override
	public String toString() {
		return "BlackNumber [id=" + id + ", number=" + number + "]";
	}
}
package com.example.blacklist;

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;

// 操作black_number表的DAO类
public class BlackNumberDao
{
	MyDbHelper myDbHelper;
	public BlackNumberDao(Context context)
	{
		myDbHelper =new MyDbHelper(context);
	}
	
	public void add(BlackNumber blackNumber)
	{
		SQLiteDatabase database = myDbHelper.getReadableDatabase();
		ContentValues values =new ContentValues();
		values.put("number", blackNumber.getNumber());
		long id = database.insert("black_number", null, values);
		Log.e("TAG", "id="+id);
		database.close();
	}
	public void deleteByID(int id)
	{
		SQLiteDatabase database = myDbHelper.getReadableDatabase();
		ContentValues values =new ContentValues();
		int deleteCount = database.delete("black_number","_id =?" , new String[]{id+""});
		Log.e("TAG", "deleteCount="+deleteCount);
		database.close();
	}
	public void update(BlackNumber blackNumber)
	{
		//1. 得到连接
		SQLiteDatabase database = myDbHelper.getReadableDatabase();
		//2. 执行update update black_number set number=xxx where _id=id
		ContentValues values = new ContentValues();
		values.put("number", blackNumber.getNumber());
		int updateCount = database.update("black_number", values , "_id="+blackNumber.getId(), null);
		Log.i("TAG", "updateCount="+updateCount);
		//3. 关闭
		database.close();

	}
	public List<BlackNumber> getAll()
	{
		List<BlackNumber> list = new ArrayList<BlackNumber>();
		//1. 得到连接
		SQLiteDatabase database = myDbHelper.getReadableDatabase();
		//2. 执行query select * from black_number
		Cursor cursor = database.query("black_number", null, null, null, null, null, "_id desc");
		//3. 从cursor中取出所有数据并封装到List中
		while(cursor.moveToNext()) {
			//id
			int id = cursor.getInt(0);
			//number
			String number = cursor.getString(1);
			list.add(new BlackNumber(id, number));
		}
		//4. 关闭
		cursor.close();
		database.close();	
		return list;
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ReflectMirroring

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值