Android——数据存储(二)(二十二)

1. SQLite数据库存储

1.1 知识点

(1)了解SQLite数据库的基本作用;

(2)掌握数据库操作辅助类:SQLiteDatabase的使用;

(3)可以使用命令操作SQLite数据库;

(4)可以完成数据库的CRUD操作;

(5)掌握数据库查询及Cursor接口的使用。

1.2 具体内容

在Android当中,本身提供了一种微型的嵌入式数据库叫做SQLite,同样可以执行SQL语句,对于不熟悉sql和jdbc的小伙伴,还是需要去自学一下。

下面我们首先使用一段代码演示如何取得数据库表操作。

java 复制代码
package com.example.sqliteopenhelper;

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

public class MySQLiteOpenHelper extends SQLiteOpenHelper {
    public static final String DATABASENAME="wancy";//数据库名称
	public static final int DATABASEVERSION=1;//版本号
	public static final String TABLENAME = "DH10";//数据库表名
	
	public MySQLiteOpenHelper(Context context) {
		super(context, DATABASENAME, null, DATABASEVERSION);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		String sql = "create table "+TABLENAME+" (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);
	}

}

以上要注意一点,在SQLite当中如果想要让某个字段自动增长,那么在创建表的时候使用"INTEGER PRIMARY KEY"声明即可。

那么这样的话一个数据库辅助类已经定义好了,那么我们下面在Activity当中去调用此类的方法。

java 复制代码
package com.example.sqliteproject;

import com.example.sqliteopenhelper.MySQLiteOpenHelper;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class SQLiteActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_sqlite);
		
		MySQLiteOpenHelper helper = new MySQLiteOpenHelper(this);
		helper.getWritableDatabase();//表示以读写的形式打开数据库
		
	}


}

现在已经取得了数据库的连接了,如果以上代码没有错误,运行之后就可以在文件列表当中找到数据表.FileExplorer/data/data/数据库辅助类所在的包/database。根据代码,可以发现,如果数据库不存在,那么会自动调用onCreate方法去创建表,如果数据库已经存在了,但是版本号发生了改变,将会调用onUpgrade方法,现将原来的表删除之后,在去创建新的表。

**·**使用SQLite数据库并完成数据库的更新操作。

java 复制代码
public void insert(String name,String birthday){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		String sql = "insert into "+TABLENAME+" (name,birthday) values ('"+name+"',"+birthday+")";
		db.execSQL(sql);
		db.close();
	}
	public void update(int id,String name,String birthday){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		String sql = "update "+TABLENAME+" set name='"+name+"',birthday="+birthday+" where id="+id;
		db.execSQL(sql);
		db.close();
	}
	public void delete(int id){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		String sql = "delete from "+TABLENAME+" where id="+id;
		db.execSQL(sql);
		db.close();
	}

以上增删改三个方法写完之后,就可以在Activity当中去进行调用。

java 复制代码
package com.example.sqliteproject;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import com.example.sqliteopenhelper.MySQLiteOpenHelper;

public class SQLiteActivity extends Activity {
	MySQLiteOpenHelper helper=null;
	Button insert,update,del = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_sqlite);
		
		insert=(Button) super.findViewById(R.id.insert);
		update = (Button) super.findViewById(R.id.update);
		del = (Button) super.findViewById(R.id.del);
		helper = new MySQLiteOpenHelper(this);
		helper.getWritableDatabase();//表示以读写的形式打开数据库
		insert.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				helper.insert("毛栗子", "1995-01-05");
				Toast.makeText(SQLiteActivity.this, "新增成功", 0).show();
			}
		});
		update.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				helper.update(1, "大白兔", "1995-01-05");
				Toast.makeText(SQLiteActivity.this, "修改成功", 0).show();
			}
		});
		del.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				helper.delete(1);
				Toast.makeText(SQLiteActivity.this, "删除成功", 0).show();
			}
		});
	}


}

