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