SELECT password FROM user_accounts WHERE username ='John';
SQLite is an embedded relational database engine. Its developers call it a self-contained, serverless, zero-configuration and transactional SQL database engine. 其实只需要记住SQLite不需要数据库服务器,不需要额外配置,其数据库本身就是一个文件的一个小型数据库。 通常情况下在android中创建数据库,系统会在"DATA/data/APP_NAME/databases".目录为我们创建数据库文件。
SQLite支持的数据类型有很多,但他们总共可以分为以下几类:
desc<table>//查看表结构
select*from<table>//查询所有更
select , fromtable ;//查看指定列
selectdistinct , fromtable ;//非重复查询
insertinto users(_id,username,password) select*from users;//复制
select username from users where username like'S%' ;//非重名字首字母为大写S的用户
select username from users where username like'__S%' ;//非重名字第三个字母为大写S的用户
select*from users where _id in(001,220,230);
select*fromuserorderby _id;//以id的顺序排列
select*fromuserorderby _id desc;//以id反的顺序排
常用Select语句
desc <table>//查看表结构
select * from <table>//查询所有更
select , from table ;//查看指定列
select distinct , from table ;//非重复查询
insert into users(_id,username,password) select * from users;//复制
select username from users where username like 'S%' ;//非重名字首字母为大写S的用户
select username from users where username like '__S%' ;//非重名字第三个字母为大写S的用户
select * from users where _id in(001,220,230);
select * from user order by _id;//以id的顺序排列
select * from user order by _id desc;//以id反的顺序排
很少有人直接在程序当中直接编写SQL语句,麻烦且容易出错。所以最好应该先在一个图形化的环境把SQL语句写好,测试好再加入到代码当中。 而且,SQLite数据库本身就是一个独立的文件,很容易从模拟器上抓取出来。可以使用图形化界面进行分析,和修改 etc.
SQLite expert 是个简单小巧的SQLite数据库管理软件,有免费版和,收费版两种,一般来讲免费版足以应付我们需求。
sqliteexpert官方网址
软件界面截图
下面的例子,我们创建一张表用来存储用户的用户名和密码;数据库名"users",表名"user_accounts",有三个列:row_id INTEGER自增类型的主键,usernameTEXT类型 用来保存用户名,passwordTEXT类型,用来保存用户密码;
这时我们便进入了"设计状态",填写表名(user_accounts),点击"Add",在Name中填写"row_id",Type选择Integer,如下图所示:
因为row_id这个列比较特殊是我们的索引的主键,所以还要点击"Index",之后会看到下图:
左边的Available Fields 中有我们刚刚创建的row_id,点击"Add"把row_id加入右边的Index Fields当中。这时上面的Primary和AutoIncrement会变成可先状态 ,勾选Primary和AutoIncrement点击OK;
再回到Field当中创建其它两列,分别为username类型为TEXT,password类型也为TEXT,但不需要创建Index; 完成之后点击Apply,这样我们便使用SQLite expert创建一张表格,点击DDL,我们可以看到SQLite expert已经为我们生成好了生成这张表格所需的SQL语句:
在android中需要代码生成表格时,只要把这段代码复制过去,就可以了。
INSERTINTO user_accounts(row_id,username,password) VALUES(001,'John','abcdef');
点击Data会发现数据库里边增加了一个用户名为John的用户。为了练习,我们不妨再增加两个用户,David和Sarah
INSERTINTO user_accounts(row_id,username,password) VALUES(002,'David','123456');
INSERTINTO user_accounts(row_id,username,password) VALUES(003,'Sarah','00000000');
DELETEFROM user_accounts WHERE username ='David';
update user_accounts SET password='666666'WHERE username ='Sarah';
SELECT* FROMM user_accounts;
一般来讲select * 的语句只在测试的时候时候,在正式代码中不推荐使用。
SELECT username,password FROM user_accounts;
这时个就发现row_id列没有显示出来。
SELECT password FROM user_accounts WHERE username ='John';
以下流程图,来源于SQLite官方文档
SELECTcount(word) ASnumberFROM cet6_table;
count()函数为我们返回一个Int整形,代表有多少行数据。返回的列的名字叫count(word),为了方便阅读和处理用as number给这个列取个 别名number;
SELECT[word],[explanation]FROM cet6_table ORDERBY word LIMIT 100 OFFSET 200"
上语句,返回自第200行的最多100条数据。分页时我们只要修改offset 后面的数据即可取得对应页面的数据。
详情见示例程序!
publicvoid init(Context context) {
try {
String databasePath = Environment.getExternalStorageDirectory().getAbsolutePath();
String databaseFilename = databasePath +"/"+ DATABASE_FILENAME;
File dir =new File(databasePath);
if (!dir.exists()){
dir.mkdir();
}
if (!(new File(databaseFilename)).exists()) {
InputStream is = context.getResources().openRawResource(
R.raw.cet6);
FileOutputStream fos =new FileOutputStream(databaseFilename);
byte[] buffer =newbyte[1024];
int count =0;
while ((count = is.read(buffer)) >0) {
fos.write(buffer, 0, count);
}
fos.flush();
fos.close();
is.close();
}
mCet6Db = SQLiteDatabase.openOrCreateDatabase(databaseFilename,
null);
} catch (Exception e) {
e.printStackTrace();
}
}
%d dayofmonth: 00
%f fractional seconds: SS.SSS
%H hour: 00-24
%j dayofyear: 001-366
%J Julian daynumber
%m month: 01-12
%M minute: 00-59
%s seconds since 1970-01-01
%S seconds: 00-59
%w dayof week 0-6with Sunday==0
%W week ofyear: 00-53
%Y year: 0000-9999
%%%
SQLite支持以下格式的日期:
YYYY-MM-DD
YYYY-MM-DD HH:MM
YYYY-MM-DD HH:MM:SS
YYYY-MM-DD HH:MM:SS.SSS
YYYY-MM-DDTHH:MM //T 就是字母T,用于分开日期和时间
YYYY-MM-DDTHH:MM:SS
YYYY-MM-DDTHH:MM:SS.SSS
HH:MM
HH:MM:SS
HH:MM:SS.SSS
now
DDDDDDDDDD //is the Julian daynumber expressed as a floating point value.
strftime('%Y-%m-%d', column_name)方法可以很方便的对日期进行格式化,而不用Java对String进行再处理。
ContentValues 不支持直接把datetime('now')之类函数作为值插入数据库。 比如想把当前系统时间插件数据库使用ContentValues:
SimpleDateFormat dateFormat =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date =new Date();
ContentValues initialValues =new ContentValues();
initialValues.put("date_created", dateFormat.format(date));
long rowId = mDb.insert(DATABASE_TABLE, null, initialValues);
或者如附带程序记事本程序一样使用exeSQL(sql);
上流程图来自O'REILLY 视频公开课"Developing Android Applications with Java"中的视频截图
SqliteOpenHelper是android框架为我们提供的一个数据库助手类。对于数据库的操作一般都是从这个类继承。它主要负责数据库的创建,打开,更新 等一些操作。
publicclass DbAccess {
class DbHelper extends SQLiteOpenHelper {
//此处代码被省略
}
privatestatic DB_NAME ="users";
private mDbHelper =null;
privatestatic mDbAccess;//to make sure there is only one instance exist;
private DbAccess(Context context){
//private constructer;
if(mDbHelper ==null){
mDbHelper =new DbHelper(context,DB_NAME);
}
}
public DbAccess getInstance(Context context){
if(mDbAccess ==null){
mDbAccess =new DbAccess(context);
}
return mDbAccess;
}
//部分代码被省略
//insert
//delete
//update
//query
}
publicclass ActivityA extends Activity{
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dao = DbAccess.getInstance(this);
////部分代码被省略
}
////部分代码被省略
}
程序的流程应该是这样的:
假设ActivityA创建了Dao的对象,又开启了ServiceA,A使用了一段时间再跳转到了ActivityB与B再接着使用同一个Dao对象。ActivityA创建Dao对象的时候 把自己的引用向上转型成一个Context对象,Dao又用这个context创建了SQLiteOpenHelper,所以SQLiteOpenHelper就持有了ActivityA的引用。当ActivityA被销毁 之后因为单例的Dao保留有ActivityA的引用,所以ActivityA不能被GC所回收,造成整个Acitivity内存的泄漏。
SQLiteDatabase db =null;
try {
mIsInitializing =true;
String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
if (db.getVersion() != mNewVersion) {
thrownew SQLiteException("Can't upgrade read-only database from version "+
db.getVersion() +" to "+ mNewVersion +": "+ path);
}
onOpen(db);
Log.w(TAG, "Opened "+ mName +" in read-only mode");
mDatabase = db;
所以我们可以自己仿照SQLiteOpenHelper写一个不需要Context的助手类,大constructer当中把路径传进行,而不是一个context对象。缺点:需要考虑多线程同步等 很多因素,有一定的复杂度。
4,在创建Dao对象时,不使用Activity的引用作为Context,而是把Application作为Context传入Dao的构造函数中进行创建。优点,不会影响程序的启动时间。 缺点:对于数据库的关闭还是不确定。
5,在第四种方案的基础上加入计数器。第调用getInstance计数器自增,再加入一个release()方法,计数器自减,当计数器为0时,关闭数据库(或者当计数器为零且 过一段时间没有连接时关闭)。
如果有什么疑问,发现bug或者有更好的想法或者建议,或者附件无法下载。请发邮件至[email protected]
1,记事本,简单CRUID练习,以及日期处理。
2,分页显示和简单六级词汇查询(数据库来自"百度文库" )