以上的程序,所有数据都是固定的,这在实际开发当中显然是不科学的,因为我们至少也要使用编辑框进行数据的编辑才是符合实际,而且使用字符串拼凑sql这种形式也是存在问题的,那么我们可以使用占位符的形式来处理这种问题。

java 复制代码
public void insert(String name,String birthday){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		java.sql.Date date = null;
		try {
			date = new java.sql.Date(new SimpleDateFormat("yyyy-MM-dd").parse(birthday).getTime());
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		String sql = "insert into "+TABLENAME+" (name,birthday) values (?,?)";//使用占位符
		Object args[] = new Object[]{name,birthday};
		db.execSQL(sql,args);
		db.close();
	}

对于以上程序,只是换了另一种操作方法来解决拼凑的问题,功能上没有变化。

对于拼凑sql和使用占位符,可以形成一个共识:

肯定是使用占位符更好一些。

在android当中,还对数据的封装提供了一个ContentValues类。 可以理解为一个Map集合,有key和value,唯一不同的是ContentValues中的key必须是字符串。以后的操作就可以直接通过key取得value。并将value设置到sql语句当中。

java 复制代码
public void insert(String name,String birthday){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		ContentValues cv = new ContentValues();
		cv.put("name", name);
		cv.put("birthday", birthday);
		db.insert(TABLENAME, null, cv);//进行新增操作
		db.close();
	}
	public void update(int id,String name,String birthday){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		ContentValues cv = new ContentValues();
		cv.put("name", name);
		cv.put("birthday", birthday);
		String whereClause = " id=?";
		String whereArgs[]= new String[]{String.valueOf(id)};
		db.update(TABLENAME, cv, whereClause, whereArgs);//更新操作
		db.close();
	}
	public void delete(int id){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		String whereClause = " id=?";
		String whereArgs[]= new String[]{String.valueOf(id)};
		db.delete(TABLENAME, whereClause, whereArgs);//删除操作
		db.close();
	}

那么对于使用占位符和ContentValues进行数据库更新操作,两种方式并没有严格的优劣之分,但是对于不会sql的开发人员来说,使用ContentValues更好的。

**·**数据库查询机Cursor接口

在JDBC中ResultSet接口不呢是的功能就是依靠一个next()方法一动指针向下取数据,是到了JDBC2.0之后才能够向上取数据,但是我们一般也不去用这种向上取的形式。

Cursor接口,出现的比ResultSet晚,所有操作机制有些不同。

java 复制代码
package com.example.sqliteproject;

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

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

public class DH10Cursor {
      private static final String TABLENAME="PR10";
      private SQLiteDatabase db =null;
	public DH10Cursor(SQLiteDatabase db) {
		super();
		this.db = db;
	}
    
	public List<String> find(){
		List<String> all = new ArrayList<String>();
		String sql = "select id,name,birthday from "+TABLENAME;
		Cursor result = this.db.rawQuery(sql, null);//执行查询语句
		//采用循环的形式去结果集中的数据
		for(result.moveToFirst();!result.isAfterLast();result.moveToNext()){
			all.add(result.getInt(0)+"====="+result.getString(1)+"====="+result.getString(2));
		}
		result.close();
		db.close();
		return all;
	}
}
java 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/mylayout"
    tools:context=".SQLiteActivity" >

   <Button
       android:id="@+id/insert"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="新增"
       />
   <Button
       android:id="@+id/update"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="修改"
       />
   <Button
       android:id="@+id/del"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="删除"
       />
   <Button
       android:id="@+id/query"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="查询"
       />
       
</LinearLayout>
java 复制代码
package com.example.sqliteproject;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Toast;

import com.example.sqliteopenhelper.MySQLiteOpenHelper;

public class SQLiteActivity extends Activity {
	MySQLiteOpenHelper helper=null;
	Button insert,update,del,query = null;
	ListView listView = null;
	LinearLayout myLayout = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_sqlite);
		helper = new MySQLiteOpenHelper(this);
		insert=(Button) super.findViewById(R.id.insert);
		update = (Button) super.findViewById(R.id.update);
		del = (Button) super.findViewById(R.id.del);
		query = (Button) super.findViewById(R.id.query);
		myLayout = (LinearLayout) super.findViewById(R.id.mylayout);
		query.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				SQLiteActivity.this.listView = new ListView(SQLiteActivity.this);
				listView.setAdapter(new ArrayAdapter<String>(SQLiteActivity.this, android.R.layout.simple_list_item_1,
						new DH10Cursor(SQLiteActivity.this.helper.getWritableDatabase()).find()));
				myLayout.addView(listView);
				
			}
		});
		
		
		insert.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				helper.insert("毛栗子", "1995-01-05");
				Toast.makeText(SQLiteActivity.this, "新增成功", 0).show();
			}
		});
		update.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				helper.update(1, "大白兔", "1995-01-05");
				Toast.makeText(SQLiteActivity.this, "修改成功", 0).show();
			}
		});
		del.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				helper.delete(1);
				Toast.makeText(SQLiteActivity.this, "删除成功", 0).show();
			}
		});
	}


}

需要注意一下Cursor的下标是从0开始的有别于ResultSet

**·**模糊查询

**·**使用sql语句完成模糊查询

java 复制代码
public List<String> find(){
		List<String> all = new ArrayList<String>();
		String sql = "select id,name,birthday from "+TABLENAME+" where name like ?";
		String args[] = new String[]{"%栗%"};
		Cursor result = this.db.rawQuery(sql, args);//执行查询语句
		//采用循环的形式去结果集中的数据
		for(result.moveToFirst();!result.isAfterLast();result.moveToNext()){
			all.add(result.getInt(0)+"====="+result.getString(1)+"====="+result.getString(2));
		}
		result.close();
		db.close();
		return all;
	}

**·**使用query方法完成

java 复制代码
public List<String> find(){
		List<String> all = new ArrayList<String>();
		//String sql = "select id,name,birthday from "+TABLENAME+" where name like ?";
		String selection = "name like ?";
		String args[] = new String[]{"%栗%"};
		String columns[] = new String[]{"id","name","birthday"};
		Cursor result = this.db.query(TABLENAME, columns, selection, args, null, null, null);//执行查询语句
		//采用循环的形式去结果集中的数据
		for(result.moveToFirst();!result.isAfterLast();result.moveToNext()){
			all.add(result.getInt(0)+"====="+result.getString(1)+"====="+result.getString(2));
		}
		result.close();
		db.close();
		return all;
	}

既然模糊查询已经能够完成了,那么还有一种功能可以往下去实现,那就是数据的分页。如果想要实现数据的部分显示,这个语句和Mysql中的查询方式是非常相似,使用limit完成。下面我们进行一些简单的分页操作。

java 复制代码
public List<String> find(){
		int currentPage = 1;//当前页码
		int lineSize = 5;//每页显示的数据量
		List<String> all = new ArrayList<String>();
		String sql = "select id,name,birthday from "+TABLENAME+" limit ?,?";
		String args[] = new String[]{String.valueOf((currentPage-1)*lineSize),String.valueOf(currentPage*lineSize)};
		Cursor result = this.db.rawQuery(sql, args);
		//采用循环的形式去结果集中的数据
		for(result.moveToFirst();!result.isAfterLast();result.moveToNext()){
			all.add(result.getInt(0)+"====="+result.getString(1)+"====="+result.getString(2));
		}
		result.close();
		db.close();
		return all;
	}

**·**使用ListView来实现滑动分页

Android中有这样一个操作,只需要手指进行上下滑动,屏幕自动滚动,到达最后一条数据的时候,提示:请稍等,数据正在加载......。这个操作需要使用SimpleAdapter来完成,因为在这个适配器中有下面这样一个方法。

java 复制代码
pulibc void notifyDataSetChanged()

表示如果在SimpleAdapter中的填充数据集合list内容一旦发生变化,就会立刻通知ListView进行及时新数据加载,当数据加载底部的时候,需要提示一个信息,而这个信息可以通过ListView组件里的方法去添加

java 复制代码
public void addFooterView(View v)

现在还剩下一个问题,需要去监听滚动事件,需要使用如下接口:

首先顶一个ListView的布局模板。

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mylayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <TableRow >
        <TextView 
            android:id="@+id/id"
            android:layout_width="50px"
            android:layout_height="wrap_content"
            android:textSize="30px"
        />
        <TextView 
            android:id="@+id/name"
            android:layout_width="120px"
            android:layout_height="wrap_content"
            android:textSize="30px"
        />
        <TextView 
            android:id="@+id/birthday"
            android:layout_width="130px"
            android:layout_height="wrap_content"
            android:textSize="30px"
        />
    </TableRow>

</TableLayout>

现在我们需进行查询操作,需要返回一共有多少笔数据,修改一下查询的类

java 复制代码
package com.example.sqliteproject;

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 DH10Cursor {
      private static final String TABLENAME="DH10";
      private SQLiteDatabase db =null;
	public DH10Cursor(SQLiteDatabase db) {
		super();
		this.db = db;
	}
    
	public List<Map<String,Object>> find(int currentPage,int lineSize){
        List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
		String sql = "select id,name,birthday from "+TABLENAME+" limit ?,?";
		String args[] = new String[]{String.valueOf((currentPage-1)*lineSize),String.valueOf(currentPage*lineSize)};
		Cursor result = this.db.rawQuery(sql, args);
		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));
			list.add(map);
		}
		return list;
	}
	
	public int getCount(){
		int count = 0;
		String sql = "select count(id) from "+TABLENAME;
		Cursor result  = this.db.rawQuery(sql, null);
		for(result.moveToFirst();!result.isAfterLast();result.moveToNext()){
			count = result.getInt(0);//取得数据的总笔数
		}
		return count;
	}
}

下面写一下主布局文件,这个比较简单,直接放一个线性布局管理器就可以了。

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

</LinearLayout>

现在只剩下Activity

java 复制代码
package com.example.sqliteproject;

import java.util.List;
import java.util.Map;

import android.app.Activity;
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;

import com.example.sqliteopenhelper.MySQLiteOpenHelper;

public class SQLiteActivity extends Activity {
	MySQLiteOpenHelper helper=null;
	ListView listView = null;
	LinearLayout mainLayout = null;
	int currentPage = 1;
	int lineSize = 5;
	int count = 0;
	int pageSize = 1;//总页数
	int lastItem = 0;//保存最后一个记录点
	SimpleAdapter adapter = null;
	TextView tv = null;//底部信息
	LinearLayout loadLayout = null;//底部提示框布局
	List<Map<String,Object>> list = null;
	LayoutParams lp= new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
	@Override
	protected void onCreate(Bundle savedInstanceState) {
	       super.onCreate(savedInstanceState);  
	       super.setContentView(R.layout.listview_main);
	       this.mainLayout=(LinearLayout) super.findViewById(R.id.mainlayout);
	       this.loadLayout=new LinearLayout(this);
	       this.tv = new TextView(this);
	       tv.setText("数据加载中,请稍后。。。");
	       tv.setGravity(Gravity.CENTER);
	       tv.setTextSize(30);
	       loadLayout.addView(this.tv,this.lp);
	       loadLayout.setGravity(Gravity.CENTER);
	       this.showAllData();
	}

