最近由於工作原因,經常需要保存用戶數據,其中涉及創建表格、增刪改查操作。雖然 SQLiteDatabase 提供 insert 、delete、update、query 方法,但每次都要小心翼翼傳入參數,對於頻繁操作數據數據比較容易出錯,影響工作效率。現在我重新學習項目中數據庫設計的方法,其中把每一張表字段寫在一個bean,通過繼承基礎 BaseDao ,調用公共的數據庫指令。 這樣做的好處很明顯:①邏輯清晰,方便以后數據的增添;②操作規范,不用每次打開數據庫或者關閉數據庫,避免忘記關閉數據庫等誤操作。
現在自己寫了一個學生信息的 Demo ,SQL 操作示意圖如下,有興趣的朋友可以看看。

代碼詳解
1、布局文件相當簡單,設置創建數據庫、更新數據庫和增刪改查操作,代碼如下所示:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical"> 6 7 <Button 8 android:id="@+id/createDatabase" 9 android:layout_width="fill_parent" 10 android:layout_height="wrap_content" 11 android:background="@color/colorBackGround" 12 android:text="創建數據庫" /> 13 14 <Button 15 android:id="@+id/updateDatabase" 16 android:layout_width="fill_parent" 17 android:layout_height="wrap_content" 18 android:layout_marginTop="5dp" 19 android:background="@color/colorBackGround" 20 android:text="更新數據庫" /> 21 22 <Button 23 android:id="@+id/insert" 24 android:layout_width="fill_parent" 25 android:layout_height="wrap_content" 26 android:layout_marginTop="5dp" 27 android:background="@color/colorBackGround" 28 android:text="插入數據" /> 29 30 <Button 31 android:id="@+id/update" 32 android:layout_width="fill_parent" 33 android:layout_height="wrap_content" 34 android:layout_marginTop="5dp" 35 android:background="@color/colorBackGround" 36 android:text="更新數據" /> 37 38 <Button 39 android:id="@+id/query" 40 android:layout_width="fill_parent" 41 android:layout_height="wrap_content" 42 android:layout_marginTop="5dp" 43 android:background="@color/colorBackGround" 44 android:text="查詢數據" /> 45 46 <Button 47 android:id="@+id/delete" 48 android:layout_width="fill_parent" 49 android:layout_height="wrap_content" 50 android:layout_marginTop="5dp" 51 android:background="@color/colorBackGround" 52 android:text="刪除數據" /> 53 </LinearLayout>
布局很簡單,頁面如下所示
2、自定義一個繼承 SQLiteOpenHelper 的類 SQLiteHelper,重寫 onCreate(SQLiteDatabase sqLiteDatabase) 和 onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) 方法。其中當調用 SQLiteHelper 的 getReadableDatabase() 或者 getWritableDatabase() 時,如果沒有創建數據庫,則自動創建數據庫;接着調用 onCreate(SQLiteDatabase sqLiteDatabase) 方法,一般在這里創建數據庫表,當有版本更新時,走 onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) 方法,更新最新的數據庫表。
1 /** 2 * Created by LCS on 2016/11/1. 3 */ 4 public class SQLiteHelper extends SQLiteOpenHelper { 5 6 private static final String TAG = "LCS_SQLiteHelper"; 7 private static final String db_name = "sql.db";//數據庫名稱 8 private static final SQLiteDatabase.CursorFactory factory = null;//暫時用不到 9 private static final int version = 1;//版本號,方便以后項目更新用戶數據庫 10 private static SQLiteHelper sqLiteHelper = null;//實例化 SQLiteHelper 對象 11 12 //創建班級通訊錄 13 private static final String create_class_address = SQLInstruction.createClassAddress(); 14 /** 15 * 構造函數 16 * @param context 17 */ 18 public SQLiteHelper(Context context){ 19 super(context, db_name, factory, version); 20 } 21 22 /** 23 * 獲取實例 24 * @param context 25 * @return 26 */ 27 public static SQLiteHelper getInstance(Context context){ 28 29 if(sqLiteHelper == null){ 30 sqLiteHelper = new SQLiteHelper(context); 31 } 32 return sqLiteHelper; 33 } 34 35 /** 36 * 第一次創建數據庫,調用此方法 37 * @param sqLiteDatabase 38 */ 39 @Override 40 public void onCreate(SQLiteDatabase sqLiteDatabase) { 41 42 //創建班級通訊錄表 43 sqLiteDatabase.execSQL(create_class_address); 44 Log.d(TAG,"create db"); 45 } 46 47 /** 48 * 更新數據庫 49 * @param sqLiteDatabase 50 * @param oldVersion 51 * @param newVersion 52 */ 53 @Override 54 public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { 55 56 if(version > 1){ 57 sqLiteDatabase.execSQL(create_class_address); 58 Log.d(TAG,"upgrade db"); 59 } 60 } 61 }
3、先構建數據庫連接管理器類 ConnectionProvider,這是真正執行數據庫語句的位置。其中 execute(String sql) 可以執行數據庫增加、刪除和更新操作,query(String sql) 執行查找操作。被執行的的數據庫指令為 Stiing 類型,我們只需要檢查此語句正確性 ( 推薦使用 SQLite3 可視化工具 ),傳入給對應的方法即可,是不是很方便?
1 /** 2 * Created by lcs on 2016/11/2. 3 * 連接管理器,數據庫語句真正執行位置 4 */ 5 public class ConnectionProvider { 6 private SQLiteDatabase db; 7 private SQLiteHelper sqLiteHelper; 8 private Context context; 9 private static ConnectionProvider provider; 10 11 public ConnectionProvider(Context context){ 12 this.context = context; 13 /* //初始化 14 initProvider();*/ 15 } 16 //初始化 17 private void initProvider() { 18 if(sqLiteHelper == null){ 19 sqLiteHelper = SQLiteHelper.getInstance(context); 20 db = null; 21 } 22 if(db == null){ 23 db = sqLiteHelper.getWritableDatabase(); 24 } 25 } 26 27 /** 28 * 真正執行數據庫操作 29 * @param sql 30 */ 31 public synchronized void execute(String sql){ 32 //打開數據庫 33 openDB(); 34 db.execSQL(sql); 35 //關閉數據庫 36 closeDB(db); 37 } 38 39 /** 40 * 真正執行數據庫操作 41 * @param sql 42 */ 43 public synchronized Cursor query(String sql){ 44 //打開數據庫 45 openDB(); 46 return db.rawQuery(sql,null); 47 } 48 49 /** 50 * 打開數據庫 51 */ 52 private void openDB() { 53 try { 54 initProvider(); 55 }catch (Exception e){ 56 e.printStackTrace(); 57 } 58 } 59 60 /** 61 * 關閉數據庫 62 * @param db 63 */ 64 private void closeDB(SQLiteDatabase db) { 65 try { 66 sqLiteHelper = null; 67 db.releaseReference(); 68 }catch (Exception e){ 69 e.printStackTrace(); 70 } 71 } 72 73 /** 74 * 獲取 ConnectionProvider 實例 75 * @return 76 */ 77 public static ConnectionProvider getInstance(Context context){ 78 if(provider == null){ 79 provider = new ConnectionProvider(context); 80 } 81 return provider; 82 } 83 }
4、構建基礎 Dao。BaseDao 很簡單,但很重要。當以后有操作數據庫需求,只需要定義 某某 Dao 繼承 BaseDao,即可調用公共的數據庫操作。
1 /** 2 * 基礎 Dao 3 * Created by lcs on 2016/11/2. 4 */ 5 public abstract class BaseDao <T>{ 6 private Context context; 7 private ConnectionProvider provider; 8 9 public BaseDao(Context context){ 10 this.context = context; 11 this.provider = ConnectionProvider.getInstance(context); 12 } 13 14 /** 15 * 執行 SQL 語句 16 */ 17 public synchronized void execute(String sql){ 18 provider.execute(sql); 19 } 20 21 /** 22 * 執行 SQL 語句 23 */ 24 public synchronized Cursor query(String sql){ 25 return provider.query(sql); 26 } 27 28 /** 29 * 是否有結果集 30 * @param c 31 * @return 32 */ 33 protected synchronized boolean hasResult(Cursor c){ 34 boolean has=false; 35 if(c.moveToFirst()&&c.getCount()>0){ 36 has=true; 37 } 38 return has; 39 } 40 41 }
5、創建學生信息 bean 。其中 StudentInfoBean 為需要存儲進數據庫的字段信息
1 /** 2 * Created by user on 2016/11/2. 3 * 學生信息 4 */ 5 public class StudentInfoBean { 6 private String name = "";//姓名 7 private String id = "";//學號 8 private String age = "";//年齡 9 private String tall = "";//身高 10 11 public String getName() { 12 return name; 13 } 14 15 public void setName(String name) { 16 this.name = name; 17 } 18 19 public String getId() { 20 return id; 21 } 22 23 public void setId(String id) { 24 this.id = id; 25 } 26 27 public String getAge() { 28 return age; 29 } 30 31 public void setAge(String age) { 32 this.age = age; 33 } 34 35 public String getTall() { 36 return tall; 37 } 38 39 public void setTall(String tall) { 40 this.tall = tall; 41 } 42 }
6、創建學生 Dao。其中 StudentDao 繼承 BaseDao,在通過學號 id 查找和刪除學生信息方法中,其實也可以傳入其他學生信息進行查找,例如學生 name 、age 等。還有通過 id 可能查到多條記錄,規范寫法應該寫成返回 StudentInfoBean 的 List 。
1 /** 2 * Created by lcs on 2016/11/2. 3 * 學生信息的 Dao 4 */ 5 public class StudentDao extends BaseDao { 6 7 public StudentDao(Context context) { 8 super(context); 9 } 10 11 /** 12 * 增加一條學生記錄 13 * @param studentInfoBean 14 * @return 15 */ 16 public boolean addStudent(StudentInfoBean studentInfoBean){ 17 boolean flag = false; 18 if(studentInfoBean != null){ 19 flag = true; 20 execute(SQLInstruction.addStudent(studentInfoBean)); 21 return flag; 22 }else { 23 return flag; 24 } 25 } 26 27 /** 28 * 通過學號 id 刪除學生信息 29 * @param id 30 * @return 31 */ 32 public boolean deleteStudentById(String id){ 33 boolean flag = false; 34 if(id != "" && !id.isEmpty()){ 35 flag = true; 36 execute(SQLInstruction.deleteStudentById(id)); 37 return flag; 38 }else { 39 return flag; 40 } 41 } 42 43 /** 44 * 更新指定列學生信息 45 * @param column 46 * @param old_value 47 * @param new_value 48 * @return 49 */ 50 public boolean updateStudent(String column ,String old_value,String new_value){ 51 boolean flag = false; 52 if(column != "" && !column.isEmpty()){ 53 execute(SQLInstruction.updateStudent(column,old_value,new_value)); 54 flag = true; 55 return flag; 56 }else { 57 return flag; 58 } 59 } 60 61 /** 62 * 通過學號 id 查詢學生信息 63 * @param id 64 * @return 65 */ 66 public StudentInfoBean queryStudent(String id){ 67 StudentInfoBean studentInfoBean = new StudentInfoBean(); 68 if(id != "" && !id.isEmpty()){ 69 Cursor cursor = query(SQLInstruction.queryStudentById(id)); 70 if(hasResult(cursor)){ 71 studentInfoBean.setName(cursor.getString(cursor.getColumnIndex("NAME"))); 72 studentInfoBean.setAge(cursor.getString(cursor.getColumnIndex("AGE"))); 73 studentInfoBean.setTall(cursor.getString(cursor.getColumnIndex("TALL"))); 74 cursor.moveToNext(); 75 } 76 cursor.close(); 77 } 78 return studentInfoBean; 79 } 80 }
7、看到現在,不知道你會不會覺得很奇怪,為什么還沒有涉及真正數據庫操作語句?那是因為我們把所有數據庫語句放在一個類中 SQLInstruction。為方便調用,將每一個數據庫操作方法設置為 static。看到這里,你是否發現如此設計數據庫操作好處呢?
1 /** 2 * Created by user on 2016/11/1. 3 * 數據庫語句 4 */ 5 public class SQLInstruction { 6 7 /** 8 * 創建班級通訊錄表 9 * 10 * @return 11 */ 12 public static String createClassAddress() { 13 StringBuffer sql = new StringBuffer(); 14 sql.append("create table IF NOT EXISTS CLASS_ADDRESS(" 15 + "NAME String," + "ID int," +"AGE int," 16 + "TALL int," + "CLASS_NAME String" + " )"); 17 return sql.toString(); 18 } 19 20 /** 21 * 在數據庫添加一條學生記錄 22 */ 23 public static String addStudent(StudentInfoBean student){ 24 StringBuffer sql = new StringBuffer("insert into CLASS_ADDRESS(") 25 .append("NAME,") 26 .append("ID,") 27 .append("AGE,") 28 .append("TALL") 29 .append(") VALUES("); 30 sql.append("'").append(student.getName()).append("',"); 31 sql.append("'").append(student.getId()).append("',"); 32 sql.append("'").append(student.getAge()).append("',"); 33 sql.append("'").append(student.getTall()).append("')"); 34 return sql.toString(); 35 } 36 37 /** 38 * 通過學號 id 刪除學生信息 39 * @param id 40 * @return 41 */ 42 public static String deleteStudentById(String id){ 43 StringBuffer sql = new StringBuffer(); 44 sql.append("delete from CLASS_ADDRESS where ID = '"); 45 sql.append(id).append("'"); 46 return sql.toString(); 47 } 48 49 /** 50 * 更新指定列學生信息 51 * @param column 52 * @param old_value 53 * @param new_value 54 * @return 55 */ 56 public static String updateStudent(String column ,String old_value,String new_value){ 57 StringBuffer sql = new StringBuffer(); 58 sql.append("update CLASS_ADDRESS set "); 59 sql.append(column).append(" = "); 60 sql.append("'").append(new_value).append("'"); 61 sql.append(" where ").append(column).append(" = "); 62 sql.append("'").append(old_value).append("'"); 63 return sql.toString(); 64 } 65 66 /** 67 * 通過學號 id 查詢學生信息 68 * @param id 69 * @return 70 */ 71 public static String queryStudentById(String id){ 72 StringBuffer sql = new StringBuffer(); 73 sql.append("select NAME,ID,AGE,TALL "); 74 sql.append("from CLASS_ADDRESS where ID = '"); 75 sql.append(id).append("'"); 76 return sql.toString(); 77 } 78 79 }
8、進入最后一步操作,在 Activity 進行數據庫操作。
1 @Override 2 public void onClick(View view) { 3 StudentDao studentDao = null; 4 switch (view.getId()){ 5 case R.id.createDatabase://創建數據庫 6 SQLiteHelper sqLiteHelper = new SQLiteHelper(SQLiteActivity.this); 7 SQLiteDatabase database_create = sqLiteHelper.getReadableDatabase(); 8 database_create.close(); 9 break; 10 11 case R.id.updateDatabase://更新數據庫 12 SQLiteHelper sqLiteHelper_update = new SQLiteHelper(SQLiteActivity.this); 13 SQLiteDatabase database_update = sqLiteHelper_update.getWritableDatabase(); 14 database_update.close(); 15 break; 16 17 case R.id.insert://插入兩條學生記錄 18 if(studentDao == null){ 19 studentDao = new StudentDao(SQLiteActivity.this); 20 } 21 boolean insert_result1 = studentDao.addStudent(studentInfoBean1); 22 boolean insert_result2 = studentDao.addStudent(studentInfoBean1); 23 Log.d(TAG,String.valueOf(insert_result1)); 24 Log.d(TAG,String.valueOf(insert_result2)); 25 break; 26 27 case R.id.delete://通過學號 id 刪除學生信息 28 if(studentDao == null){ 29 studentDao = new StudentDao(SQLiteActivity.this); 30 } 31 boolean delete_result = studentDao.deleteStudentById("1000001"); 32 Log.d(TAG,String.valueOf(delete_result)); 33 break; 34 35 case R.id.update://更新指定列學生信息 36 if(studentDao == null){ 37 studentDao = new StudentDao(SQLiteActivity.this); 38 } 39 boolean update_result = studentDao.updateStudent("NAME", "小明", "飛天"); 40 Log.d(TAG,String.valueOf(update_result)); 41 break; 42 43 case R.id.query://通過學號 id 查詢學生信息 44 if(studentDao == null){ 45 studentDao = new StudentDao(SQLiteActivity.this); 46 } 47 StudentInfoBean studentInfoBean = studentDao.queryStudent("1000001"); 48 String age = studentInfoBean.getAge(); 49 String name = studentInfoBean.getName(); 50 String tall = studentInfoBean.getTall(); 51 Log.d(TAG,"age= " + age + "\nname = " + name + "\ntall= " + tall); 52 break; 53 } 54 }
現在重新看 SQL 操作示意圖,應該會覺得好理解些吧!