/*
* Copyright (C) 2013 litesuits.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.litesuits.orm.db;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.litesuits.orm.db.annotation.Column;
import com.litesuits.orm.db.annotation.Mapping;
import com.litesuits.orm.db.annotation.PrimaryKey;
import com.litesuits.orm.db.annotation.Table;
import com.litesuits.orm.db.assit.Checker;
import com.litesuits.orm.db.assit.Querier;
import com.litesuits.orm.db.assit.SQLBuilder;
import com.litesuits.orm.db.assit.Transaction;
import com.litesuits.orm.db.enums.AssignType;
import com.litesuits.orm.db.assit.SQLStatement;
import com.litesuits.orm.db.model.*;
import com.litesuits.orm.db.utils.DataUtil;
import com.litesuits.orm.db.utils.FieldUtil;
import com.litesuits.orm.log.OrmLog;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
/**
* 表管理
*
* @author MaTianyu
* @date 2013-6-16上午12:27:32
*/
public final class TableManager {
private static final String TAG = TableManager.class.getSimpleName();
private static final String ID[] = new String[]{"id", "_id"};
/**
* 数据库表信息
*/
private String dbName = "";
/**
* 这里放的是数据库表信息(表名、字段、建表语句...)
* 每个数据库对应一个
* key : Class Name
* value: {@link EntityTable}
*/
private final HashMap<String, SQLiteTable> mSqlTableMap = new HashMap<String, SQLiteTable>();
/**
* 这里放的是类的实体信息表(主键、属性、关系映射...)
* 全局单例
* key : Class Name
* value: {@link EntityTable}
*/
private final static HashMap<String, EntityTable> mEntityTableMap = new HashMap<String, EntityTable>();
public TableManager(String dbName, SQLiteDatabase db) {
this.dbName = dbName;
initSqlTable(db);
}
public void initSqlTable(SQLiteDatabase db) {
// 关键点:初始化全部数据库表
initAllTablesFromSQLite(db);
}
public void clearSqlTable() {
synchronized (mSqlTableMap) {
mSqlTableMap.clear();
}
}
/**
* 清空数据
*/
public void release() {
clearSqlTable();
mEntityTableMap.clear();
}
/**
* 检测表是否建立,没有则建一张新表。
*/
public EntityTable checkOrCreateTable(SQLiteDatabase db, Object entity) {
return checkOrCreateTable(db, entity.getClass());
}
/**
* 检测[数据库表]是否建立,没有则建一张新表。
*/
public synchronized EntityTable checkOrCreateTable(SQLiteDatabase db, Class claxx) {
// 关键点1:获取[实体表]
EntityTable table = getTable(claxx);
// 关键点2: 判断[数据库表]是否存在,是否需要新加列。
if (!checkExistAndColumns(db, table)) {
// 关键点3:新建[数据库表]并加入表队列
if (createTable(db, table)) {
putNewSqlTableIntoMap(table);
}
}
return table;
}
/**
* 检测[映射表]是否建立,没有则建一张新表。
*/
public synchronized void checkOrCreateMappingTable(SQLiteDatabase db, String tableName,
String column1, String column2) {
// 关键点1:获取[实体表]
EntityTable table = getMappingTable(tableName, column1, column2);
// 关键点2: 判断[数据库表]是否存在,是否需要新加列。
if (!checkExistAndColumns(db, table)) {
// 关键点3:新建[数据库表]并加入表队列
if (createTable(db, table)) {
putNewSqlTableIntoMap(table);
}
}
}
/**
* 仅仅检测[数据库表]是否建立
*/
public boolean isSQLMapTableCreated(String tableName1, String tableName2) {
return mSqlTableMap.get(getMapTableName(tableName1, tableName2)) != null;
}
/**
* 仅仅检测[数据库表]是否建立
*/
public boolean isSQLTableCreated(String tableName) {
return mSqlTableMap.get(tableName) != null;
}
/**
* 检查表是否存在,存在的话检查是否需要改动,添加列字段。
* 注:sqlite仅仅支持表改名、表添加列两种alter方式。表中修改、刪除列是不被直接支持的。
* 不能新加主键:The column may not have a PRIMARY KEY or UNIQUE constraint.
* <p> http://www.sqlite.org/lang_altertable.html
*/
private boolean checkExistAndColumns(SQLiteDatabase db, EntityTable entityTable) {
SQLiteTable sqlTable = mSqlTableMap.get(entityTable.name);
if (sqlTable != null) {
if (OrmLog.isPrint) {
OrmLog.d(TAG, "Table [" + entityTable.name + "] Exist");
}
if (!sqlTable.isTableChecked) {
// 表仅进行一次检查,检验是否有新字段加入。
sqlTable.isTableChecked = true;
if (OrmLog.isPrint) {
OrmLog.i(TAG, "Table [" + entityTable.name + "] check column now.");
}
if (entityTable.key != null) {
if (sqlTable.columns.get(entityTable.key.column) == null) {
SQLStatement stmt = SQLBuilder.buildDropTable(sqlTable.name);
stmt.execute(db);
if (OrmLog.isPrint) {
OrmLog.i(TAG, "Table [" + entityTable.name + "] Primary Key has changed, " +
"so drop and recreate it later.");
}
return false;
}
}
if (entityTable.pmap != null) {
ArrayList<String> newColumns = new ArrayList<String>();
for (String col : entityTable.pmap.keySet()) {
if (sqlTable.columns.get(col) == null) {
newColumns.add(col);
}
}
if (!Checker.isEmpty(newColumns)) {
for (String col : newColumns) {
sqlTable.columns.put(col, 1);
}
int sum = insertNewColunms(db, entityTable.name, newColumns);
if (OrmLog.isPrint) {
if (sum > 0) {
OrmLog.i(TAG,
"Table [" + entityTable.name + "] add " + sum + " new column : " + newColumns);
} else {
OrmLog.e(TAG,
"Table [" + entityTable.name + "] add " + sum + " new column error : " +
newColumns);
}
}
}
}
}
return true;
}
if (OrmLog.isPrint) {
OrmLog.d(TAG, "Table [" + entityTable.name + "] Not Exist");
}
return false;
}
/**
* 将Sql Table放入存储集合
*/
private void putNewSqlTableIntoMap(EntityTable table) {
if (OrmLog.isPrint) {
OrmLog.i(TAG, "Table [" + table.name + "] Create Success");
}
SQLiteTable sqlTable = new SQLiteTable();
sqlTable.name = table.name;
sqlTable.columns = new HashMap<String, Integer>();
if (table.key != null) {
sqlTable.columns.put(table.key.column, 1);
}
if (table.pmap != null) {
for (String col : table.pmap.keySet()) {
sqlTable.columns.put(col, 1);
}
}
// 第一次建表,不用检查
sqlTable.isTableChecked = true;
mSqlTableMap.put(sqlTable.name, sqlTable);
}
/**
* 初始化全部表及其列名,初始化失败,则无法进行下去。
*/
private void initAllTablesFromSQLite(SQLiteDatabase db) {
synchronized (mSqlTableMap) {
if (Checker.isEmpty(mSqlTableMap)) {
if (OrmLog.isPrint) {
OrmLog.i(TAG, "Initialize SQL table start--------------------->");
}
SQLStatement st = SQLBuilder.buildTableObtainAll();
final EntityTable table = getTable(SQLiteTable.class, false);
Querier.doQuery(db, st, new Querier.CursorParser() {
@Override
public void parseEachCursor(SQLiteDatabase db, Cursor c) throws Exception {
SQLiteTable sqlTable = new SQLiteTable();
DataUtil.injectDataToObject(c, sqlTable, table);
ArrayList<String> colS = getAllColumnsFromSQLite(db, sqlTable.name);
if (Checker.isEmpty(colS)) {
// 如果读数据库失败了,那么解析建表语句
OrmLog.e(TAG, "读数据库失败了,开始解析建表语句");
colS = transformSqlToColumns(sqlTable.sql);
}
sqlTable.columns = new HashMap<String, Integer>();
for (String col : colS) {
sqlTable.columns.put(col, 1);
}
if (OrmLog.isPrint) {
OrmLog.i(TAG, "Find One SQL Table: " + sqlTable);
OrmLog.i(TAG, "Table Column: " + colS);
}
mSqlTableMap.put(sqlTable.name, sqlTable);
}
});
if (OrmLog.isPrint) {
OrmLog.i(TAG, "Initialize SQL table end ---------------------> " + mSqlTableMap.size());
}
}
}
}
/**
* 插入新列
*/
private int insertNewColunms(SQLiteDatabase db, final String tableName, final List<String> columns) {
Integer size = null;
if (!Checker.isEmpty(columns)) {
size = Transaction.execute(db, new Transaction.Worker<Integer>() {
@Override
public Integer doTransaction(SQLiteDatabase db) {
for (String c : columns) {
SQLStatement stmt = SQLBuilder.buildAddColumnSql(tableName, c);
stmt.execute(db);
}
return columns.size();
}
});
}
return size == null ? 0 : size;
}
/**
* 建立新表
*/
private boolean createTable(SQLiteDatabase db, EntityTable table) {
return SQLBuilder.buildCreateTable(table).execute(db);
}
/**
* 数据库分析
* 通过读数据库得到一张表的全部列名
*/
public ArrayList<String> getAllColumnsFromSQLite(SQLiteDatabase db, final String tableName) {
final EntityTable table = getTable(SQLiteColumn.class, false);
final ArrayList<String> list = new ArrayList<String>();
SQLStatement st = SQLBuilder.buildColumnsObtainAll(tableName);
Querier.doQuery(db, st, new Querier.CursorParser() {
@Override
public void parseEachCursor(SQLiteDatabase db, Cursor c) throws Exception {
SQLiteColumn col = new SQLiteColumn();
DataUtil.injectDataToObject(c, col, table);
list.add(col.name);
}
});
return list;
}
/**
* 语义分析
* 依据表的sql“CREATE TABLE”建表语句得到一张表的全部列名。
*/
public ArrayList<String> transformSqlToColumns(String sql) {
if (sql != null) {
int start = sql.indexOf("(");
int end = sql.lastIndexOf(")");
if (start > 0 && end > 0) {
sql = sql.substring(start + 1, end);
String cloumns[] = sql.split(",");
ArrayList<String> colList = new ArrayList<String>();
for (String col : cloumns) {
col = col.trim();
int endS = col.indexOf(" ");
if (endS > 0) {
col = col.substring(0, endS);
}
colList.add(col);
}
OrmLog.e(TAG, "降级:语义分析表结构(" + colList.toString() + " , Origin SQL is: " + sql);
return colList;
}
}
return null;
}
/* —————————————————————————— 静态私有方法 ———————————————————————— */
/**
* 获取缓存实体表信息
*/
private static EntityTable getEntityTable(String name) {
return mEntityTableMap.get(name);
}
/**
* 缓存的实体表信息
*
* @return 返回前一个和此Key相同的Value,没有则返回null。
*/
private static EntityTable putEntityTable(String tableName, EntityTable entity) {
return mEntityTableMap.put(tableName, entity);
}
/**
* 获取映射表信息(Entity Table)
* 注意映射表存储在MAP中,key 为 database name + table name, value 为 entity table。
*
* @return {@link EntityTable}
*/
private EntityTable getMappingTable(String tableName, String column1, String column2) {
EntityTable table = getEntityTable(dbName + tableName);
if (table == null) {
table = new EntityTable();
table.name = tableName;
table.pmap = new LinkedHashMap<String, Property>();
table.pmap.put(column1, null);
table.pmap.put(column2, null);
TableManager.putEntityTable(dbName + tableName, table);
}
return table;
}
/* —————————————————————————— 静态公共方法 ———————————————————————— */
/**
* 根据实体生成表信息,一定需要PrimaryKey
*/
public static EntityTable getTable(Object entity) {
return getTable(entity.getClass(), true);
}
/**
* 根据类生成表信息,一定需要PrimaryKey
*/
public static EntityTable getTable(Class<?> claxx) {
return getTable(claxx, true);
}
/**
* 获取实体表信息(Entity Table)
* 注意映射表存储在MAP中,key 为 class name, value 为 entity table。
*
* @return {@link EntityTable}
*/
public static synchronized EntityTable getTable(Class<?> claxx, boolean needPK) {
EntityTable table = getEntityTable(claxx.getName());
//if(OrmLog.isPrint)OrmLog.i(TAG, "table : " + table + " , claxx: " + claxx);
if (table == null) {
table = new EntityTable();
table.claxx = claxx;
table.name = getTableName(claxx);
table.pmap = new LinkedHashMap<String, Property>();
List<Field> fields = FieldUtil.getAllDeclaredFields(claxx);
for (Field f : fields) {
if (FieldUtil.isInvalid(f)) {
continue;
}
// 获取列名,每个属性都有,没有注解默认取属性名
Column col = f.getAnnotation(Column.class);
String column = col != null ? col.value() : f.getName();
Property p = new Property(column, f);
// 主键判断
PrimaryKey key = f.getAnnotation(PrimaryKey.class);
if (key != null) {
// 主键不加入属性Map
table.key = new Primarykey(p, key.value());
// 主键为系统分配,对类型有要求
checkPrimaryKey(table.key);
} else {
//ORM handle
Mapping mapping = f.getAnnotation(Mapping.class);
if (mapping != null) {
table.addMapping(new MapProperty(p, mapping.value()));
} else {
table.pmap.put(p.column, p);
}
}
}
if (table.key == null) {
for (String col : table.pmap.keySet()) {
for (String id : ID) {
if (id.equalsIgnoreCase(col)) {
Property p = table.pmap.get(col);
if (p.field.getType() == String.class) {
// 主键移除属性Map
table.pmap.remove(col);
table.key = new Primarykey(p, AssignType.BY_MYSELF);
break;
} else if (FieldUtil.isNumber(p.field.getType())) {
// 主键移除属性Map
table.pmap.remove(col);
table.key = new Primarykey(p, AssignType.AUTO_INCREMENT);
break;
}
}
}
if (table.key != null) {
break;
}
}
}
if (needPK && table.key == null) {
throw new RuntimeException(
"你必须为[" + table.claxx.getSimpleName() + "]设置主键(you must set the primary key...)" +
"\n 提示:在对象的属性上加PrimaryKey注解来设置主键。");
}
putEntityTable(claxx.getName(), table);
}
return table;
}
private static void checkPrimaryKey(Primarykey key) {
if (key.isAssignedBySystem()) {
if (!FieldUtil.isNumber(key.field.getType())) {
throw new RuntimeException(
AssignType.AUTO_INCREMENT
+ " Auto increment primary key must be a number ...\n " +
"错误提示:自增主键必须设置为数字类型");
}
} else if (key.isAssignedByMyself()) {
if (String.class != key.field.getType() && !FieldUtil.isNumber(key.field.getType())) {
throw new RuntimeException(
AssignType.BY_MYSELF
+ " Custom primary key must be string or number ...\n " +
"错误提示:自定义主键值必须为String或者Number类型");
}
} else {
throw new RuntimeException(
" Primary key without Assign Type ...\n " +
"错误提示:主键无类型");
}
}
/**
* 根据类自动生成表名字
*/
public static String getTableName(Class<?> claxx) {
Table anno = claxx.getAnnotation(Table.class);
if (anno != null) {
return anno.value();
} else {
return claxx.getName().replaceAll("\\.", "_");
}
}
public static String getMapTableName(Class c1, Class c2) {
return getMapTableName(getTableName(c1), getTableName(c2));
}
public static String getMapTableName(EntityTable t1, EntityTable t2) {
return getMapTableName(t1.name, t2.name);
}
public static String getMapTableName(String tableName1, String tableName2) {
if (tableName1.compareTo(tableName2) < 0) {
return tableName1 + "_" + tableName2;
} else {
return tableName2 + "_" + tableName1;
}
}
}