package org.aisen.android.component.orm;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.text.TextUtils;
import com.alibaba.fastjson.JSON;
import org.aisen.android.common.utils.Logger;
import org.aisen.android.component.orm.extra.AutoIncrementTableColumn;
import org.aisen.android.component.orm.extra.Extra;
import org.aisen.android.component.orm.extra.TableColumn;
import org.aisen.android.component.orm.extra.TableInfo;
import org.aisen.android.component.orm.utils.FieldUtils;
import org.aisen.android.component.orm.utils.SqlUtils;
import org.aisen.android.component.orm.utils.TableInfoUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Hashtable;
import java.util.List;
/**
* 面向对象的数据库操作帮助类,支持App内部、Sdcard建库,dbName表示不同的库<br/>
* DB版本升级,即version发生升级的时候,默认清空数据库<br/>
* DB版本不变,Class字段增加字段时,Table会自动增加相对应的一列,Sqlite不支持Table的自动删除操作。<br/>
*
* @author wangdan
*
*/
public class SqliteUtility {
public static final String TAG = "SqliteUtility";
private static Hashtable<String, SqliteUtility> dbCache = new Hashtable<String, SqliteUtility>();
private String dbName;
private SQLiteDatabase db;
SqliteUtility(String dbName, SQLiteDatabase db) {
this.db = db;
this.dbName = dbName;
dbCache.put(dbName, this);
Logger.d(TAG, "将库 %s 放到缓存中", dbName);
}
public static SqliteUtility getInstance() {
return getInstance(SqliteUtilityBuilder.DEFAULT_DB);
}
public static SqliteUtility getInstance(String dbName) {
return dbCache.get(dbName);
}
/*******************************************开始Select系列方法****************************************************/
public <T> T selectById(Extra extra, Class<T> clazz, Object id) {
try {
TableInfo tableInfo = checkTable(clazz);
String selection = String.format(" %s = ? ", tableInfo.getPrimaryKey().getColumn());
String extraSelection = SqlUtils.appendExtraWhereClause(extra);
if (!TextUtils.isEmpty(extraSelection))
selection = String.format("%s and %s", selection, extraSelection);
List<String> selectionArgList = new ArrayList<String>();
selectionArgList.add(String.valueOf(id));
String[] extraSelectionArgs = SqlUtils.appendExtraWhereArgs(extra);
if (extraSelectionArgs != null && extraSelectionArgs.length > 0)
selectionArgList.addAll(Arrays.asList(extraSelectionArgs));
String[] selectionArgs = selectionArgList.toArray(new String[0]);
List<T> list = select(clazz, selection, selectionArgs, null, null, null, null);
if (list.size() > 0) {
return list.get(0);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public <T> List<T> select(Extra extra, Class<T> clazz) {
String selection = SqlUtils.appendExtraWhereClause(extra);
String[] selectionArgs = SqlUtils.appendExtraWhereArgs(extra);
return select(clazz, selection, selectionArgs, null, null, null, null);
}
public <T> List<T> select(Class<T> clazz, String selection, String[] selectionArgs) {
return select(clazz, selection, selectionArgs, null, null, null, null);
}
public <T> List<T> select(Class<T> clazz, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy, String limit) {
TableInfo tableInfo = checkTable(clazz);
ArrayList<T> list = new ArrayList<T>();
if (Logger.DEBUG) {
Logger.d(TAG, " method[select], table[%s], selection[%s], selectionArgs%s, groupBy[%s], having[%s], orderBy[%s], limit[%s] ",
tableInfo.getTableName(), selection, JSON.toJSON(selectionArgs), String.valueOf(groupBy), String.valueOf(having), String.valueOf(orderBy), String.valueOf(limit));
}
List<String> columnList = new ArrayList<String>();
columnList.add(tableInfo.getPrimaryKey().getColumn());
for (TableColumn tableColumn : tableInfo.getColumns())
columnList.add(tableColumn.getColumn());
long start = System.currentTimeMillis();
Cursor cursor = db.query(tableInfo.getTableName(), columnList.toArray(new String[0]),
selection, selectionArgs, groupBy, having, orderBy, limit);
Logger.d(TAG, "table[%s] 查询数据结束,耗时 %s ms", tableInfo.getTableName(), String.valueOf(System.currentTimeMillis() - start));
start = System.currentTimeMillis();
try {
if (cursor.moveToFirst()) {
do {
try {
T entity = clazz.newInstance();
// 绑定主键
bindSelectValue(entity, cursor, tableInfo.getPrimaryKey());
// 绑定其他数据
for (TableColumn column : tableInfo.getColumns())
bindSelectValue(entity, cursor, column);
list.add(entity);
} catch (Exception e) {
e.printStackTrace();
}
} while (cursor.moveToNext());
}
} finally {
cursor.close();
}
Logger.d(TAG, "table[%s], 设置数据结束,耗时 %s ms", tableInfo.getTableName(), String.valueOf(System.currentTimeMillis() - start));
Logger.d(TAG, "查询到数据 %d 条", list.size());
return list;
}
/*******************************************开始Insert系列方法****************************************************/
/**
* 如果主键实体已经存在,则忽略插库
*
* @param extra
* @param entities
*/
public <T> void insert(Extra extra, T... entities) {
try {
if (entities != null && entities.length > 0)
insert(extra, Arrays.asList(entities));
else
Logger.d(TAG, "method[insert(Extra extra, T... entities)], entities is empty");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 如果主键实体已经存在,使用新的对象存库
*
* @param extra
* @param entities
*/
public <T> void insertOrReplace(Extra extra, T... entities) {
try {
if (entities != null && entities.length > 0)
insert(extra, Arrays.asList(entities), "INSERT OR REPLACE INTO ");
else
Logger.d(TAG, "method[insertOrReplace(Extra extra, T... entities)], entities is empty");
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> void insert(Extra extra, List<T> entityList) {
try {
insert(extra, entityList, "INSERT OR IGNORE INTO ");
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> void insertOrReplace(Extra extra, List<T> entityList) {
try {
insert(extra, entityList, "INSERT OR REPLACE INTO ");
} catch (Exception e) {
e.printStackTrace();
}
}
private <T> void insert(Extra extra, List<T> entityList, String insertInto) {
if (entityList == null || entityList.size() == 0) {
Logger.d(TAG, "method[insert(Extra extra, List<T> entityList)], entityList is empty");
return;
}
TableInfo tableInfo = checkTable(entityList.get(0).getClass());
synchronized (tableInfo) {
long start = System.currentTimeMillis();
db.beginTransaction();
try {
String sql = SqlUtils.createSqlInsert(insertInto, tableInfo);
Logger.v(TAG, insertInto + " sql = %s", sql);
SQLiteStatement insertStatement = db.compileStatement(sql);
long bindTime = 0;
long startTime = System.currentTimeMillis();
for (T entity : entityList) {
bindInsertValues(extra, insertStatement, tableInfo, entity);
bindTime += (System.currentTimeMillis() - startTime);
startTime = System.currentTimeMillis();
insertStatement.execute();
}
Logger.d(TAG, "bindvalues 耗时 %s ms", bindTime + "");
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Logger.d(TAG, "表 %s %s 数据 %d 条, 执行时间 %s ms",
tableInfo.getTableName(),
insertInto,
entityList.size(),
String.valueOf(System.currentTimeMillis() - start));
// 只有一条数据,且主键为自增主键
if (entityList.size() == 1 && tableInfo.getPrimaryKey() instanceof AutoIncrementTableColumn) {
Cursor cursor = null;
try {
cursor = db.rawQuery("select last_insert_rowid() from " + tableInfo.getTableName(), null);
if(cursor.moveToFirst()) {
int newId = cursor.getInt(0);
Logger.d(TAG, "表%s自增主键[%d]", tableInfo.getTableName(), newId);
T bean = entityList.get(0);
try {
tableInfo.getPrimaryKey().getField().setAccessible(true);
tableInfo.getPrimaryKey().getField().set(bean, newId);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
}
}
/*******************************************开始Update系列方法****************************************************/
public <T> void update(Extra extra, T... entities) {
innerUpdate(extra, Arrays.asList(entities));
}
public <T> void update(Extra extra, List<T> entityList) {
innerUpdate(extra, entityList);
}
private <T> void innerUpdate(Extra extra, List<T> entityList) {
try {
if (entityList != null && entityList.size() > 0) {
TableInfo tableInfo = checkTable(entityList.get(0).getClass());
for (int i = 0; i < entityList.size(); i++) {
T t = entityList.get(i);
tableInfo.getPrimaryKey().getField().setAccessible(true);
Object id = tableInfo.getPrimaryKey().getField().get(t);
String whereClause = String.format(" %s = ? ", tableInfo.getPrimaryKey().getColumn());
String extraSelection = SqlUtils.appendExtraWhereClause(extra);
if (!TextUtils.isEmpty(extraSelection))
whereClause = String.format("%s and %s", whereClause, extraSelection);
List<String> selectionArgList = new ArrayList<>();
selectionArgList.add(String.valueOf(id));
String[] extraSelectionArgs = SqlUtils.appendExtraWhereArgs(extra);
if (extraSelectionArgs != null && extraSelectionArgs.length > 0)
selectionArgList.addAll(Arrays.asList(extraSelectionArgs));
String[] whereArgs = selectionArgList.toArray(new String[0]);
ContentValues values = new ContentValues();
for (TableColumn colunm : tableInfo.getColumns()) {
bindValue(values, colunm, t);
}
int rowId = db.update(tableInfo.getTableName(), values, whereClause, whereArgs);
if (Logger.DEBUG) {
Logger.d(TAG, " method[update], table[%s], whereClause[%s], whereArgs[%s], rowId[%d]",
tableInfo.getTableName(), whereClause, JSON.toJSON(whereArgs), rowId);
}
}
}
else {
Logger.d(TAG, "method[update(Extra extra, T... entities)], entities is empty");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> int update(Class<?> clazz, ContentValues values, String whereClause, String[] whereArgs) {
try {
TableInfo tableInfo = checkTable(clazz);
return db.update(tableInfo.getTableName(), values, whereClause, whereArgs);
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
/*******************************************开始Delete系列方法****************************************************/
public <T> void deleteAll(Extra extra, Class<T> clazz) {
try {
TableInfo tableInfo = checkTable(clazz);
String where = SqlUtils.appendExtraWhereClauseSql(extra);
if (!TextUtils.isEmpty(where))
where = " where " + where;
String sql = "DELETE FROM '" + tableInfo.getTableName() + "' " + where;
Logger.d(TAG, "method[delete] table[%s], sql[%s]", tableInfo.getTableName(), sql);
long start = System.currentTimeMillis();
db.execSQL(sql);
Logger.d(TAG, "表 %s 清空数据, 耗时 %s ms", tableInfo.getTableName(), String.valueOf(System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> void deleteById(Extra extra, Class<T> clazz, Object id) {
try {
TableInfo tableInfo = checkTable(clazz);
String whereClause = String.format(" %s = ? ", tableInfo.getPrimaryKey().getColumn());
String extraWhereClause = SqlUtils.appendExtraWhereClause(extra);
if (!TextUtils.isEmpty(extraWhereClause))
whereClause = String.format("%s and %s", whereClause, extraWhereClause);
List<String> whereArgList = new ArrayList<String>();
whereArgList.add(String.valueOf(id));
String[] extraWhereArgs = SqlUtils.appendExtraWhereArgs(extra);
if (extraWhereArgs != null && extraWhereArgs.length > 0)
whereArgList.addAll(Arrays.asList(extraWhereArgs));
String[] whereArgs = whereArgList.toArray(new String[0]);
if (Logger.DEBUG) {
Logger.d(TAG, " method[deleteById], table[%s], id[%s], whereClause[%s], whereArgs%s ",
tableInfo.getTableName(), String.valueOf(id), whereClause, JSON.toJSON(whereArgs));
}
long start = System.currentTimeMillis();
int rowCount = db.delete(tableInfo.getTableName(), whereClause, whereArgs);
Logger.d(TAG, "表 %s 删除数据 %d 条, 耗时 %s ms", tableInfo.getTableName(), rowCount, String.valueOf(System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> void delete(Class<T> clazz, String whereClause, String[] whereArgs) {
try {
TableInfo tableInfo = checkTable(clazz);
long start = System.currentTimeMillis();
int rowCount = db.delete(tableInfo.getTableName(), whereClause, whereArgs);
if (Logger.DEBUG) {
Logger.d(TAG, "method[delete], table[%s], whereClause[%s], whereArgs%s ",
tableInfo.getTableName(), whereClause, JSON.toJSON(whereArgs));
}
Logger.d(TAG, "表 %s 删除数据 %d 条,耗时 %s ms", tableInfo.getTableName(), rowCount, String.valueOf(System.currentTimeMillis() - start));
} catch (Exception e) {
e.printStackTrace();
}
}
/*******************************************系列统计的方法****************************************************/
public long sum(Class<?> clazz, String column, String whereClause, String[] whereArgs) {
TableInfo tableInfo = checkTable(clazz);
if (TextUtils.isEmpty(column))
return 0;
String sql = null;
if (TextUtils.isEmpty(whereClause)) {
whereArgs = null;
sql = String.format(" select sum(%s) as _sum_ from %s ", column, tableInfo.getTableName());
}
else {
sql = String.format(" select sum(%s) as _sum_ from %s where %s ", column, tableInfo.getTableName(), whereClause);
}
Logger.d(TAG, "sum() --- > " + sql);
Logger.d(TAG, whereArgs);
try {
long time = System.currentTimeMillis();
Cursor cursor = db.rawQuery(sql, whereArgs);
if (cursor.moveToFirst()) {
long sum = cursor.getLong(cursor.getColumnIndex("_sum_"));
Logger.d(TAG, "sum = %s 耗时%sms", String.valueOf(sum) ,String.valueOf(System.currentTimeMillis() - time));
cursor.close();
return sum;
}
} catch (Exception e) {
Logger.printExc(SqliteUtility.class, e);
}
return 0;
}
public long count(Class<?> clazz, String whereClause, String[] whereArgs) {
TableInfo tableInfo = checkTable(clazz);
String sql = null;
if (TextUtils.isEmpty(whereClause)) {
whereArgs = null;
sql = String.format(" select count(*) as _count_ from %s ", tableInfo.getTableName());
}
else {
sql = String.format(" select count(*) as _count_ from %s where %s ", tableInfo.getTableName(), whereClause);
}
Logger.d(TAG, "count --- > " + sql);
Logger.d(TAG, whereArgs);
try {
long time = System.currentTimeMillis();
Cursor cursor = db.rawQuery(sql, whereArgs);
if (cursor.moveToFirst()) {
long count = cursor.getLong(cursor.getColumnIndex("_count_"));
Logger.d(TAG, "count = %s 耗时%sms", String.valueOf(count) ,String.valueOf(System.currentTimeMillis() - time));
cursor.close();
return count;
}
} catch (Exception e) {
Logger.printExc(SqliteUtility.class, e);
}
return 0;
}
/*******************************************系列绑定数据的方法****************************************************/
private <T> void bindUpdateValues(ContentValues values, TableInfo tableInfo, T entity) {
for (int i = 0; i < tableInfo.getColumns().size(); i++) {
TableColumn column = tableInfo.getColumns().get(i);
bindValue(values, column, entity);
}
// createAt
long createAt = System.currentTimeMillis();
values.put(FieldUtils.CREATEAT, createAt);
}
private <T> void bindInsertValues(Extra extra, SQLiteStatement insertStatement, TableInfo tableInfo, T entity) {
int index = 1;
// 如果是自增主键,不设置值
if (tableInfo.getPrimaryKey() instanceof AutoIncrementTableColumn)
;
else
bindValue(insertStatement, index++, tableInfo.getPrimaryKey(), entity);
for (int i = 0; i < tableInfo.getColumns().size(); i++) {
TableColumn column = tableInfo.getColumns().get(i);
bindValue(insertStatement, index++, column, entity);
}
// owner
String owner = extra == null || TextUtils.isEmpty(extra.getOwner()) ? "" : extra.getOwner();
insertStatement.bindString(index++, owner);
// key
String key = extra == null || TextUtils.isEmpty(extra.getKey()) ? "" : extra.getKey();
insertStatement.bindString(index++, key);
// createAt
long createAt = System.currentTimeMillis();
insertStatement.bindLong(index, createAt);
}
private <T> void bindValue(ContentValues values, TableColumn column, T entity) {
// 通过反射绑定数据
try {
column.getField().setAccessible(true);
Object value = column.getField().get(entity);
if (value == null) {
return;
}
if (Logger.DEBUG) {
Logger.v(TAG, " method[bindValue_ContentValues], key[%s], value[%s]", column.getColumn(), value + "");
}
if ("object".equalsIgnoreCase(column.getDataType())) {
values.put(column.getColumn(), JSON.toJSONString(value));
}
else if ("INTEGER".equalsIgnoreCase(column.getColumnType())) {
values.put(column.getColumn(), Long.parseLong(value.toString()));
}
else if ("REAL".equalsIgnoreCase(column.getColumnType())) {
values.put(column.getColumn(), Double.parseDouble(value.toString()));
}
else if ("BLOB".equalsIgnoreCase(column.getColumnType())) {
values.put(column.getColumn(), (byte[]) value);
}
else if ("TEXT".equalsIgnoreCase(column.getColumnType())) {
values.put(column.getColumn(), value.toString());
}
} catch (Exception e) {
e.printStackTrace();
Logger.w(TAG, "属性 %s bindvalue 异常", column.getField().getName());
}
}
private <T> void bindValue(SQLiteStatement insertStatement, int index, TableColumn column, T entity) {
// 通过反射绑定数据
try {
column.getField().setAccessible(true);
Object value = column.getField().get(entity);
if (value == null) {
insertStatement.bindNull(index);
return;
}
if (Logger.DEBUG) {
Logger.v(TAG, " method[bindValue_SQLiteStatement], key[%s], value[%s]", column.getColumn(), value + "");
}
if ("object".equalsIgnoreCase(column.getDataType())) {
insertStatement.bindString(index, JSON.toJSONString(value));
}
else if ("INTEGER".equalsIgnoreCase(column.getColumnType())) {
insertStatement.bindLong(index, Long.parseLong(value.toString()));
}
else if ("REAL".equalsIgnoreCase(column.getColumnType())) {
insertStatement.bindDouble(index, Double.parseDouble(value.toString()));
}
else if ("BLOB".equalsIgnoreCase(column.getColumnType())) {
insertStatement.bindBlob(index, (byte[]) value);
}
else if ("TEXT".equalsIgnoreCase(column.getColumnType())) {
insertStatement.bindString(index, value.toString());
}
} catch (Exception e) {
e.printStackTrace();
Logger.w(TAG, "属性 %s bindvalue 异常", column.getField().getName());
}
}
private <T> void bindSelectValue(T entity, Cursor cursor, TableColumn column) {
Field field = column.getField();
field.setAccessible(true);
try {
if (field.getType().getName().equals("int") ||
field.getType().getName().equals("java.lang.Integer")) {
field.set(entity, cursor.getInt(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("long") ||
field.getType().getName().equals("java.lang.Long")) {
field.set(entity, cursor.getLong(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("float") ||
field.getType().getName().equals("java.lang.Float")) {
field.set(entity, cursor.getFloat(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("double") ||
field.getType().getName().equals("java.lang.Double")) {
field.set(entity, cursor.getDouble(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("boolean") ||
field.getType().getName().equals("java.lang.Boolean")) {
field.set(entity, Boolean.parseBoolean(cursor.getString(cursor.getColumnIndex(column.getColumn()))));
}
else if (field.getType().getName().equals("char") ||
field.getType().getName().equals("java.lang.Character")) {
field.set(entity, cursor.getString(cursor.getColumnIndex(column.getColumn())).toCharArray()[0]);
}
else if (field.getType().getName().equals("byte") ||
field.getType().getName().equals("java.lang.Byte")) {
field.set(entity, (byte) cursor.getInt(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("short") ||
field.getType().getName().equals("java.lang.Short")) {
field.set(entity, cursor.getShort(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("java.lang.String")) {
field.set(entity, cursor.getString(cursor.getColumnIndex(column.getColumn())));
}
else if (field.getType().getName().equals("[B")) {
field.set(entity, cursor.getBlob(cursor.getColumnIndex(column.getColumn())));
}
else {
String text = cursor.getString(cursor.getColumnIndex(column.getColumn()));
field.set(entity, JSON.parseObject(text, field.getGenericType()));
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 检查table是否已经存在<br/>
* 不存在,就自动创建<br/>
* 存在,检查Entity字段是否有增加,有则更新表<br/>
*
* @param clazz
*/
private <T> TableInfo checkTable(Class<T> clazz) {
TableInfo tableInfo = TableInfoUtils.exist(dbName, clazz);
if (tableInfo != null) {
;
}
else {
tableInfo = TableInfoUtils.newTable(dbName, db, clazz);
}
return tableInfo;
}
}