目录
- 介绍
- SQLite常用的三个类介绍
- SQLiteOpenHelper类的使用
- public void onCreate
- public void onUpgrade
- 使用SQLite操作数据库方法介绍
- API的使用
- 原生SQL的使用
- 区分何时使用API何时使用原生SQL
- SQLite数据库管理
- Windows下的SQLite管理工具
- MAC下的SQLite管理工具
- 课程目标
- 前端代码
- 后端代码
- UserBean.java
- SQLite的核心操作-DBAdapter.java
- MainActivity.java
- 运行效果
介绍
Android内带SQLite内嵌式数据库了。这对于我们存储一些更复杂的结构化数据带来了极大的便利。比如说我们要存储应用内的常用联系人,购物车暂存信息,常量。必竟从xml或者是json里取数据都没有一条Select语句来得简单。
SQLite常用有五种数据类型:
- NULL
- INTEGER
- REAL(浮点数)
- TEXT(字符串文本)
- BLOB(二进制对象)
虽然只有五种,但是对于varchar,char等其他数据类型都是可以保存的,如下create table语句依然是可以生效的:
create table user(name varchar())
SQLite常用的三个类介绍
- SQLiteOpenHelper:抽象类,我们通过继承该类,然后重写数据库创建以及更新的方法, 我们还可以通过该类的对象获得数据库实例,或者关闭数据库;
- SQLiteDatabase:数据库访问类:我们可以通过该类的对象来对数据库做一些增删改查的操作;
- Cursor:游标,有点类似于JDBC里的resultset,结果集!可以简单理解为指向数据库中某 一个记录的指针;
这三个类我们直接来看下面的Sample代码各位就知道是什么样的组合应用了。
private SQLiteDatabase db; | |
private Context context = null; | |
private DBOpenHelper dbOpenHelper; | |
private String DB_NAME="user.db"; | |
private static final String DB_TABLE = "t_user_login"; | |
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION); | |
try { | |
db = dbOpenHelper.getWritableDatabase(); | |
db.open(); | |
StringBuilder sqlStr = new StringBuilder(); | |
sqlStr.append("select loginId,password,user_name from ").append(DB_TABLE); | |
Cursor cur = db.rawQuery(sqlStr.toString(), null); | |
while (cur.moveToNext()) { | |
String loginId = cur.getString(cur.getColumnIndexOrThrow("loginId")); | |
String password = cur.getString(cur.getColumnIndexOrThrow("password")); | |
String userName = cur.getString(cur.getColumnIndexOrThrow("user_name")); | |
UserBean user = new UserBean(); | |
user.setLoginId(loginId); | |
user.setPassword(password); | |
user.setUserName(userName); | |
userList.add(user); | |
} | |
} catch (SQLiteException ex) { | |
Log.e(TAG, ">>>>>>open db error: " + ex.getMessage(), ex); | |
}finally { | |
try{ | |
db.close(); | |
}catch(Exception e){} | |
} |
使用上和JDBC几乎一样,此处的Cursor相当于JDBC里的ResultSet。
敲黑板重要提醒-Android中如何正确使用db.open()/db.close()
在我们的例子里我们在每一个业务方法操作都会使用db.open一下,在finally块里db.close一下。
同时要在Activity的onStop方法中去dbclose()掉它。
记得,它就和水龙头一样,用完就要关。
SQLiteOpenHelper类的使用
这个类通常我们都要新建一个其它的类来extends这个类才能使用,主要的是这个类里有这么两个方法是很有用的。
public void onCreate
方法全签名:public void onCreate(SQLiteDatabase db)
这个类的作用就是当SQLiteOpenHelper被实例化时,第一次用来做“初始化数据库”用的。比如说我们有一个类如下
private static class DBOpenHelper extends SQLiteOpenHelper {
然后当你实例化这个类时
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
它会自动触发这个onCreate方法,在onCreate方法中我们可以做表创建以及数据初始化操作如:
private static final String DB_CREATE = "create table " + | |
DB_TABLE + " (" + KEY_ID + " VARCHAR() primary key , " + | |
KEY_PASSWORD + " text not null, " + KEY_NAME + " text not null);"; | |
public void onCreate(SQLiteDatabase db) { | |
Log.i(TAG, ">>>>>>execute create table->" + DB_CREATE); | |
db.execSQL(DB_CREATE); | |
StringBuilder initDataSql = new StringBuilder(); | |
initDataSql.append("INSERT INTO ").append(DB_TABLE). | |
append("(").append(KEY_ID).append(",").append(KEY_PASSWORD). | |
append(",").append(KEY_NAME).append(")").append("values(?,?,?)"); | |
Log.i(TAG, ">>>>>>execute initDataSql->" + initDataSql.toString()); | |
db.execSQL(initDataSql.toString(), new String[]{"root", "", "root"}); | |
Log.i(TAG, ">>>>>>db init successfully"); | |
} |
public void onUpgrade
方法全签名:public void onUpgrade(SQLiteDatabase db, int _oldVersion, int _newVersion)
它有一个_newVersion这么一个参数,这个参数也很有意思。当你在实例化SQLiteOpenHelper类传入的DB_VERSION参数>就近一次实例化这个类传入的DB_VERSION参数时,它就会触发这个onUpgrade()方法,在这个方法里我们一般是根据业务场景的需要,没有一概而论该怎么办,通用的做法有:
- 重新执行一遍onCreate();此时所有的数据会被清空并初始化;
- 有些Android发版时不需要做全数据库清除,往往太过危险,而是会执行alter更改表结构、新建表、新插入-insert一些数据或者是稍带着update一些数据;
因此我才说,不一概而论而是需要依赖于你的实际业务场景来做操作。在我们的例子里我们会在onUpgrade里再执行一下onCreate()。
使用SQLite操作数据库方法介绍
这一块在使用上分两块。它类似Hibernate一样,也有API和原生SQL之分。
API的使用
即不需要书写SQL,如下样例:
public long addItem(UserBean user) throws Exception { | |
try { | |
ContentValues newValues = new ContentValues(); | |
newValues.put(KEY_ID, user.getLoginId()); | |
newValues.put(KEY_PASSWORD, user.getPassword()); | |
newValues.put(KEY_NAME, user.getUserName()); | |
Log.i(TAG, "addItem successfully with loginId->" + user.getLoginId() + " password->" + user.getPassword()); | |
return db.insert(DB_TABLE, null, newValues); | |
} catch (Exception e) { | |
Log.e(TAG, "addItem error: " + e.getMessage(), e); | |
throw new Exception("addItem error: " + e.getMessage(), e); | |
} | |
} |
我们可以看到全程没有使用SQL。
原生SQL的使用
public List<UserBean> queryAll() { | |
List<UserBean> userList = new ArrayList<UserBean>(); | |
try { | |
StringBuilder sqlStr = new StringBuilder(); | |
sqlStr.append("select loginId,password,user_name from ").append(DB_TABLE); | |
Cursor cur = db.rawQuery(sqlStr.toString(), null); | |
while (cur.moveToNext()) { | |
String loginId = cur.getString(cur.getColumnIndexOrThrow("loginId")); | |
String password = cur.getString(cur.getColumnIndexOrThrow("password")); | |
String userName = cur.getString(cur.getColumnIndexOrThrow("user_name")); | |
UserBean user = new UserBean(); | |
user.setLoginId(loginId); | |
user.setPassword(password); | |
user.setUserName(userName); | |
userList.add(user); | |
} | |
} catch (Exception e) { | |
Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e); | |
} | |
return userList; | |
} |
区分何时使用API何时使用原生SQL
如何区分呢?
答案很简单,以下是最佳实践 :
- 单表操作,就使用API好了;
- 跨表(>1个表)的操作或者是较复杂的逻辑如果使用API会产生循环套来套去不如写一条长点的SQL那么请直接使用原生SQL;
在样例前我们最后要了解一下,SQLite数据库管理工具。
SQLite数据库管理
Android里的SQLite一旦生成后它必须导出到AndroidStudio外部才能管理。按照如下步骤来导出SQLite数据库
先使用Device File Explorer打开data->data->你的包全路径下/databases,这里面有一个.db文件就是SQLite数据库文件。另一个.db-journal是Transaction日志,我们后面讲SQLite的Transaction时会讲到。
选择这个user.db右键选->save as,就可以导出到本地磁盘了。
然后你可以使用如下工具去管理SQLite数据库文件。
Windows下的SQLite管理工具
请使用SQLite Expert Professional,我使用的是5.4.4。不过这个是收费的,它长这个样但相当专业(收费的当然专业)。
MAC下的SQLite管理工具
请使用SQLiteManager,完全免费并且和Windows的SQLite Expert Professional收费的一样功能全。为什么呢?难道因为MAC贵。。。所以。。。有预收费之说:)?
好了,原理介绍完毕,我们要进入实践课程了。
课程目标
1.该APP启动会创建user.db数据库,新建一张t_user_login表并初始化一条root/111111数据进入内嵌SQLite数据库;
2.用户名密码输入root/111111点【登录】按钮可以得到登录成功,如果输入其它的不存在的帐户信息会以Toast显示“登录失败请校验你的用户名和密码”;
3.用户名密码输入任意值,点击【增加一条记录】按钮,可以插入一条数据到内嵌SQLite;
4.点击【查询所有记录】会以Log和Toast显示目前所有的相对应的表内的数据即:select *操作;
5.例子中对于单表的插入操作我们使用的是Android自带的SQLiteOpenHelper的原生API;
6.校验登录和查询所有记录我们用的是原生SQL+Cursor;
下面就进入代码部分吧。
前端代码
<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" | |
tools:context=".MainActivity"> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:orientation="vertical"> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="用户登陆" /> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginTop="dp" | |
android:text="请输入用户名" /> | |
<EditText | |
android:id="@+id/editLoginid" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:hint="用户名" /> | |
<TextView | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="请输入密码" /> | |
<EditText | |
android:id="@+id/editPassword" | |
android:layout_width="match_parent" | |
android:layout_height="wrap_content" | |
android:hint="密码" | |
android:inputType="textPassword" /> | |
<Button | |
android:id="@+id/buttonLogin" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="登录" /> | |
<View | |
android:layout_width="match_parent" | |
android:layout_height="dp" | |
android:background="#dfdfdf" /> | |
</LinearLayout> | |
<LinearLayout | |
android:layout_width="match_parent" | |
android:layout_height="match_parent"> | |
<Button | |
android:id="@+id/buttonAddItem" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginRight="dp" | |
android:text="增加一条记录"/> | |
<Button | |
android:id="@+id/buttonQueryAll" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_marginLeft="dp" | |
android:text="查询所有记录"/> | |
</LinearLayout> | |
</LinearLayout> |
后端代码
UserBean.java
package org.mk.android.demo.demosimplesqlite; | |
import java.io.Serializable; | |
public class UserBean implements Serializable { | |
public String getLoginId() { | |
return loginId; | |
} | |
public void setLoginId(String loginId) { | |
this.loginId = loginId; | |
} | |
private String loginId = ""; | |
public String getPassword() { | |
return password; | |
} | |
public void setPassword(String password) { | |
this.password = password; | |
} | |
public String getUserName() { | |
return userName; | |
} | |
public void setUserName(String userName) { | |
this.userName = userName; | |
} | |
private String password = ""; | |
private String userName = ""; | |
} |
SQLite的核心操作-DBAdapter.java
package org.mk.android.demo.demosimplesqlite; | |
import android.content.ContentValues; | |
import android.content.Context; | |
import android.database.Cursor; | |
import android.database.sqlite.SQLiteDatabase; | |
import android.database.sqlite.SQLiteException; | |
import android.database.sqlite.SQLiteOpenHelper; | |
import android.util.Log; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class DBAdapter { | |
private static final String TAG = "DemoSQLite"; | |
private static final String DB_NAME = "user.db"; | |
private static final String DB_TABLE = "t_user_login"; | |
private static final int DB_VERSION =; | |
public static final String KEY_ID = "loginId"; | |
public static final String KEY_PASSWORD = "password"; | |
public static final String KEY_NAME = "user_name"; | |
private SQLiteDatabase db; | |
private Context context = null; | |
private DBOpenHelper dbOpenHelper; | |
public DBAdapter(Context ctx) { | |
context = ctx; | |
} | |
public void close() { | |
try { | |
if (db != null) { | |
db.close(); | |
db = null; | |
} | |
} catch (Exception e) { | |
} | |
} | |
public void open() { | |
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION); | |
try { | |
db = dbOpenHelper.getWritableDatabase(); | |
} catch (SQLiteException ex) { | |
Log.e(TAG, ">>>>>>open db error: " + ex.getMessage(), ex); | |
} | |
} | |
public long addItem(UserBean user) throws Exception { | |
try { | |
ContentValues newValues = new ContentValues(); | |
newValues.put(KEY_ID, user.getLoginId()); | |
newValues.put(KEY_PASSWORD, user.getPassword()); | |
newValues.put(KEY_NAME, user.getUserName()); | |
Log.i(TAG, "addItem successfully with loginId->" + user.getLoginId() + " password->" + user.getPassword()); | |
return db.insert(DB_TABLE, null, newValues); | |
} catch (Exception e) { | |
Log.e(TAG, "addItem error: " + e.getMessage(), e); | |
throw new Exception("addItem error: " + e.getMessage(), e); | |
} | |
} | |
public List<UserBean> queryAll() { | |
List<UserBean> userList = new ArrayList<UserBean>(); | |
try { | |
StringBuilder sqlStr = new StringBuilder(); | |
sqlStr.append("select loginId,password,user_name from ").append(DB_TABLE); | |
Cursor cur = db.rawQuery(sqlStr.toString(), null); | |
while (cur.moveToNext()) { | |
String loginId = cur.getString(cur.getColumnIndexOrThrow("loginId")); | |
String password = cur.getString(cur.getColumnIndexOrThrow("password")); | |
String userName = cur.getString(cur.getColumnIndexOrThrow("user_name")); | |
UserBean user = new UserBean(); | |
user.setLoginId(loginId); | |
user.setPassword(password); | |
user.setUserName(userName); | |
userList.add(user); | |
} | |
} catch (Exception e) { | |
Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e); | |
} | |
return userList; | |
} | |
public int checkLogin(String loginId, String pwd) { | |
int result =; | |
StringBuilder sqlStr = new StringBuilder(); | |
sqlStr.append("SELECT COUNT(*) FROM ").append(DB_TABLE).append(" WHERE ").append(KEY_ID).append("=? AND ").append(KEY_PASSWORD).append("=?"); | |
Log.i(TAG, ">>>>>>execute checkLogin SQL: " + sqlStr.toString()); | |
Log.i(TAG, ">>>>>>LoginId->" + loginId + " password->" + pwd); | |
try { | |
Cursor cur = db.rawQuery(sqlStr.toString(), new String[]{loginId, pwd}); | |
if (cur != null) { | |
cur.moveToFirst(); | |
result = cur.getInt(); | |
} | |
} catch (Exception e) { | |
Log.e(TAG, "checkLogin dao error: " + e.getMessage(), e); | |
} | |
return result; | |
} | |
private static class DBOpenHelper extends SQLiteOpenHelper { | |
public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { | |
super(context, name, factory, version); | |
} | |
private static final String DB_CREATE = "create table " + | |
DB_TABLE + " (" + KEY_ID + " VARCHAR() primary key , " + | |
KEY_PASSWORD + " text not null, " + KEY_NAME + " text not null);"; | |
public void onCreate(SQLiteDatabase db) { | |
Log.i(TAG, ">>>>>>execute create table->" + DB_CREATE); | |
db.execSQL(DB_CREATE); | |
StringBuilder initDataSql = new StringBuilder(); | |
initDataSql.append("INSERT INTO ").append(DB_TABLE). | |
append("(").append(KEY_ID).append(",").append(KEY_PASSWORD). | |
append(",").append(KEY_NAME).append(")").append("values(?,?,?)"); | |
Log.i(TAG, ">>>>>>execute initDataSql->" + initDataSql.toString()); | |
db.execSQL(initDataSql.toString(), new String[]{"root", "", "root"}); | |
Log.i(TAG, ">>>>>>db init successfully"); | |
} | |
public void onUpgrade(SQLiteDatabase db, int _oldVersion, int _newVersion) { | |
//db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); | |
//onCreate(_db); | |
db.execSQL("DROP TABLE IF EXISTS " + DB_TABLE); | |
onCreate(db); | |
} | |
} | |
} |
MainActivity.java
这个就来得相对简单了。
package org.mk.android.demo.demosimplesqlite; | |
import androidx.appcompat.app.AppCompatActivity; | |
import android.content.Context; | |
import android.database.sqlite.SQLiteDatabase; | |
import android.database.sqlite.SQLiteOpenHelper; | |
import android.os.Bundle; | |
import android.util.Log; | |
import android.view.View; | |
import android.widget.Button; | |
import android.widget.EditText; | |
import android.widget.Toast; | |
import java.util.ArrayList; | |
import java.util.List; | |
public class MainActivity extends AppCompatActivity { | |
private static final String TAG = "DemoSQLite"; | |
private SQLiteDatabase db; | |
private Context context; | |
private DBAdapter dbAdapter; | |
private EditText editLoginId; | |
private EditText editPassword; | |
private Button buttonLogin; | |
private Button buttonAddItem; | |
private Button buttonQueryAll; | |
private String strLoginId; | |
private String strPassword; | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
context = getApplicationContext(); | |
buttonLogin = (Button) findViewById(R.id.buttonLogin); | |
buttonAddItem = (Button) findViewById(R.id.buttonAddItem); | |
buttonQueryAll = (Button) findViewById(R.id.buttonQueryAll); | |
editLoginId = (EditText) findViewById(R.id.editLoginid); | |
editPassword = (EditText) findViewById(R.id.editPassword); | |
dbAdapter = new DBAdapter(context); | |
buttonLogin.setOnClickListener(new View.OnClickListener() { | |
public void onClick(View view) { | |
try { | |
dbAdapter.open(); | |
strLoginId = editLoginId.getText().toString(); | |
strPassword = editPassword.getText().toString(); | |
int answer = dbAdapter.checkLogin(strLoginId, strPassword); | |
Log.i(TAG, ">>>>>>checkLogin is->" + answer); | |
if (answer !=) { | |
Toast.makeText(context, "登录失败请校验你的用户名和密码", Toast.LENGTH_LONG).show(); | |
} else { | |
Toast.makeText(context, "登录成功", Toast.LENGTH_LONG).show(); | |
} | |
} catch (Exception e) { | |
Log.e(TAG, ">>>>>>checkLogin error: " + e.getMessage(), e); | |
} finally { | |
dbAdapter.close(); | |
} | |
} | |
}); | |
buttonAddItem.setOnClickListener(new View.OnClickListener() { | |
public void onClick(View view) { | |
try { | |
dbAdapter.open(); | |
strLoginId = editLoginId.getText().toString(); | |
strPassword = editPassword.getText().toString(); | |
UserBean user = new UserBean(); | |
user.setLoginId(strLoginId); | |
user.setPassword(strPassword); | |
user.setUserName(strLoginId); | |
long result = dbAdapter.addItem(user); | |
Toast.makeText(context, "增加一条数据成功", Toast.LENGTH_LONG).show(); | |
} catch (Exception e) { | |
Log.e("TAG", ">>>>>>addItem error: " + e.getMessage(), e); | |
} finally { | |
dbAdapter.close(); | |
} | |
} | |
}); | |
buttonQueryAll.setOnClickListener(new View.OnClickListener() { | |
public void onClick(View view) { | |
try { | |
dbAdapter.open(); | |
StringBuilder sb = new StringBuilder(); | |
List<UserBean> userList = new ArrayList<UserBean>(); | |
userList = dbAdapter.queryAll(); | |
if (userList != null && userList.size() >) { | |
userList.forEach(v -> { | |
sb.append("loginId->").append(v.getLoginId()) | |
.append(" password->").append(v.getPassword()) | |
.append(" userName->").append(v.getUserName()) | |
.append("\n"); | |
}); | |
Log.i(TAG,sb.toString()); | |
Toast.makeText(context, "查询所有数据\n" + sb.toString(), Toast.LENGTH_LONG).show(); | |
} | |
} catch (Exception e) { | |
Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e); | |
} finally { | |
dbAdapter.close(); | |
} | |
} | |
}); | |
} | |
protected void onStart(){ | |
super.onStart(); | |
dbAdapter.open(); | |
} | |
protected void onStop() { | |
super.onStop(); | |
dbAdapter.close(); | |
} | |
} |
运行效果
自己动一下手试试吧。