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的建库建表,以及对表项的操作都使用面向对象的思想进行封装,极大的方便以后的使用,并且让代码的逻辑性更好,可读性更高。

一个简单的基于AndroidSqlite数据库的操作封装,它有如下的好处:便捷地创建表和增添表字段灵活的数据类型处理通过操作对象来insert或者update表记录支持多种查询方式,支持多表自定义的复杂查询,支持分页查询支持事务快速开始:    1. 设计表:@Table(name="t_user") public class UserModel {     @Table.Column(name="user_id",type=Column.TYPE_INTEGER,isPrimaryKey=true)     public Integer userId;     @Table.Column(name="user_name",type=Column.TYPE_STRING,isNull=false)     public String userName;     @Table.Column(name="born_date",type=Column.TYPE_TIMESTAMP)     public Date bornDate;     @Table.Column(name="pictrue",type=Column.TYPE_BLOB)     public byte[] pictrue;     @Table.Column(name="is_login",type=Column.TYPE_BOOLEAN)     public Boolean isLogin;     @Table.Column(name="weight",type=Column.TYPE_DOUBLE)     public Double weight; }2. 初始化对象:SQLiteDatabase db = context.openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null); DbSqlite dbSqlite = new DbSqlite(db); IBaseDao userDAO = DaoFactory.createGenericDao(dbSqlite, UserModel.class);3. 创建表:userDAO.createTable(); 4. Insert 记录:UserModel user = new UserModel(); user.userName = "darcy"; user.isLogin = true; user.weight = 60.5; user.bornDate = new Date(); byte[] picture = {0x1,0x2,0x3,0x4}; user.pictrue = picture; userDAO.insert(user);5. Update 记录:UserModel user = new UserModel(); user.weight = 88.0; userDAO.update(user, "user_name=?", "darcy");6. 查询://单条结果查询 UserModel user = userDAO.queryFirstRecord("user_name=?", "darcy"); //一般查询 List userList = userDAO.query("user_name=? and weight > ?", "darcy" , "60"); //分页查询 PagingList pagingList = userDAO.pagingQuery(null, null, 1, 3);7. 事务支持:DBTransaction.transact(mDb, new DBTransaction.DBTransactionInterface() {         @Override         public void onTransact() {             // to do                 } };8. 更新表(目前只支持添加字段)@Table(name="t_user" , version=2) //修改表版本 public class UserModel {     //members above...     //new columns     @Table.Column(name="new_column_1",type=Column.TYPE_INTEGER)     public Integer newColumn;     @Table.Column(name="new_column_2",type=Column.TYPE_INTEGER)     public Integer newColumn2; } userDAO.updateTable();缺点和不足:还没支持多对一或者一多的关系没支持联合主键没支持表的外键设计其他...实例:SqliteLookup(Android内查看Sqlite数据库利器): https://github.com/YeDaxia/SqliteLookup 标签:SQLiteUtils
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值