Android入門之使用SQLite內(nèi)嵌式數(shù)據(jù)庫(kù)詳解
介紹
Android內(nèi)帶SQLite內(nèi)嵌式數(shù)據(jù)庫(kù)了。這對(duì)于我們存儲(chǔ)一些更復(fù)雜的結(jié)構(gòu)化數(shù)據(jù)帶來了極大的便利。比如說我們要存儲(chǔ)應(yīng)用內(nèi)的常用聯(lián)系人,購(gòu)物車暫存信息,常量。必竟從xml或者是json里取數(shù)據(jù)都沒有一條Select語句來得簡(jiǎn)單。
SQLite常用有五種數(shù)據(jù)類型:
- NULL
- INTEGER
- REAL(浮點(diǎn)數(shù))
- TEXT(字符串文本)
- BLOB(二進(jìn)制對(duì)象)
雖然只有五種,但是對(duì)于varchar,char等其他數(shù)據(jù)類型都是可以保存的,如下create table語句依然是可以生效的:
create table user(name varchar(20))
SQLite常用的三個(gè)類介紹
- SQLiteOpenHelper:抽象類,我們通過繼承該類,然后重寫數(shù)據(jù)庫(kù)創(chuàng)建以及更新的方法, 我們還可以通過該類的對(duì)象獲得數(shù)據(jù)庫(kù)實(shí)例,或者關(guān)閉數(shù)據(jù)庫(kù);
- SQLiteDatabase:數(shù)據(jù)庫(kù)訪問類:我們可以通過該類的對(duì)象來對(duì)數(shù)據(jù)庫(kù)做一些增刪改查的操作;
- Cursor:游標(biāo),有點(diǎn)類似于JDBC里的resultset,結(jié)果集!可以簡(jiǎn)單理解為指向數(shù)據(jù)庫(kù)中某 一個(gè)記錄的指針;
這三個(gè)類我們直接來看下面的Sample代碼各位就知道是什么樣的組合應(yīng)用了。
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相當(dāng)于JDBC里的ResultSet。
敲黑板重要提醒-Android中如何正確使用db.open()/db.close()
在我們的例子里我們?cè)诿恳粋€(gè)業(yè)務(wù)方法操作都會(huì)使用db.open一下,在finally塊里db.close一下。
同時(shí)要在Activity的onStop方法中去dbclose()掉它。
記得,它就和水龍頭一樣,用完就要關(guān)。
SQLiteOpenHelper類的使用
這個(gè)類通常我們都要新建一個(gè)其它的類來extends這個(gè)類才能使用,主要的是這個(gè)類里有這么兩個(gè)方法是很有用的。
public void onCreate
方法全簽名:public void onCreate(SQLiteDatabase db)
這個(gè)類的作用就是當(dāng)SQLiteOpenHelper被實(shí)例化時(shí),第一次用來做“初始化數(shù)據(jù)庫(kù)”用的。比如說我們有一個(gè)類如下
private static class DBOpenHelper extends SQLiteOpenHelper {
然后當(dāng)你實(shí)例化這個(gè)類時(shí)
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
它會(huì)自動(dòng)觸發(fā)這個(gè)onCreate方法,在onCreate方法中我們可以做表創(chuàng)建以及數(shù)據(jù)初始化操作如:
private static final String DB_CREATE = "create table " + DB_TABLE + " (" + KEY_ID + " VARCHAR(20) primary key , " + KEY_PASSWORD + " text not null, " + KEY_NAME + " text not null);"; @Override 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", "111111", "root"}); Log.i(TAG, ">>>>>>db init successfully"); }
public void onUpgrade
方法全簽名:public void onUpgrade(SQLiteDatabase db, int _oldVersion, int _newVersion)
它有一個(gè)_newVersion這么一個(gè)參數(shù),這個(gè)參數(shù)也很有意思。當(dāng)你在實(shí)例化SQLiteOpenHelper類傳入的DB_VERSION參數(shù)>就近一次實(shí)例化這個(gè)類傳入的DB_VERSION參數(shù)時(shí),它就會(huì)觸發(fā)這個(gè)onUpgrade()方法,在這個(gè)方法里我們一般是根據(jù)業(yè)務(wù)場(chǎng)景的需要,沒有一概而論該怎么辦,通用的做法有:
- 重新執(zhí)行一遍onCreate();此時(shí)所有的數(shù)據(jù)會(huì)被清空并初始化;
- 有些Android發(fā)版時(shí)不需要做全數(shù)據(jù)庫(kù)清除,往往太過危險(xiǎn),而是會(huì)執(zhí)行alter更改表結(jié)構(gòu)、新建表、新插入-insert一些數(shù)據(jù)或者是稍帶著update一些數(shù)據(jù);
因此我才說,不一概而論而是需要依賴于你的實(shí)際業(yè)務(wù)場(chǎng)景來做操作。在我們的例子里我們會(huì)在onUpgrade里再執(zhí)行一下onCreate()。
使用SQLite操作數(shù)據(jù)庫(kù)方法介紹
這一塊在使用上分兩塊。它類似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; }
區(qū)分何時(shí)使用API何時(shí)使用原生SQL
如何區(qū)分呢?
答案很簡(jiǎn)單,以下是最佳實(shí)踐 :
- 單表操作,就使用API好了;
- 跨表(>1個(gè)表)的操作或者是較復(fù)雜的邏輯如果使用API會(huì)產(chǎn)生循環(huán)套來套去不如寫一條長(zhǎng)點(diǎn)的SQL那么請(qǐng)直接使用原生SQL;
在樣例前我們最后要了解一下,SQLite數(shù)據(jù)庫(kù)管理工具。
SQLite數(shù)據(jù)庫(kù)管理
Android里的SQLite一旦生成后它必須導(dǎo)出到AndroidStudio外部才能管理。按照如下步驟來導(dǎo)出SQLite數(shù)據(jù)庫(kù)
先使用Device File Explorer打開data->data->你的包全路徑下/databases,這里面有一個(gè).db文件就是SQLite數(shù)據(jù)庫(kù)文件。另一個(gè).db-journal是Transaction日志,我們后面講SQLite的Transaction時(shí)會(huì)講到。
選擇這個(gè)user.db右鍵選->save as,就可以導(dǎo)出到本地磁盤了。
然后你可以使用如下工具去管理SQLite數(shù)據(jù)庫(kù)文件。
Windows下的SQLite管理工具
請(qǐng)使用SQLite Expert Professional,我使用的是5.4.4。不過這個(gè)是收費(fèi)的,它長(zhǎng)這個(gè)樣但相當(dāng)專業(yè)(收費(fèi)的當(dāng)然專業(yè))。
MAC下的SQLite管理工具
請(qǐng)使用SQLiteManager,完全免費(fèi)并且和Windows的SQLite Expert Professional收費(fèi)的一樣功能全。為什么呢?難道因?yàn)镸AC貴。。。所以。。。有預(yù)收費(fèi)之說:)?
好了,原理介紹完畢,我們要進(jìn)入實(shí)踐課程了。
課程目標(biāo)
1.該APP啟動(dòng)會(huì)創(chuàng)建user.db數(shù)據(jù)庫(kù),新建一張t_user_login表并初始化一條root/111111數(shù)據(jù)進(jìn)入內(nèi)嵌SQLite數(shù)據(jù)庫(kù);
2.用戶名密碼輸入root/111111點(diǎn)【登錄】按鈕可以得到登錄成功,如果輸入其它的不存在的帳戶信息會(huì)以Toast顯示“登錄失敗請(qǐng)校驗(yàn)?zāi)愕挠脩裘兔艽a”;
3.用戶名密碼輸入任意值,點(diǎn)擊【增加一條記錄】按鈕,可以插入一條數(shù)據(jù)到內(nèi)嵌SQLite;
4.點(diǎn)擊【查詢所有記錄】會(huì)以Log和Toast顯示目前所有的相對(duì)應(yīng)的表內(nèi)的數(shù)據(jù)即:select *操作;
5.例子中對(duì)于單表的插入操作我們使用的是Android自帶的SQLiteOpenHelper的原生API;
6.校驗(yàn)登錄和查詢所有記錄我們用的是原生SQL+Cursor;
下面就進(jìn)入代碼部分吧。
前端代碼
<?xml version="1.0" encoding="utf-8"?> <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="10dp" android:text="請(qǐng)輸入用戶名" /> <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="請(qǐng)輸入密碼" /> <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="1dp" 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="10dp" android:text="增加一條記錄"/> <Button android:id="@+id/buttonQueryAll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" 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 = 2; 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 = 0; 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(0); } } 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(20) primary key , " + KEY_PASSWORD + " text not null, " + KEY_NAME + " text not null);"; @Override 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", "111111", "root"}); Log.i(TAG, ">>>>>>db init successfully"); } @Override 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
這個(gè)就來得相對(duì)簡(jiǎn)單了。
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; @Override 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() { @Override 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 != 1) { Toast.makeText(context, "登錄失敗請(qǐng)校驗(yàn)?zāi)愕挠脩裘兔艽a", 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() { @Override 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, "增加一條數(shù)據(jù)成功", Toast.LENGTH_LONG).show(); } catch (Exception e) { Log.e("TAG", ">>>>>>addItem error: " + e.getMessage(), e); } finally { dbAdapter.close(); } } }); buttonQueryAll.setOnClickListener(new View.OnClickListener() { @Override 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() > 0) { 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, "查詢所有數(shù)據(jù)\n" + sb.toString(), Toast.LENGTH_LONG).show(); } } catch (Exception e) { Log.e(TAG, ">>>>>>queryAll error: " + e.getMessage(), e); } finally { dbAdapter.close(); } } }); } @Override protected void onStart(){ super.onStart(); dbAdapter.open(); } @Override protected void onStop() { super.onStop(); dbAdapter.close(); } }
運(yùn)行效果
自己動(dòng)一下手試試吧。
以上就是Android入門之使用SQLite內(nèi)嵌式數(shù)據(jù)庫(kù)詳解的詳細(xì)內(nèi)容,更多關(guān)于Android SQLite內(nèi)嵌式數(shù)據(jù)庫(kù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 中ListView的Item點(diǎn)擊事件失效的快速解決方法
這篇文章主要介紹了Android 中ListView的Item點(diǎn)擊事件失效的快速解決方法的相關(guān)資料,需要的朋友可以參考下2016-09-09詳解android webView獨(dú)立進(jìn)程通訊方式
本篇文章主要介紹了android webView獨(dú)立進(jìn)程通訊方式,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-09-09基于RxJava2實(shí)現(xiàn)的簡(jiǎn)單圖片爬蟲的方法
本篇文章主要介紹了基于RxJava2實(shí)現(xiàn)的簡(jiǎn)單圖片爬蟲的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-02-02ViewPager實(shí)現(xiàn)帶引導(dǎo)小圓點(diǎn)與自動(dòng)跳轉(zhuǎn)的引導(dǎo)界面
這篇文章主要為大家詳細(xì)介紹了ViewPager實(shí)現(xiàn)帶引導(dǎo)小圓點(diǎn)與自動(dòng)跳轉(zhuǎn)的引導(dǎo)界面,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11Android中自定義view實(shí)現(xiàn)側(cè)滑效果
這篇文章主要介紹了Android中自定義view實(shí)現(xiàn)側(cè)滑效果的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11Android中Activity跳轉(zhuǎn)的創(chuàng)建步驟總結(jié)
這篇文章主要介紹了Android中Activity跳轉(zhuǎn)的創(chuàng)建步驟總結(jié),本文詳細(xì)的講解了從工程創(chuàng)建到跳轉(zhuǎn)Activity的實(shí)現(xiàn)完整過程,需要的朋友可以參考下2014-10-10