1. 程式人生 > >Android SQLite 建表 面向物件程式設計 完美封裝 一勞永逸

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的建庫建表,以及對錶項的操作都使用面向物件的思想進行封裝,極大的方便以後的使用,並且讓程式碼的邏輯性更好,可讀性更高。