架构表示数据库如何组织的正式声明,它体现于您用于创建数据库的SQL
语句,它有助于创建伴随类,即契约类,契约类是用于定义URI
、表格和列名称的常数的容器。契约类允许您跨同一软件包中的所有其他类使用相同的常数。 您可以在一个位置更改列名称并使其在您整个代码中传播。以下是一个契约类:
package cn.zhouchaoyuan.dao;
import android.provider.BaseColumns;
public final class BookReaderContract {
/*不经意实例化的时候可以条用此构造器*/
public BookReaderContract() {}
/*实现BaseColumns接口,继承已有主键字段_ID*/
public static abstract class BookEntry implements BaseColumns {
public static final String TABLE_NAME = "Book";
public static final String COLUMN_NAME_AUHOR = "author";
public static final String COLUMN_NAME_NAME = "name";
public static final String COLUMN_NAME_PRICE = "price";
public static final String COLUMN_NAME_PAGES = "pages";
}
}
典型的创建数据的语句实例:
package cn.zhouchaoyuan.dao;
import static cn.zhouchaoyuan.dao.BookReaderContract.BookEntry.*;
public class SQLClause {
/*静态导入*/
public static final String REAL_TYPE = " real";
public static final String TEXT_TYPE = " text";
public static final String COMMA_SEP = ",";
public static final String INTEGER_TYPE = " integer";
public static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + TABLE_NAME + " (" +
_ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +
COLUMN_NAME_AUHOR + TEXT_TYPE + COMMA_SEP +
COLUMN_NAME_NAME + TEXT_TYPE + COMMA_SEP +
COLUMN_NAME_PRICE + REAL_TYPE + COMMA_SEP +
COLUMN_NAME_PAGES + INTEGER_TYPE +
" )";
public static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + TABLE_NAME;
}
就像您在设备的内部存储中保存文件那样,Android
将您的数据库保存在私人磁盘空间,即关联的应用。您的数据是安全的,因为在默认情况下,其他应用无法访问此区域。
为了管理Android
里面的数据库,系统提供我们一个SQLiteOpenHelper
,这是一个抽象类,我们需要自己实现子类并且重写抽象方法public void onCreate(SQLiteDatabase db)
和public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
。然后通过getReadableDatabase()
或者getWritableDatabase()
获得一个SQLiteDatabase
,当然如果存在了要创建的数据库名字,那么public void onCreate(SQLiteDatabase db)
不会执行。实例如下:
package cn.zhouchaoyuan.dao;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import static cn.zhouchaoyuan.dao.SQLClause.*;
public class BookReaderHelper extends SQLiteOpenHelper{
/*静态导入*/
private Context context;
public BookReaderHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.context = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
//当version版本不同时,会回调这个方法
db.execSQL(SQL_DELETE_ENTRIES);
onCreate(db);
}
}
调用下面的语句,onCreate
被执行(BookStore.db
还不存在):
BookReaderHelper brHelper = new BookReaderHelper(this,"BookStore.db",null,1);
brHelper.getReadableDatabase();
这个时候我们不知道是否真的创建了数据库,我们来到DDMS
的File Explorer
查看data/data/cn.zhouchaoyuan.firstapplication/databases
确实创建了一个BookStore.db
。如下图:
另外我们可以通过adb shell
操作数据库,将C:\Users\chaoyuan\AppData\Local\Android\sdk\platform-tools
(我的电脑环境)配置到环境变量中,在dos中键入adb shell
,然后进入data/data/cn.zhouchaoyuan.firstapplication/databases
目录,如下:
可以看到sdk\platform-tools
目录下有sqlite3
的命令,直接在shell
键入sqlite3加数据库名字
即可操作指定数据库。
使用
.table
可以显示所有的表,.schema
可以显示数据库的建表语句。
因为onCreate
这个方法只会被执行一次,所以我们可以通过数据库的版本信息来升级数据库,调用onUpgrade
实现数据库的更新。
添加数据的方法原型:public long insert (String table, String nullColumnHack, ContentValues values)
table
要插入表名nullColumnHack
指定在ContentValues
为空的情况下框架可在其中插入 NULL 的列的名称(如果您将其设置为 "null", 那么框架将不会在没有值时插入行。)values
要插入的数据,以键值对形式保存,键必须和列名相同
根据上面所建的表,编写一个示例插入方法:
public void insert(SQLiteDatabase db){/*静态导入了常量*/
ContentValues contentValues = new ContentValues();
contentValues.put(_ID,1);
contentValues.put(COLUMN_NAME_AUHOR,AUTHOR);
contentValues.put(COLUMN_NAME_NAME,"The Plan B");
contentValues.put(COLUMN_NAME_PAGES,120);
contentValues.put(COLUMN_NAME_PRICE,23.33);
db.insert(TABLE_NAME,null,contentValues);
}
更新数据的方法原型:public int update (String table, ContentValues values, String whereClause, String[] whereArgs)
table
要更新的表名values
要插入的数据,以键值对形式保存,键必须和列名相同,null
是允许的whereClause
where
语句的列名,null
话会更新所有行whereArgs
where
语句的列名的值,一次对应
根据上面所建的表,编写一个示例更新方法:
public void update(SQLiteDatabase db){/*静态导入了常量*/
ContentValues contentValues = new ContentValues();
contentValues.put(COLUMN_NAME_PRICE,6.666);
db.update(TABLE_NAME,contentValues,"_ID = ?",new String[]{"1"});
}
和查询信息一样,删除数据同样需要提供一些删除标准。DB的API提供了一个防止SQL注入的机制来创建查询与删除标准。删除数据的方法原型:public int delete (String table, String whereClause, String[] whereArgs)
table
要更新的表名whereClause
可选的where
语句whereArgs
whereClause
语句中表达式的?占位参数列表
SQL Injection:(程序员在编写代码时没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入)
根据上面所建的表,编写一个示例删除方法:
public void delete(SQLiteDatabase db){/*静态导入了常量*/
db.delete(TABLE_NAME, COLUMN_NAME_PAGES + ">= ?", new String[]{"120"});
}
最简单的查询数据的方法原型:query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
table
要查询的表名columns
想要显示的列,若为空则返回所有列,不建议设置为空,如果不是返回所有列selection
where
子句,声明要返回的行的要求,如果为空则返回表的所有行selectionArgs
where
子句对应的条件值groupBy
分组方式,若为空则不分组having
having
条件,若为空则返回全部(不建议)orderBy
排序方式,为空则为默认排序方式
public void query(SQLiteDatabase db){/*静态导入了常量*/
String[] columns = {
COLUMN_NAME_NAME,
COLUMN_NAME_AUHOR
};
String sortOrder = _ID + " DESC";
Cursor c = db.query(
TABLE_NAME, // The table to query
columns, // The columns to return
null, // The columns for the WHERE clause
null, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
sortOrder // The sort order
);
if(c.moveToFirst()){
do {
String name = c.getString(c.getColumnIndex(COLUMN_NAME_NAME));
String author = c.getString(c.getColumnIndex(COLUMN_NAME_AUHOR));
Log.e("book name: ",name);
Log.e("author: ",author);
//Toast.makeText(this,"book: "+name+",author: "+author,Toast.LENGTH_SHORT).show();
}while(c.moveToNext());
}
}
虽然Android提供了非常多的API来实现增删改查,我们也可以通过直接书写几乎是SQL语句的来答案我们的目的,如下:
db.execSQL("insert into Book(_ID,author,name,price,pages) values(?,?,?,?,?)",new String[]{"1207020203","zcy","The Plan C","12.34","233"});
db.execSQL("updata Book set price = ? where name = ?",new String[]{"12.34","The Plan C"});
db.execSQL("delete from Book where pages >= ?",new String[]{"233"});
db.rawQuery("select * from Book",null);