Android SQLite 建表 面向物件程式設計 完美封裝 一勞永逸
Android專案開發過程中經常使用到SQLite資料對資料進行儲存,每個app都會有各自的資料DB,以及各種表項。這就意味著每次進行app開發都要編寫資料庫以及表項的建立程式碼,而這些建庫建表程式碼量往往不少,但是大多雷同,只是具體資料不一樣。僅僅拷貝後,替換都覺得麻煩。
為何不將建庫建表封裝起來呢?下次建庫或者建表時只用配置對應的資料庫名,表名以及表屬性欄位即可。
1,建庫建表
簡單來說SQLite建表就是利用SQLiteDatabase獲取DBOpenHelper 來執行相應的建表SQL語句即可。SQLiteDatabase物件容易獲取,建庫只用一條拼接後的SQL建庫一句即可,而一個庫中可以存在多張表,每張表的建表語句有些微的不同(應為表字段有差異),故建表語句數對應了需要建立的表數。下面在程式碼中體現與說明建庫建表過程。
package com.ws.coyc.wsnote.SQLiteHelper;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import com.ws.coyc.wsnote.SQLiteHelper.Utils.l;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
/**
* Created by coyc on 16-8-23.
* before use the SDK you should do SQLiteManager.init();
* then you can create table by SQLiteManager.crateTable();
*/
public class SQLiteManager {
/*
member
*/
// SQLite
public SQLiteDatabase db;//資料庫操作物件
private Context context; //上下文
private DBOpenHelper dbOpenHelper;//建表所需的幫助類(不懂的另外百度)
private class DBOpenHelper extends SQLiteOpenHelper {
public DBOpenHelper(Context context, String name,
SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase _db) {
int size = tables.size();//獲取當前需要建表的數量
for(int i = 0;i<size;i++)//拼接建表SQL語句
{
String sql = getTableCreateSQLString(tables.get(i));
_db.execSQL(sql);//執行建庫建表語句
}
}
@Override
public void onUpgrade(SQLiteDatabase _db, int _oldVersion,int _newVersion) {
int size = tables.size();//同建立表機制,拼接跟新表SQL語句
for(int i = 0;i<size;i++)
{
_db.execSQL("DROP TABLE IF EXISTS " + getTableCreateSQLString(tables.get(i)));
}
onCreate(_db);
}// end for private static class DBOpenHelper extends SQLiteOpenHelper
}
// DB_name
private String DB_NAME;
// DB_version
private int DB_VERSION = 1;
//ArrayList <Table>
private ArrayList<Table> tables = new ArrayList<>();//該庫中表物件列表
/*
fun
*/
//constrat
public SQLiteManager()
{
}
//init 初始化建庫工作,傳入庫名
public void init(Context context,String DB_name)
{
tables.clear();
this.context = context;
this.DB_NAME = DB_name;
}
private static SQLiteManager instance = null;
public static SQLiteManager getInstance()//單例模式 方便外部呼叫
{
if(instance == null)
{
synchronized (SQLiteManager.class)
{
if(instance == null)
{
instance = new SQLiteManager();
}
}
}
return instance;
}
//open 做資料庫操作之前都要呼叫open方法
public void open() {
dbOpenHelper = new DBOpenHelper(context, DB_NAME, null, DB_VERSION);
try {
db = dbOpenHelper.getWritableDatabase();
} catch (SQLiteException ex) {
ex.printStackTrace();
exceptionHandler();
}
db.beginTransaction();
}
/**
* 資料庫檔案損壞刪除異常處理
*
*/
private void exceptionHandler() {
if(db == null)
{
return;
}
File file = new File(db.getPath());
if (!file.exists()) {
try {
if (file.createNewFile()) {
open();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//close 長時間不適用資料庫可執行close方法關閉資料庫
public void close() {
db.setTransactionSuccessful();
db.endTransaction();
if (db != null) {
db.close();
db = null;
}
}
//create table 登錄檔 外界任何想在該庫下建立的表都必須註冊 註冊後tables列表會增加,這回作用到建表時SQL語句的生成
public void registerTable(String table_name, ArrayList<Item> items)
{
Table table = new Table(table_name,"_id",items);
tables.add(table);
}
//兩種不同的註冊方式 建議使用第二種方式進行註冊,因為第二種方式的表物件是外界傳入的,外界可以利用該物件進行更多的操作。詳情見下
public void registerTable(Table table)
{
tables.add(table);
}
// get table create sql by items 建表語句拼接程式碼 遍歷tables列表,由於每個表又可以有多個欄位,故欄位型別不同又有對應的語句區別。
private static String getTableCreateSQLString(Table table) {
String sql = "create table "+table.table_name;//表名 下面依次是“主鍵”+“item1”+“item2”+.....+"item n" 拼接程式碼原理不熟的百度能搜出一大把,這裡只是稍微換了一種形式的寫法。
sql += "("+table.keyItem+" integer primary key autoincrement";
int size = table.items.size();
for(int i = 0;i<size;i++)
{
sql += ",";
if(table.items.get(i).type.equals(Item.item_type_integer))
{
sql += table.items.get(i).text+" integer not null";
}else if(table.items.get(i).type.equals(Item.item_type_text))
{
sql += table.items.get(i).text+" text not null";
}else if(table.items.get(i).type.equals(Item.item_type_boolen))
{
sql += table.items.get(i).text+" bool not null";
}else if(table.items.get(i).type.equals(Item.item_type_long))
{
sql += table.items.get(i).text+" long not null";
}
// TODO: 16-8-23 add other data type 可能有別的資料屬性 要看SQLite還支援那些資料的儲存
}
sql += ");";
Log.i("coyc","getTableCreateSQLString"+sql);
return sql;
}
//如果是使用第一種方式註冊的 這裡提供一個介面獲取本類中tables列表中的某個表,傳入所需要獲取的表名即可(不是重點)
public Table getTableByName(String tableName)
{
int size = tables.size();
if(size == 0)
{
return null;
}else
{
for(int i = 0;i<size;i++)
{
if(tableName.equalsIgnoreCase(tables.get(i).table_name))
{
return tables.get(i);
}
}
}
return null;
}
}
上面程式碼中引用了Table物件以及Item物件,Table物件好理解就是表物件。而Item對是各個表中每個欄位對應的物件。比如說商品表中可能含有“商品名”,這個商品名就是一個item物件。程式碼如下:
package com.ws.coyc.wsnote.SQLiteHelper;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;
import java.util.ArrayList;
/**
* Created by coyc on 16-8-23.
*/
public class Table {
/*
member
*/
//table name
public String table_name;
//table strings 表屬性列表
public ArrayList<Item> items = new ArrayList<>();
//key 自定主鍵項 一般預設為“_id”
public String keyItem;
//db
public Table()
{
}
public Table(String table_name,String keyItem)
{
this.keyItem = keyItem;
this.table_name = table_name;
}
public Table(String table_name,String keyItem,ArrayList<Item> items)
{
this.keyItem = keyItem;
this.table_name = table_name;
this.items = items;
}
/*
fun 查改增刪 方法的具體實現 這裡強烈建議具體方法由子類區實現,因為不同的表所對應的查改增刪方法都有不同。
以後我們在使用的時候,我們可以讓自己的表物件繼承與Table物件,這樣專案中就很好區分各個表物件,也可以很好的管理專案中的表。
*/
//create table
//delete table
//inserte item
}
}
item類
package com.ws.coyc.wsnote.SQLiteHelper;
/**
* Created by coyc on 16-8-23.
*/
public class Item {
//屬性儲存型別 比如說商品名這個屬性儲存格式可能是文字,而商品價格屬性儲存型別就可能是integer型,這個根據具體情況合理初始化item物件即可
public static final String item_type_integer = "item_type_integer";
public static final String item_type_text = "item_type_text";
public static final String item_type_long = "item_type_long";
public static final String item_type_boolen = "item_type_boolen";
public String text = "";//用於SQL拼接的關鍵字(要是無法理解可見接下來的例子)
public String type = "";//該item的型別
public Item(String text,String type)
{
this.text = text;
this.type = type;
}
public Item()
{
}
}
上面就完成了對SQLite建庫建表的封裝過程,接下來只需在專案中進行簡單的配置,即可使用。做到程式碼的可移植性,以及可複用性。
目前來說程式碼結構如下:(utils包是工具包,不用管)
2,專案引用
接下來看具體的操作程式碼:
使用流程如下:
1 ,初始化SQLiteManager
2,編寫自己的Table類繼承與Table,並建立。
3,在SQLiteManager中註冊
4,執行建立表語句(SQLite在首次使用是進行建立)
5,open資料庫,進行操作
public BillTable billTable;//賬單表(這裡什麼表都行,看自己需求)
public PersonTable personTable;//使用者表
private void initSQL(Context context) {
//init table
billTable = new BillTable();
personTable = new PersonTable();
//init SQL
SQLiteManager.getInstance().init(context,DB_NAME);
SQLiteManager.getInstance().registerTable(billTable);
SQLiteManager.getInstance().registerTable(personTable);
//在執行open方法時 如果是第一次呼叫 系統則會執行建表語句
SQLiteManager.getInstance().open();//when the app close you should to close the SQL
}
//該方法呼叫完成後建表工作就都完成了
我們對比一下如果不使用封裝的方法則每次專案中需要SQLite時可能效果是這樣的(這裡只是部分截圖,程式碼太多根本截不下來):
如果使用面向物件的方法的效果就會這樣的:
是不是簡單了很多!
當然我們還有寫程式碼未展示出來,接下來以personTable為例展示如何繼承Table類,重寫關鍵方法,以及Item屬性的構建。
package com.ws.coyc.wsnote.Data.Table;
import com.ws.coyc.wsnote.SQLiteHelper.Item;
import com.ws.coyc.wsnote.SQLiteHelper.Table;
/**
* Created by coyc on 16-9-9.
*/
public class PersonTable extends Table{
public static final String name = "name";
public static final String address1 = "address1";
public static final String address2 = "address2";
public static final String phone = "phone";
public static final String src_photo = "src_photo";
public PersonTable()
{
init();
}
public void init()
{
table_name = "person";
keyItem = "_id";//預設給予主鍵為“_id”
//構建屬性
items.add(new Item(name , Item.item_type_text));
items.add(new Item(address1 , Item.item_type_text));
items.add(new Item(address2 , Item.item_type_text));
items.add(new Item(phone , Item.item_type_text));
items.add(new Item(src_photo , Item.item_type_text));
}
}
public long insert(ContentValues cv)
{
return SQLiteManager.getInstance().db.insert(table_name,null,cv);
}
//delete item
public long deleteAllItem()
{
return SQLiteManager.getInstance().db.delete(table_name,null,null);
}
//
public long deleteOneByItem(String item,String content)
{
String[] args = {String.valueOf(content)};
return SQLiteManager.getInstance().db.delete(table_name,item+" =?",args);
}
//update item
public long updateOneByItem(String item,String content,ContentValues contentData)
{
String[] args = {String.valueOf(content)};
return SQLiteManager.getInstance().db.update(table_name,contentData, item+" =? ",args);
}
//上述的deleteAllItem方法可以將其放入到父類Table中去,因為這個方法可以共用,甚至insert方法也可以,這裡我就不寫回去了。但是下面這個getContentValues方法是每個類獨有的,必須重寫。因為每個表的屬性都不同。
public ContentValues getContentValues() {
ContentValues contentValues = new ContentValues();
contentValues.put("name","coyc");
contentValues.put("address1","長沙");
contentValues.put("phone","10086");
contentValues.put("src_photo","");
return contentValues;
}
寫到這裡基本上就算完成了 ,想要操作某個表時,直接使用該表物件的特定方法即可。eg:
personTable.insert(personTable.getContentValues);
3,總結:程式設計師都是很懶的,所以他們想到了封裝。寫一次程式碼以後都用現成的,想想都很好。本文中是對SQLite的建庫建表,以及對錶項的操作都使用面向物件的思想進行封裝,極大的方便以後的使用,並且讓程式碼的邏輯性更好,可讀性更高。