    private void showAllData(){
    	this.helper = new MySQLiteOpenHelper(this);
    	this.listView = new ListView(this);
    	DH10Cursor cursor = new DH10Cursor(this.helper.getWritableDatabase());//获得一个查询操作对象
    	this.count = cursor.getCount();//取得总数据笔数
    	this.list = cursor.find(currentPage, lineSize);
    	this.adapter = new SimpleAdapter(this, list, R.layout.listview_item_layout, 
    			new String[]{"id","name","birthday"},new int[]{R.id.id,R.id.name,R.id.birthday});//实例化适配器对象
    	SQLiteActivity.this.listView.addFooterView(SQLiteActivity.this.loadLayout);
    	this.listView.setAdapter(adapter);
    	this.listView.setOnScrollListener(new OnScrollListener() {
			
			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				if(SQLiteActivity.this.lastItem==SQLiteActivity.this.adapter.getCount()//表示当前记录已经在最底部
						&&SQLiteActivity.this.currentPage<SQLiteActivity.this.pageSize//当前页要小于总页数
						&&scrollState==OnScrollListener.SCROLL_STATE_IDLE//屏幕不再滚动
						){
					SQLiteActivity.this.currentPage++;
					SQLiteActivity.this.listView.setSelection(SQLiteActivity.this.lastItem);//设置显示位置
					SQLiteActivity.this.listView.addFooterView(SQLiteActivity.this.loadLayout);
					SQLiteActivity.this.appendData();
				}
				
			}
			
			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				SQLiteActivity.this.lastItem = firstVisibleItem+visibleItemCount-1;
			}
		});
    	mainLayout.addView(this.listView);
    	if(this.count%this.lineSize==0){
    		this.pageSize = this.count/this.lineSize;
    	}else{
    		this.pageSize = this.count/this.lineSize+1;
    	}
    }
    private void appendData(){//更新数据方法
    	DH10Cursor cursor = new DH10Cursor(this.helper.getWritableDatabase());
    	List<Map<String,Object>> newData = cursor.find(currentPage, lineSize);
    	this.list.addAll(newData);
    	this.adapter.notifyDataSetChanged();//适配器重新加载集合数据
    }
}

以上就是整个实现下滑屏幕实现分页加载数据的程序。

事务的处理是针对数据库而已,以后的开发当中,只要针对增删改,都需要用事务进行处理。

java 复制代码
public void insert(String name,String birthday){
		SQLiteDatabase db = super.getWritableDatabase();//取得数据库操作对象
		db.beginTransaction();//开启事务
		try{
		ContentValues cv = new ContentValues();
		cv.put("name", name);
		cv.put("birthday", birthday);
		db.insert(TABLENAME, null, cv);//进行新增操作
		db.setTransactionSuccessful();//正确执行,否则回滚
		}catch(Exception e){
			db.endTransaction();//事务关闭
			db.close();
		}
		
	}

1.3 小结

(1)SQLite数据库是一个专门用于嵌入式设备的数据库;

(2)SQLite支持SQL语句的操作;

(3)可以使用SQLiteOpenHelper类完成数据库的操作;

(4)所有的查询数据使用Cursor进行接收;

相关推荐
子非衣3 小时前
MySQL修改JSON格式数据示例
android·mysql·json
openinstall全渠道统计6 小时前
免填邀请码工具:赋能六大核心场景,重构App增长新模型
android·ios·harmonyos
双鱼大猫7 小时前
一句话说透Android里面的ServiceManager的注册服务
android
双鱼大猫7 小时前
一句话说透Android里面的查找服务
android
双鱼大猫7 小时前
一句话说透Android里面的SystemServer进程的作用
android
双鱼大猫7 小时前
一句话说透Android里面的View的绘制流程和实现原理
android
双鱼大猫8 小时前
一句话说透Android里面的Window的内部机制
android
双鱼大猫8 小时前
一句话说透Android里面的为什么要设计Window?
android
双鱼大猫8 小时前
一句话说透Android里面的主线程创建时机,frameworks层面分析
android
苏金标9 小时前
android 快速定位当前页面
android