上一節博客《Android 項目實踐(二)——網絡連接 》中講解了網絡連接的封裝,這一節就要開始數據庫封裝了。
一、爲什麼封裝數據庫?
其實封裝數據庫的原因和封裝網絡連接的原因是相同的。在這我們就簡單的說一下原因:類似於網絡連接,數據庫創建和操作的方式也是很多了:不同數據庫有不同的API,像MySQL,SQLite等,在Xutils框架中也給我們封裝了數據庫的操作方法。假設在開發應用時,我們使用的是SQLite數據庫及其API,但是在接下來的升級和維護中,我們要將數據庫的操作修改爲xUtils框架提供的方法,這時,如果我們沒有封裝數據庫就會需要大量的修改代碼,這樣會給我們的應用打來很大的威脅。如果我們應用的數據庫操作封裝爲一個類,當替換數據庫操作方法時,只替換封裝的部分即可,簡單方便又安全。
二、封裝數據庫
在本次的開發實踐中我們使用的是SQLite數據庫,整理就封裝SQLite數據庫了。
1. 創建SQLiteOpenHelper
要想對數據庫進行操作就需要先創建一個數據庫。在之前將數據存儲的《Android中數據存儲——SQLite數據庫存儲數據 》博客中已經講解了數據庫的基本使用,可以先參考這篇博客瞭解SQLite數據庫的基本使用。這裏我們將數據庫創建封裝成一個類:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
/**
* Created by Administrator on 2015/10/8.
*/
public class MySQLiteOpenHelper extends SQLiteOpenHelper {
private static MySQLiteOpenHelper helper;
//構造器,傳入四個參數Context對象,數據庫名字name,操作數據庫的Cursor對象,版本號version。
private MySQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
//爲了簡化構造器的使用,我們自定義一個構造器
private MySQLiteOpenHelper(Context context, String name) {
this(context, name, null, 1);//傳入Context和數據庫的名稱,調用上面那個構造器
}
//將自定義的數據庫創建類單例。
public static synchronized MySQLiteOpenHelper getInstance(Context context) {
if(helper==null){
helper = new MySQLiteOpenHelper(context, "DayDayUpDb");//數據庫名稱爲create_db。
}
return helper;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
//在創建數據庫時,初始化創建數據庫中包含的數據庫表。這裏以一個“客戶”的數據表爲例
/*
customer 創建 "客戶"數據表
*/
sqLiteDatabase.execSQL("create table if not exists "+TableConfig.TABLE_CUSTOMER+"("
+TableConfig.Customer.CUSTOMER_ID+" integer not null primary key autoincrement,"
+TableConfig.Customer.CUSTOMER_NAME+ " verchar(20),"
+TableConfig.Customer.DELIVERY_PHONE+ " verchar(20),"
+TableConfig.Customer.ADDR+ " verchar(20) ,"
+TableConfig.Customer.ACCESS_TYPE+ " verchar(20),"
+TableConfig.Customer.CUSTOMER_RATING+ " verchar(20), "
+TableConfig.Customer.LAYERS+ " verchar(20),"
+TableConfig.Customer.CONTACTS+ " verchar(20),"
+TableConfig.Customer.REMARK+ " verchar(20))");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
//用於升級數據庫,只需要在創建本類對象時傳入一個比之前創建傳入的version大的數即可。
}
}
Point: 以上的代碼在《Android中數據存儲——SQLite數據庫存儲數據 》博客中是有講解的,創建一個數據庫,初始化數據庫時創建數據庫中包含的數據表。可能大家會注意到,創建數據表如下:
/*
customer 創建 "客戶"數據表
*/
sqLiteDatabase.execSQL("create table if not exists "+TableConfig.TABLE_CUSTOMER+"("
+TableConfig.Customer.CUSTOMER_ID+" integer not null primary key autoincrement,"
+TableConfig.Customer.CUSTOMER_NAME+ " verchar(20),"
+TableConfig.Customer.DELIVERY_PHONE+ " verchar(20),"
+TableConfig.Customer.ADDR+ " verchar(20) ,"
+TableConfig.Customer.ACCESS_TYPE+ " verchar(20),"
+TableConfig.Customer.CUSTOMER_RATING+ " verchar(20), "
+TableConfig.Customer.LAYERS+ " verchar(20),"
+TableConfig.Customer.CONTACTS+ " verchar(20),"
+TableConfig.Customer.REMARK+ " verchar(20))");
在創建時,使用到了TableConfig這個類,這個類也是我們自己定義的,用來存儲一些數據庫中數據表的表名,字段名等常量。可以看一下TableConfig類:
public class TableConfig {
public static final String TABLE_CUSTOMER = "customer";
/**
* Customer數據表的字段
*/
public static class Customer{
//Customer
public static final String CUSTOMER_ID="id";
public static final String CUSTOMER_NAME="customerName";
public static final String DELIVERY_PHONE="deliveryPhone";
public static final String ADDR="addr";
public static final String ACCESS_TYPE="accessType";
public static final String CUSTOMER_RATING="customerRating";
public static final String LAYERS="layers";
public static final String REMARK="remark";
public static final String CONTACTS="contacts";
}
}
2. 創建數據庫
咦,爲什麼第二部還是創建數據庫來?
因爲在第一步中我們指創建了一個數據庫的創建類,並沒有調用它創建數據庫,在這一布中我們就要創建數據庫啦!
public class DbManager {
private static DbManager manager;
private MySQLiteOpenHelper mySQLiteOpenHelper;
private SQLiteDatabase db;
/**
* 私有化構造器
*/
private DbManager() {
//創建數據庫
mySQLiteOpenHelper = MySQLiteOpenHelper.getInstance(BaseApplication.getContext());
if (db == null) {
db = mySQLiteOpenHelper.getWritableDatabase();
}
}
/**
* 單例DbManager類
*
* @return 返回DbManager對象
*/
public static DbManager newInstances() {
if (manager == null) {
manager = new DbManager();
}
return manager;
}
/**
* 獲取數據庫的對象
*
* @return 返回SQLiteDatabase數據庫的對象
*/
public SQLiteDatabase getDataBase() {
return db;
}
}
PointOne: 這裏我們創建了一個數據庫的創建類,這樣當我們要創建數據庫只需要調用如下簡單的異步就可以了:
DbManager manager = DbManager.newInstances();
SQLiteDatabase db = manager.getDataBase();
PointTwo: 細心的同志們一定會發現在我們構造器中創建數據庫時還是用到了另一個類:
/**
* 私有化構造器
*/
private DbManager() {
//創建數據庫
mySQLiteOpenHelper = MySQLiteOpenHelper.getInstance(BaseApplication.getContext());
if (db == null) {
db = mySQLiteOpenHelper.getWritableDatabase();
}
}
這裏使用到了BaseApplication,先看BaseApplication的定義:
public class BaseApplication extends Application {
private static BaseApplication mApplication;
/**
* 獲取Context
* @return 返回Context的對象
*/
public static Context getContext(){
return mApplication.getApplicationContext();
}
@Override
public void onCreate() {
super.onCreate();
this.mApplication = this;
}
}
在這個類中我們定義一個靜態獲得Context上下文的方法,以後我們在也不用爲獲得不了Context二發愁啦!
BaseApplication類繼承Application ,我們定義當應用啓動時調用這個BaseApplication啓動。然後我們在BaseApplication類中做一些初始化等的操作,這個了誒的定義會在接下來的博客中詳細講解。這裏不多說。
3. 操作數據庫
數據庫,數據表都創建好了,我們就該哦操作數據庫了,我們定義有一個數據表達額操作類,將數據庫的操作封裝在這裏:
public class TableOperate {
private DbManager manager;
private SQLiteDatabase db;
public TableOperate() {
//創建數據庫
manager = DbManager.newInstances();
db = manager.getDataBase();
}
/**
* 查詢數據庫的名,數據庫的添加
*
* @param tableName 查詢的數據庫的名字
* @param entityType 查詢的數據庫所對應的module
* @param fieldName 查詢的字段名
* @param value 查詢的字段值
* @param <T> 泛型代表AttendInformation,Customer,Order,User,WorkDaily類
* @return 返回查詢結果,結果爲AttendInformation,Customer,Order,User,WorkDaily對象
*/
public <T> ArrayList<T> query(String tableName, Class<T> entityType, String fieldName, String value) {
ArrayList<T> list = new ArrayList();
Cursor cursor = db.query(tableName, null, fieldName + " like ?", new String[]{value}, null, null, " id desc", null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
try {
T t = entityType.newInstance();
for (int i = 0; i < cursor.getColumnCount(); i++) {
String content = cursor.getString(i);//獲得獲取的數據記錄第i條字段的內容
String columnName = cursor.getColumnName(i);// 獲取數據記錄第i條字段名的
Field field = entityType.getDeclaredField(columnName);//獲取該字段名的Field對象。
field.setAccessible(true);//取消對age屬性的修飾符的檢查訪問,以便爲屬性賦值
field.set(t, content);
field.setAccessible(false);//恢復對age屬性的修飾符的檢查訪問
}
list.add(t);
cursor.moveToNext();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
return list;
}
/**
* 向數據庫插入數據
*
* @param tableName 數據庫插入數據的數據表
* @param object 數據庫插入的對象
*/
public void insert(String tableName, Object object) {
Class clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();//獲取該類所有的屬性
ContentValues value = new ContentValues();
for (Field field : fields) {
try {
field.setAccessible(true); //取消對age屬性的修飾符的檢查訪問,以便爲屬性賦值
String content = (String) field.get(object);//獲取該屬性的內容
value.put(field.getName(), content);
field.setAccessible(false);//恢復對age屬性的修飾符的檢查訪問
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
db.insert(tableName, null, value);
}
/**
* 刪除數據
*
* @param tableName 刪除數據庫的表名
* @param fieldName 刪除的字段名
* @param value 刪除的字段的值
*/
public void delete(String tableName, String fieldName, String value) {
db.delete(tableName, fieldName + "=?", new String[]{value});
}
/**
* 更改數據庫內容
*
* @param tableName 更改數據的數據表
* @param columnName 更改的數據的字段名
* @param columnValue 更改的數據的字段值
* @param object 更改的數據
*/
public void uptate(String tableName, String columnName, String columnValue, Object object) {
try {
Class clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();//獲取該類所有的屬性
ContentValues value = new ContentValues();
for (Field field : fields) {
field.setAccessible(true); //取消對age屬性的修飾符的檢查訪問,以便爲屬性賦值
String content = (String) field.get(object);//獲取該屬性的內容
value.put(field.getName(), content);
field.setAccessible(false);//恢復對age屬性的修飾符的檢查訪問
}
db.update(tableName, value, columnName+ "=?", new String[]{columnValue});
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
}
}
這裏定義了數據庫“增刪改查”四種操作。在講解四種方法之前,首先要說明一下,每一個數據表都一個他自己對應的module類,一般我們都會將這個module類存放在dao文件夾下(dao文件夾是新建的,用於存放module類的),就像我們的”客戶“數據表,就有一個它對應的Customer類:
public class Customer {
/*
* customerName 客戶名稱
* deliveryPhone 送貨電話
* addr 地址
* accessType 通路類型
* customerRating 客戶等級
* layers 圖層
* remark 備註
* contacts 聯繫人
*/
private String customerName;
private String deliveryPhone;
private String addr;
private String accessType;
private String customerRating;
private String layers;
private String remark;
private String contacts;
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getDeliveryPhone() {
return deliveryPhone;
}
public void setDeliveryPhone(String deliveryPhone) {
this.deliveryPhone = deliveryPhone;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getAccessType() {
return accessType;
}
public void setAccessType(String accessType) {
this.accessType = accessType;
}
public String getCustomerRating() {
return customerRating;
}
public void setCustomerRating(String customerRating) {
this.customerRating = customerRating;
}
public String getLayers() {
return layers;
}
public void setLayers(String layers) {
this.layers = layers;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public String getContacts() {
return contacts;
}
public void setContacts(String contacts) {
this.contacts = contacts;
}
}
PointOne: ”增“:
/**
* 向數據庫插入數據
*
* @param tableName 數據庫插入數據的數據表
* @param object 數據庫插入的對象
*/
public void insert(String tableName, Object object) {
Class clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();//獲取該類所有的屬性
ContentValues value = new ContentValues();
for (Field field : fields) {
try {
field.setAccessible(true); //取消對age屬性的修飾符的檢查訪問,以便爲屬性賦值
String content = (String) field.get(object);//獲取該屬性的內容
value.put(field.getName(), content);
field.setAccessible(false);//恢復對age屬性的修飾符的檢查訪問
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
db.insert(tableName, null, value);
}
因爲我們針對的是不同的數據表,所有我們這裏使用到了反射,將傳入的modulel類通過返回或的它所有的屬性,然後將屬性中的內容添加到ContentValues 對象中,進而添加到數據表中。(反射不懂的參考《Java中的反射和註解 》)博客。
PointTwo: ”刪“:
/**
* 刪除數據
* @param tableName 刪除數據庫的表名
* @param fieldName 刪除的字段名
* @param value 刪除的字段的值
*/
public void delete(String tableName, String fieldName, String value) {
db.delete(tableName, fieldName + "=?", new String[]{value});
}
刪除比較簡單,不用多講吧。
PointThree: ”改“:
/**
* 更改數據庫內容
* @param tableName 更改數據的數據表
* @param columnName 更改的數據的字段名
* @param columnValue 更改的數據的字段值
* @param object 更改的數據
*/
public void uptate(String tableName, String columnName, String columnValue, Object object) {
try {
Class clazz = object.getClass();
Field[] fields = clazz.getDeclaredFields();//獲取該類所有的屬性
ContentValues value = new ContentValues();
for (Field field : fields) {
field.setAccessible(true); //取消對age屬性的修飾符的檢查訪問,以便爲屬性賦值
String content = (String) field.get(object);//獲取該屬性的內容
value.put(field.getName(), content);
field.setAccessible(false);//恢復對age屬性的修飾符的檢查訪問
}
db.update(tableName, value, columnName+ "=?", new String[]{columnValue});
} catch (IllegalAccessException e1) {
e1.printStackTrace();
}
}
同樣是利用反射的原理,將object中的內容添加到ContentValues 對象中,然後調用update()方法修改。
PointFour: ”查“:
/**
* 查詢數據庫的名,數據庫的添加
*
* @param tableName 查詢的數據庫的名字
* @param entityType 查詢的數據庫所對應的module
* @param fieldName 查詢的字段名
* @param value 查詢的字段值
* @param <T> 泛型代表AttendInformation,Customer,Order,User,WorkDaily類
* @return 返回查詢結果,結果爲AttendInformation,Customer,Order,User,WorkDaily對象
*/
public <T> ArrayList<T> query(String tableName, Class<T> entityType, String fieldName, String value) {
ArrayList<T> list = new ArrayList();
Cursor cursor = db.query(tableName, null, fieldName + " like ?", new String[]{value}, null, null, " id desc", null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
try {
T t = entityType.newInstance();
for (int i = 0; i < cursor.getColumnCount(); i++) {
String content = cursor.getString(i);//獲得獲取的數據記錄第i條字段的內容
String columnName = cursor.getColumnName(i);// 獲取數據記錄第i條字段名的
Field field = entityType.getDeclaredField(columnName);//獲取該字段名的Field對象。
field.setAccessible(true);//取消對age屬性的修飾符的檢查訪問,以便爲屬性賦值
field.set(t, content);
field.setAccessible(false);//恢復對age屬性的修飾符的檢查訪問
}
list.add(t);
cursor.moveToNext();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
return list;
}
查詢數據庫傳入了四個參數,其中Class<T> entityType
是傳入的數據庫中數據表的module。通過反射機制查詢。
4. 數據庫初始化
創建好數據庫,在每次加載應用時,我麼需要對數據庫進行初始化。這裏我們定義一個類:
public class DataBaseInit {
/**
* 初始化客戶數據表
*/
public static void initCustomerTable() {
Customer customer1 = new Customer();
customer1.setCustomerName("華慶豐");
customer1.setDeliveryPhone("15345675673");
customer1.setAddr("前進");
customer1.setAccessType("農貿批發");
customer1.setCustomerRating("A");
customer1.setLayers("默認圖層");
customer1.setRemark("");
customer1.setContacts("");
Customer customer2 = new Customer();
customer2.setCustomerName("物美草橋");
customer2.setDeliveryPhone("");
customer2.setAddr("");
customer2.setAccessType("");
customer2.setCustomerRating("");
customer2.setLayers("");
customer2.setRemark("");
customer2.setContacts("");
Customer customer3 = new Customer();
customer3.setCustomerName("京客隆");
customer3.setDeliveryPhone("15937542659");
customer3.setAddr("前進華潤五金機電附近");
customer3.setAccessType("農貿批發");
customer3.setCustomerRating("A");
customer3.setLayers("默認圖層");
customer3.setRemark("");
customer3.setContacts("");
TableOperate operate = new TableOperate();//創建數據庫操作類
operate.insert(TableConfig.TABLE_CUSTOMER,customer1);
operate.insert(TableConfig.TABLE_CUSTOMER,customer2);
operate.insert(TableConfig.TABLE_CUSTOMER, customer3);
//刪除
// operate.delete(TableConfig.TABLE_CUSTOMER,TableConfig.Customer.CUSTOMER_NAME,"京客隆");
//修改
// Customer custtomer4 = new Customer();
// operate.uptate(TableConfig.TABLE_CUSTOMER,TableConfig.Customer.CUSTOMER_NAME,"京客隆",customer4);
//查詢
// ArrayList list = operate.query(TableConfig.TABLE_CUSTOMER, Customer.class, TableConfig.Customer.CUSTOMER_NAME, "京客隆");
// Customer cus = (Customer) list.get(0);
// Log.d("data", ""+cus.getCustomerName());
}
}
這裏我們就只向數據表中添加了幾條數據。
然後下面註釋掉的部分是數據庫其他操作的使用範例。
目錄結構
這樣我們的數據庫就基本封裝完成了,我們來通過一個目錄結構來理一下封裝的思路:
我們通過下面這張圖來加深印象: