package com.android.pc.ioc.db.sqlite; import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.text.TextUtils; import com.android.pc.ioc.app.Ioc; import com.android.pc.ioc.db.table.DbModel; import com.android.pc.ioc.db.table.Id; import com.android.pc.ioc.db.table.KeyValue; import com.android.pc.ioc.db.table.Table; import com.android.pc.ioc.db.table.TableUtils; public class DbUtils { // *************************************** create instance **************************************************** /** * key: dbName */ private static HashMap<String, DbUtils> daoMap = new HashMap<String, DbUtils>(); private SQLiteDatabase database; private DaoConfig config; private boolean debug = false; private boolean allowTransaction = false; private DbUtils(DaoConfig config) { if (config == null) { throw new IllegalArgumentException("daoConfig may not be null"); } if (config.getContext() == null) { throw new IllegalArgumentException("context mey not be null"); } String sdCardPath = config.getSdCardPath(); if (TextUtils.isEmpty(sdCardPath)) { this.database = new SQLiteDbHelper(config).getWritableDatabase(); } else { this.database = createDbFileOnSDCard(config); } this.config = config; } private synchronized static DbUtils getInstance(DaoConfig daoConfig) { DbUtils dao = daoMap.get(daoConfig.getDbName()); if (dao == null) { dao = new DbUtils(daoConfig); daoMap.put(daoConfig.getDbName(), dao); } else { dao.config = daoConfig; } return dao; } public static DbUtils create(Context context) { DaoConfig config = new DaoConfig(context); return getInstance(config); } public static DbUtils create(Context context, String dbName) { DaoConfig config = new DaoConfig(context); config.setDbName(dbName); return getInstance(config); } public static DbUtils create(Context context, String sdCardPath, String dbName) { DaoConfig config = new DaoConfig(context); config.setSdCardPath(sdCardPath); config.setDbName(dbName); return getInstance(config); } public static DbUtils create(Context context, String dbName, int dbVersion, DbUpgradeListener dbUpgradeListener) { DaoConfig config = new DaoConfig(context); config.setDbName(dbName); config.setDbVersion(dbVersion); config.setDbUpgradeListener(dbUpgradeListener); return getInstance(config); } public static DbUtils create(Context context, String sdCardPath, String dbName, int dbVersion, DbUpgradeListener dbUpgradeListener) { DaoConfig config = new DaoConfig(context); config.setSdCardPath(sdCardPath); config.setDbName(dbName); config.setDbVersion(dbVersion); config.setDbUpgradeListener(dbUpgradeListener); return getInstance(config); } public static DbUtils create(DaoConfig daoConfig) { return getInstance(daoConfig); } public DbUtils configDebug(boolean debug) { this.debug = debug; return this; } public DbUtils configAllowTransaction(boolean allowTransaction) { this.allowTransaction = allowTransaction; return this; } public SQLiteDatabase getDatabase() { return database; } public String getSdCardPath() { return config.getSdCardPath(); } // *********************************************** operations ******************************************************** public void saveOrUpdate(Object entity) { try { beginTransaction(); saveOrUpdateWithoutTransaction(entity); setTransactionSuccessful(); } finally { endTransaction(); } } public void saveOrUpdateAll(List<?> entities) { try { beginTransaction(); for (Object entity : entities) { saveOrUpdateWithoutTransaction(entity); } setTransactionSuccessful(); } finally { endTransaction(); } } public void replace(Object entity) { try { beginTransaction(); replaceWithoutTransaction(entity); setTransactionSuccessful(); } finally { endTransaction(); } } public void replaceAll(List<?> entities) { try { beginTransaction(); for (Object entity : entities) { replaceWithoutTransaction(entity); } setTransactionSuccessful(); } finally { endTransaction(); } } public void save(Object entity) { try { beginTransaction(); saveWithoutTransaction(entity); setTransactionSuccessful(); } finally { endTransaction(); } } public void saveAll(List<?> entities) { try { beginTransaction(); for (Object entity : entities) { saveWithoutTransaction(entity); } setTransactionSuccessful(); } finally { endTransaction(); } } public boolean saveBindingId(Object entity) { boolean result = false; try { beginTransaction(); result = saveBindingIdWithoutTransaction(entity); setTransactionSuccessful(); } finally { endTransaction(); } return result; } public void saveBindingIdAll(List<?> entities) { try { beginTransaction(); for (Object entity : entities) { if (!saveBindingIdWithoutTransaction(entity)) { Ioc.getIoc().getLogger().e("saveBindingId error, transaction will not commit!"); } } setTransactionSuccessful(); } finally { endTransaction(); } } public void delete(Object entity) { if (!tableIsExist(entity.getClass())) return; try { beginTransaction(); deleteWithoutTransaction(entity); setTransactionSuccessful(); } finally { endTransaction(); } } public void deleteAll(List<?> entities) { if (entities == null || entities.size() < 1 || !tableIsExist(entities.get(0).getClass())) return; try { beginTransaction(); for (Object entity : entities) { deleteWithoutTransaction(entity); } setTransactionSuccessful(); } finally { endTransaction(); } } public void deleteById(Class<?> entityType, Object idValue) { if (!tableIsExist(entityType)) return; try { beginTransaction(); execNonQuery(SqlInfoBuilder.buildDeleteSqlInfo(entityType, idValue)); setTransactionSuccessful(); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } finally { endTransaction(); } } public void delete(Class<?> entityType, WhereBuilder whereBuilder) { if (!tableIsExist(entityType)) return; try { beginTransaction(); SqlInfo sql = SqlInfoBuilder.buildDeleteSqlInfo(entityType, whereBuilder); execNonQuery(sql); setTransactionSuccessful(); } finally { endTransaction(); } } public void update(Object entity) { if (!tableIsExist(entity.getClass())) return; try { beginTransaction(); updateWithoutTransaction(entity); setTransactionSuccessful(); } finally { endTransaction(); } } public void updateAll(List<?> entities) { if (entities == null || entities.size() < 1 || !tableIsExist(entities.get(0).getClass())) return; try { beginTransaction(); for (Object entity : entities) { updateWithoutTransaction(entity); } setTransactionSuccessful(); } finally { endTransaction(); } } public void update(Object entity, WhereBuilder whereBuilder) { if (!tableIsExist(entity.getClass())) return; try { beginTransaction(); execNonQuery(SqlInfoBuilder.buildUpdateSqlInfo(this, entity, whereBuilder)); setTransactionSuccessful(); } finally { endTransaction(); } } @SuppressWarnings("unchecked") public <T> T findById(Class<T> entityType, Object idValue) { if (!tableIsExist(entityType)) return null; Id id = Table.get(entityType).getId(); Selector selector = Selector.from(entityType).where(id.getColumnName(), "=", idValue); String sql = selector.limit(1).toString(); long seq = CursorUtils.FindCacheSequence.getSeq(); findTempCache.setSeq(seq); Object obj = findTempCache.get(sql); if (obj != null) { return (T) obj; } Cursor cursor = execQuery(sql); try { if (cursor.moveToNext()) { T entity = (T) CursorUtils.getEntity(this, cursor, entityType, seq); findTempCache.put(sql, entity); return entity; } } finally { IOUtils.closeQuietly(cursor); } return null; } @SuppressWarnings("unchecked") public <T> T findFirst(Selector selector) { if (!tableIsExist(selector.getEntityType())) return null; String sql = selector.limit(1).toString(); long seq = CursorUtils.FindCacheSequence.getSeq(); findTempCache.setSeq(seq); Object obj = findTempCache.get(sql); if (obj != null) { return (T) obj; } Cursor cursor = execQuery(sql); try { if (cursor.moveToNext()) { T entity = (T) CursorUtils.getEntity(this, cursor, selector.getEntityType(), seq); findTempCache.put(sql, entity); return entity; } } finally { IOUtils.closeQuietly(cursor); } return null; } public <T> T findFirst(Object entity) { if (!tableIsExist(entity.getClass())) return null; Selector selector = Selector.from(entity.getClass()); List<KeyValue> entityKvList = SqlInfoBuilder.entity2KeyValueList(this, entity); if (entityKvList != null) { WhereBuilder wb = WhereBuilder.b(); for (KeyValue keyValue : entityKvList) { wb.append(keyValue.getKey(), "=", keyValue.getValue()); } selector.where(wb); } return findFirst(selector); } @SuppressWarnings("unchecked") public <T> List<T> findAll(Selector selector) { if (!tableIsExist(selector.getEntityType())) return null; String sql = selector.toString(); long seq = CursorUtils.FindCacheSequence.getSeq(); findTempCache.setSeq(seq); Object obj = findTempCache.get(sql); if (obj != null) { return (List<T>) obj; } Cursor cursor = execQuery(sql); List<T> result = new ArrayList<T>(); try { while (cursor.moveToNext()) { T entity = (T) CursorUtils.getEntity(this, cursor, selector.getEntityType(), seq); result.add(entity); } findTempCache.put(sql, result); } finally { IOUtils.closeQuietly(cursor); } return result; } public <T> List<T> findAll(Object entity) { if (!tableIsExist(entity.getClass())) return null; Selector selector = Selector.from(entity.getClass()); List<KeyValue> entityKvList = SqlInfoBuilder.entity2KeyValueList(this, entity); if (entityKvList != null) { WhereBuilder wb = WhereBuilder.b(); for (KeyValue keyValue : entityKvList) { wb.append(keyValue.getKey(), "=", keyValue.getValue()); } selector.where(wb); } return findAll(selector); } public DbModel findDbModelFirst(SqlInfo sqlInfo) { Cursor cursor = execQuery(sqlInfo); try { if (cursor.moveToNext()) { return CursorUtils.getDbModel(cursor); } } finally { IOUtils.closeQuietly(cursor); } return null; } public DbModel findDbModelFirst(DbModelSelector selector) { if (!tableIsExist(selector.getEntityType())) return null; Cursor cursor = execQuery(selector.limit(1).toString()); try { if (cursor.moveToNext()) { return CursorUtils.getDbModel(cursor); } } finally { IOUtils.closeQuietly(cursor); } return null; } public List<DbModel> findDbModelAll(SqlInfo sqlInfo) { Cursor cursor = execQuery(sqlInfo); List<DbModel> dbModelList = new ArrayList<DbModel>(); try { while (cursor.moveToNext()) { dbModelList.add(CursorUtils.getDbModel(cursor)); } } finally { IOUtils.closeQuietly(cursor); } return dbModelList; } public List<DbModel> findDbModelAll(DbModelSelector selector) { if (!tableIsExist(selector.getEntityType())) return null; Cursor cursor = execQuery(selector.toString()); List<DbModel> dbModelList = new ArrayList<DbModel>(); try { while (cursor.moveToNext()) { dbModelList.add(CursorUtils.getDbModel(cursor)); } } finally { IOUtils.closeQuietly(cursor); } return dbModelList; } // ******************************************** config ****************************************************** public static class DaoConfig { private Context context; private String dbName = "xUtils.db"; // default db name private int dbVersion = 1; private DbUpgradeListener dbUpgradeListener; private String sdCardPath; public DaoConfig(Context context) { this.context = context; } public Context getContext() { return context; } public String getDbName() { return dbName; } public void setDbName(String dbName) { this.dbName = dbName; } public int getDbVersion() { return dbVersion; } public void setDbVersion(int dbVersion) { this.dbVersion = dbVersion; } public DbUpgradeListener getDbUpgradeListener() { return dbUpgradeListener; } public void setDbUpgradeListener(DbUpgradeListener dbUpgradeListener) { this.dbUpgradeListener = dbUpgradeListener; } public String getSdCardPath() { return sdCardPath; } public void setSdCardPath(String sdCardPath) { this.sdCardPath = sdCardPath; } } public interface DbUpgradeListener { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion); } private class SQLiteDbHelper extends SQLiteOpenHelper { private DbUpgradeListener mDbUpgradeListener; public SQLiteDbHelper(DaoConfig config) { super(config.getContext(), config.getDbName(), null, config.getDbVersion()); this.mDbUpgradeListener = config.getDbUpgradeListener(); } @Override public void onCreate(SQLiteDatabase db) { } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (mDbUpgradeListener != null) { mDbUpgradeListener.onUpgrade(db, oldVersion, newVersion); } else { try { dropDb(); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } } } private SQLiteDatabase createDbFileOnSDCard(DaoConfig config) { SQLiteDatabase result = null; File dbFile = new File(config.getSdCardPath(), config.getDbName()); boolean dbFileExists = dbFile.exists(); result = SQLiteDatabase.openOrCreateDatabase(dbFile, null); if (result != null) { int oldVersion = result.getVersion(); int newVersion = config.getDbVersion(); if (oldVersion != newVersion) { if (dbFileExists&&config.getDbUpgradeListener()!=null) { config.getDbUpgradeListener().onUpgrade(result, oldVersion, newVersion); } result.setVersion(newVersion); } } return result; } // ***************************** private operations with out transaction ***************************** private void saveOrUpdateWithoutTransaction(Object entity) { if (TableUtils.getIdValue(entity) != null) { updateWithoutTransaction(entity); } else { saveBindingIdWithoutTransaction(entity); } } private void replaceWithoutTransaction(Object entity) { createTableIfNotExist(entity.getClass()); execNonQuery(SqlInfoBuilder.buildReplaceSqlInfo(this, entity)); } private void saveWithoutTransaction(Object entity) { createTableIfNotExist(entity.getClass()); execNonQuery(SqlInfoBuilder.buildInsertSqlInfo(this, entity)); } private boolean saveBindingIdWithoutTransaction(Object entity) { createTableIfNotExist(entity.getClass()); List<KeyValue> entityKvList = SqlInfoBuilder.entity2KeyValueList(this, entity); if (entityKvList != null && entityKvList.size() > 0) { Table table = Table.get(entity.getClass()); ContentValues cv = new ContentValues(); DbUtils.fillContentValues(cv, entityKvList); Long id = database.insert(table.getTableName(), null, cv); if (id == -1) { return false; } table.getId().setValue2Entity(entity, id.toString()); return true; } return false; } private void deleteWithoutTransaction(Object entity) { try { execNonQuery(SqlInfoBuilder.buildDeleteSqlInfo(entity)); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } private void updateWithoutTransaction(Object entity) { try { execNonQuery(SqlInfoBuilder.buildUpdateSqlInfo(this, entity)); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } // ************************************************ tools *********************************** private static void fillContentValues(ContentValues contentValues, List<KeyValue> list) { if (list != null && contentValues != null) { for (KeyValue kv : list) { contentValues.put(kv.getKey(), kv.getValue().toString()); } } else { Ioc.getIoc().getLogger().w("List<KeyValue> is empty or ContentValues is empty!"); } } private void createTableIfNotExist(Class<?> entityType) { if (!tableIsExist(entityType)) { SqlInfo sqlInfo = SqlInfoBuilder.buildCreateTableSqlInfo(entityType); execNonQuery(sqlInfo); } } public boolean tableIsExist(Class<?> entityType) { Table table = Table.get(entityType); if (table.isCheckDatabase()) { return true; } Cursor cursor = null; try { cursor = execQuery("SELECT COUNT(*) AS c FROM sqlite_master WHERE type ='table' AND name ='" + table.getTableName() + "'"); if (cursor != null && cursor.moveToNext()) { int count = cursor.getInt(0); if (count > 0) { table.setCheckDatabase(true); return true; } } } finally { IOUtils.closeQuietly(cursor); } return false; } public void dropDb() { Cursor cursor = null; try { cursor = execQuery("SELECT name FROM sqlite_master WHERE type ='table'"); if (cursor != null) { while (cursor.moveToNext()) { try { execNonQuery("DROP TABLE " + cursor.getString(0)); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } } } finally { IOUtils.closeQuietly(cursor); } } public void dropTable(Class<?> entityType) { if (!tableIsExist(entityType)) return; Table table = Table.get(entityType); execNonQuery("DROP TABLE " + table.getTableName()); } // /////////////////////////////////// exec sql ///////////////////////////////////////////////////// private void debugSql(String sql) { if (config != null && debug) { Ioc.getIoc().getLogger().d(sql); } } private void beginTransaction() { if (allowTransaction) { database.beginTransaction(); } } private void setTransactionSuccessful() { if (allowTransaction) { database.setTransactionSuccessful(); } } private void endTransaction() { if (allowTransaction) { database.endTransaction(); } } public void execNonQuery(SqlInfo sqlInfo) { debugSql(sqlInfo.getSql()); try { if (sqlInfo.getBindArgs() != null) { database.execSQL(sqlInfo.getSql(), sqlInfo.getBindArgsAsArray()); } else { database.execSQL(sqlInfo.getSql()); } } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } public void execNonQuery(String sql) { debugSql(sql); try { database.execSQL(sql); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } public void deleteAll(Class clazz) { try { Table table = Table.get(clazz); database.delete(table.getTableName(), null, null); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } } public Cursor execQuery(SqlInfo sqlInfo) { debugSql(sqlInfo.getSql()); try { return database.rawQuery(sqlInfo.getSql(), sqlInfo.getBindArgsAsStrArray()); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } return null; } public Cursor execQuery(String sql) { debugSql(sql); try { return database.rawQuery(sql, null); } catch (Exception e) { Ioc.getIoc().getLogger().e(e); } return null; } // ///////////////////// temp cache //////////////////////////////////////////////////////////////// private final FindTempCache findTempCache = new FindTempCache(); private class FindTempCache { private FindTempCache() { } /** * key: sql; value: find result */ private final ConcurrentHashMap<String, Object> cache = new ConcurrentHashMap<String, Object>(); private long seq = 0; public void put(String sql, Object result) { cache.put(sql, result); } public Object get(String sql) { return cache.get(sql); } public void setSeq(long seq) { if (this.seq != seq) { cache.clear(); this.seq = seq; } } } }