Android中使用android.database.sqlite.SQLiteDatabase来表示一个数据库对象,它提供了两种模式来帮助开发者进行增删改查等基本数据库操作。
//利用sql查询数据 Cursor data = db.rawQuery("select id,name from table"); //利用sql插入数据 db.execSql("insert into contacts (id,name) values (2,'cpacm')");稍微学过sql语句的人应该都看的懂上面的代码(其实看语句的意思也能知道个大概~)
//结构化的方式查询数据 Cursor data = db.query("contacts",new String[]{"id","name"},null,null,null,null,null); //结构化方式插入数据 ContentValue values = new ContentValues(); values.put("id",2); values.put("name","cpacm"); db.insert("table",null,values);
/** * 参数说明 * table:数据表名,columns:需要显示的列名,如果为null则相当与* * selection:相当于sql语句的where条件;selectionArgs数组放的是where条件要替换的?号 * groupBy:SQL语句的Group, orderBy: 排序,默认asc **/ public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy){
}
比如说我要查询的SQL语句为
SELECT CustomerName, SUM(OrderPrice) FROM Orders WHERE Country=? GROUP BY CustomerName HAVING SUM(OrderPrice)>500 ORDER BY CustomerName
那么我写的代码如下
//数据表名 String table = "Orders" ; //要显示的列名 String[] columns = new String[] { "CustomerName" , "SUM(OrderPrice)" }; //选择条件 String selection = "Country=?" ; //里面的变量对应条件中的问号,多个的时候请一一入座。 String[] selectionArgs = new String[]{ "China" }; //分组名 String groupBy = "CustomerName" ; //分组的条件 String having = "SUM(OrderPrice)>500" ; //按字段排序 String orderBy = "CustomerName" ; Cursor c = db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy, null );
这样就能实现数据库的查询了。其它的语句参数都是差不多的,这里就不一一介绍了。
public long insert (String table, String nullColumnHack, ContentValues values)
public int delete(String table, String whereClause, String[] whereArgs)
public int update(String table, ContentValues values, String whereClause, String[] whereArgs)
课外小知识:关于GroupBy和Having的使用
group by 顾名思义就是按照xxx进行分组,它必须有“聚合函数”来配合才能使用,使用时至少需要一个分组标识字段。聚合函数有:sum()、count()、avg()等,使用group by目的就是要将数据分组进行汇总操作。比如上面sql语句的CustomerName,如果它有四个行{“张三”,“李四”,“张三”,“李四”},那么此时就会分成两组,分别为张三组和李四组,然后统计出他们使用的orderprice总和。
HAVING作用就是为每一个组指定条件,像where指定条件一样,也就是说,可以根据你指定的条件来选择行。如果你要使用HAVING子句的话,它必须处在GROUP BY子句之后。还是上面的SQL语句,如果张三的SUM(OrderPrice)没有超过500,那么张三组就不会显示。
//编译复杂的SQL语句 SQLiteStatement compiledSql = db.compileStatement(aSQL); //执行SQL compiledSql.execute();
除此以外,Android还提供了丰富的高级数据库功能,比如支持触发器、支持复合索引以及支持对数据库事务的处理。
try{ db.beginTransaction(); //执行相关的数据库操作,如有异常,直接进入finally部分。 }finally{ //不论成功都要调用endTransaction来结束事务 db.endTransaction(); }
课外小知识:所谓事务是用户定义的一个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。例如,在关系数据库中,一个事务可以是一条SQL语句、一组SQL语句或整个程序。 简单举个例子就是你要同时修改数据库中两个不同表的时候,如果它们不是一个事务的话,当第一个表修改完,可是第二表改修出现了异常而没能修改的情况下,就只有第二个表回到未修改之前的状态,而第一个表已经被修改完毕。 而当你把它们设定为一个事务的时候,当第一个表修改完,可是第二表改修出现了异常而没能修改的情况下,第一个表和第二个表都要回到未修改的状态!这就是所谓的事务回滚。
package com.example.notebook; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase.CursorFactory; public class DBHelper extends SQLiteOpenHelper{ private static final int VERSION=1; /** * 在SQLiteOpenHelper的子类当中,必须有该构造函数 * @param context 上下文对象 * @param name 数据库名称 * @param factory * @param version 当前数据库的版本,值必须是整数并且是递增的状态 */ public DBHelper(Context context,String name,CursorFactory factory,int version){ super(context,name,factory,version); } public DBHelper(Context context, String name, int version){ this(context,name,null,version); } public DBHelper(Context context, String name){ this(context,name,VERSION); } @Override public void onCreate(SQLiteDatabase db) { // 数据库首次构造时,会调用该函数,可以在这里构造表、索引,等等 System.out.println("create a database"); //execSQL用于执行SQL语句 db.execSQL("create table notebook(_id integer primary key autoincrement,pic varchar(50),title varchar(20),content text,time varchar)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 如果给定的当前数据库版本高于已有数据库版本,调用该函数 System.out.println("upgrade a database"); } }
package com.example.notebook; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.util.Log; public class DBManager { private Context mContext = null; private SQLiteDatabase mSQLiteDatabase = null;//用于操作数据库的对象 private DBHelper dh = null;//用于创建数据库的对象 private String dbName = "note.db";//数据库的名称 private int dbVersion = 1;//数据库的版本 public DBManager(Context context){ mContext = context; } public void open(){ try{ dh = new DBHelper(mContext, dbName, null, dbVersion);//建立数据库 if(dh == null){ Log.v("msg", "is null"); return ; } mSQLiteDatabase = dh.getWritableDatabase();//以可写方式打开数据库 //dh.onOpen(mSQLiteDatabase); }catch(SQLiteException se){ se.printStackTrace(); } } public void close(){ mSQLiteDatabase.close();//关闭数据库 dh.close(); } public Cursor selectAll(){ Cursor cursor = null; try{ //sql语句操作 String sql = "select * from notebook"; cursor = mSQLiteDatabase.rawQuery(sql, null); }catch(Exception ex){ ex.printStackTrace(); cursor = null; } return cursor; } public Cursor selectById(int id){ //String result[] = {}; Cursor cursor = null; try{ //sql语句操作 String sql = "select * from notebook where _id='" + id +"'"; cursor = mSQLiteDatabase.rawQuery(sql, null); }catch(Exception ex){ ex.printStackTrace(); cursor = null; } return cursor; } public long insert(String title, String content,String pic){ long datetime = System.currentTimeMillis(); long l = -1; try{ //结构化方式操作 ContentValues cv = new ContentValues(); cv.put("title", title); cv.put("content", content); cv.put("time", datetime); cv.put("pic", pic); l = mSQLiteDatabase.insert("notebook", null, cv); // Log.v("datetime", datetime+""+l); }catch(Exception ex){ ex.printStackTrace(); l = -1; } return l; } public int delete(int id){ int affect = 0; try{ //结构化方式操作 affect = mSQLiteDatabase.delete("notebook", "_id=?", new String[]{String.valueOf(id)}); }catch(Exception ex){ ex.printStackTrace(); affect = -1; } return affect; } public int update(int id, String title, String content,String pic){ int affect = 0; try{ //结构化方式操作 ContentValues cv = new ContentValues(); cv.put("title", title); cv.put("content", content); cv.put("pic", pic); String w[] = {String.valueOf(id)}; affect = mSQLiteDatabase.update("notebook", cv, "_id=?", w); }catch(Exception ex){ ex.printStackTrace(); affect = -1; } return affect; } }
获取数据示例
private DBManager dm = null;// 数据库管理对象
private Cursor cursor = null;
dm = new DBManager(this);//数据库操作对象 dm.open();//打开数据库操作对象 cursor = dm.selectAll();//获取所有数据 cursor.moveToFirst();//将游标移动到第一条数据,使用前必须调用 int count = cursor.getCount();//个数 ArrayList<String> contents = new ArrayList<String>();//图片的所有集合 ArrayList<String> imgs = new ArrayList<String>();//图片的所有集合 ArrayList<String> items = new ArrayList<String>();//标题的所有集合 ArrayList<String> times = new ArrayList<String>();//时间的所有集合 for(int i= 0; i < count; i++){ contents.add(cursor.getString(cursor.getColumnIndex("content"))); imgs.add(cursor.getString(cursor.getColumnIndex("pic"))); items.add(cursor.getString(cursor.getColumnIndex("title"))); times.add(cursor.getString(cursor.getColumnIndex("time")));
//cursor.getInt(cursor.getColumnIndex("_id")) cursor.moveToNext();//将游标指向下一个 } dm.close();//关闭数据操作对象
本质上而言,云端存储就是通过网络将移动设备上的数据存储到远端服务器上。在Android中,增加了一些辅助功能,使得整个流程的实现变得更为简单。首先是通过Google账号来标识用户身份。在android中,默认支持使用Google账号作为用户身份的标识,系统上各个应用都可以通过账号系统获得用户的登录信息。其次,有了Google账号,使得开发者不需要自行构建后台服务系统。
Android的云端数据存取由系统服务BackupManagerService来统一管理。当应用提交备份数据请求时,BackupManagerService会将该请求放入备份队列中,该队列会按照一定的控制逻辑定时提交到云端。当有新应用安装到系统时,会触发数据恢复事件,BackupManagerService会凭借应用包名和用户账号从云端取出相应的备份数据,尝试恢复。
在实践中,Android会构造一个派生自BackupAgent类的子类android.app.backup.BackupAgentHelper的对象,来更方便地构建云端存储组件。
import java.io.File; import java.io.IOException; import android.app.backup.BackupAgentHelper; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.FileBackupHelper; import android.os.ParcelFileDescriptor; public class MyBackupAgent extends BackupAgentHelper { private static final String KEY = "my_backup"; @Override public void onCreate() { //构造文件读写对象,声明需要备份的文件 FileBackupHelper helper = new FileBackupHelper(this,"backup_file"); addHelper(KEY,helper); super.onCreate(); } @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException { //调用父类方法,提交整个文件到云端 super.onBackup(oldState, data, newState); } @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { // 调用父类方法,将从云端获取的文件覆盖本地文件 super.onRestore(data, appVersionCode, newState); } @Override public void onRestoreFile(ParcelFileDescriptor data, long size, File destination, int type, long mode, long mtime) throws IOException { // TODO Auto-generated method stub super.onRestoreFile(data, size, destination, type, mode, mtime); }
Android不会自行将数据提交到云端,开发者需要显性调用android.app.backup.BackupManager的dataChanged函数来触发。
和所有组件一样,云端存储组件是由系统进行托管的。这就需要把组件的相关信息放入配置文件中。
<application android:backupAgent = "MyBackupAgent" ...>
到这里,数据的部分就讲解的差不多了,剩下的就要回到我们的数据源组件ContentProvider了。数据这一块对于开发者来说至关重要,怎样使用,如何做最有效率,这些问题也是我们技术员要一直研究的重点。 参考文章:Android 的Backup服务管理机制与架构分析 http://blog.csdn.net/goohong/article/details/8026045