/**
* Copyright (c) 2012-2013, Michael Yang 杨福海 (www.yangfuhai.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 net.tsz.afinal;
import java.io.File;
import java.io.IOException;
import java.lang.String;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import net.tsz.afinal.db.sqlite.CursorUtils;
import net.tsz.afinal.db.sqlite.DbModel;
import net.tsz.afinal.db.sqlite.ManyToOneLazyLoader;
import net.tsz.afinal.db.sqlite.OneToManyLazyLoader;
import net.tsz.afinal.db.sqlite.SqlBuilder;
import net.tsz.afinal.db.sqlite.SqlInfo;
import net.tsz.afinal.db.table.KeyValue;
import net.tsz.afinal.db.table.ManyToOne;
import net.tsz.afinal.db.table.OneToMany;
import net.tsz.afinal.db.table.TableInfo;
import net.tsz.afinal.exception.DbException;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class FinalDb {
private static final String TAG = "FinalDb";
private static HashMap<String, FinalDb> daoMap = new HashMap<String, FinalDb>();
private SQLiteDatabase db;
private DaoConfig config;
private FinalDb(DaoConfig config) {
if (config == null)
throw new DbException("daoConfig is null");
if (config.getContext() == null)
throw new DbException("android context is null");
if (config.getTargetDirectory() != null
&& config.getTargetDirectory().trim().length() > 0) {
this.db = createDbFileOnSDCard(config.getTargetDirectory(),
config.getDbName());
} else {
this.db = new SqliteDbHelper(config.getContext()
.getApplicationContext(), config.getDbName(),
config.getDbVersion(), config.getDbUpdateListener())
.getWritableDatabase();
}
this.config = config;
}
private synchronized static FinalDb getInstance(DaoConfig daoConfig) {
FinalDb dao = daoMap.get(daoConfig.getDbName());
if (dao == null) {
dao = new FinalDb(daoConfig);
daoMap.put(daoConfig.getDbName(), dao);
}
return dao;
}
/**
* 创建FinalDb
*
* @param context
*/
public static FinalDb create(Context context) {
DaoConfig config = new DaoConfig();
config.setContext(context);
return create(config);
}
/**
* 创建FinalDb
*
* @param context
* @param isDebug
* 是否是debug模式(debug模式进行数据库操作的时候将会打印sql语句)
*/
public static FinalDb create(Context context, boolean isDebug) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setDebug(isDebug);
return create(config);
}
/**
* 创建FinalDb
*
* @param context
* @param dbName
* 数据库名称
*/
public static FinalDb create(Context context, String dbName) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setDbName(dbName);
return create(config);
}
/**
* 创建 FinalDb
*
* @param context
* @param dbName
* 数据库名称
* @param isDebug
* 是否为debug模式(debug模式进行数据库操作的时候将会打印sql语句)
*/
public static FinalDb create(Context context, String dbName, boolean isDebug) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setDbName(dbName);
config.setDebug(isDebug);
return create(config);
}
/**
* 创建FinalDb
*
* @param context
* @param dbName
* 数据库名称
*/
public static FinalDb create(Context context, String targetDirectory,
String dbName) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setDbName(dbName);
config.setTargetDirectory(targetDirectory);
return create(config);
}
/**
* 创建 FinalDb
*
* @param context
* @param dbName
* 数据库名称
* @param isDebug
* 是否为debug模式(debug模式进行数据库操作的时候将会打印sql语句)
*/
public static FinalDb create(Context context, String targetDirectory,
String dbName, boolean isDebug) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setTargetDirectory(targetDirectory);
config.setDbName(dbName);
config.setDebug(isDebug);
return create(config);
}
/**
* 创建 FinalDb
*
* @param context
* 上下文
* @param dbName
* 数据库名字
* @param isDebug
* 是否是调试模式:调试模式会log出sql信息
* @param dbVersion
* 数据库版本信息
* @param dbUpdateListener
* 数据库升级监听器:如果监听器为null,升级的时候将会清空所所有的数据
* @return
*/
public static FinalDb create(Context context, String dbName,
boolean isDebug, int dbVersion, DbUpdateListener dbUpdateListener) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setDbName(dbName);
config.setDebug(isDebug);
config.setDbVersion(dbVersion);
config.setDbUpdateListener(dbUpdateListener);
return create(config);
}
/**
*
* @param context
* 上下文
* @param targetDirectory
* db文件路径,可以配置为sdcard的路径
* @param dbName
* 数据库名字
* @param isDebug
* 是否是调试模式:调试模式会log出sql信息
* @param dbVersion
* 数据库版本信息
* @param dbUpdateListener数据库升级监听器
* :如果监听器为null,升级的时候将会清空所所有的数据
* @return
*/
public static FinalDb create(Context context, String targetDirectory,
String dbName, boolean isDebug, int dbVersion,
DbUpdateListener dbUpdateListener) {
DaoConfig config = new DaoConfig();
config.setContext(context);
config.setTargetDirectory(targetDirectory);
config.setDbName(dbName);
config.setDebug(isDebug);
config.setDbVersion(dbVersion);
config.setDbUpdateListener(dbUpdateListener);
return create(config);
}
/**
* 创建FinalDb
*
* @param daoConfig
* @return
*/
public static FinalDb create(DaoConfig daoConfig) {
return getInstance(daoConfig);
}
/**
* 保存数据库,速度要比save快
*
* @param entity
*/
public void save(Object entity) {
checkTableExist(entity.getClass());
exeSqlInfo(SqlBuilder.buildInsertSql(entity));
}
/**
* 保存数据到数据库<br />
* <b>注意:</b><br />
* 保存成功后,entity的主键将被赋值(或更新)为数据库的主键, 只针对自增长的id有效
*
* @param entity
* 要保存的数据
* @return ture: 保存成功 false:保存失败
*/
public boolean saveBindId(Object entity) {
checkTableExist(entity.getClass());
List<KeyValue> entityKvList = SqlBuilder
.getSaveKeyValueListByEntity(entity);
if (entityKvList != null && entityKvList.size() > 0) {
TableInfo tf = TableInfo.get(entity.getClass());
ContentValues cv = new ContentValues();
insertContentValues(entityKvList, cv);
Long id = db.insert(tf.getTableName(), null, cv);
if (id == -1)
return false;
tf.getId().setValue(entity, id);
return true;
}
return false;
}
/**
* 把List<KeyValue>数据存储到ContentValues
*
* @param list
* @param cv
*/
private void insertContentValues(List<KeyValue> list, ContentValues cv) {
if (list != null && cv != null) {
for (KeyValue kv : list) {
cv.put(kv.getKey(), kv.getValue().toString());
}
} else {
Log.w(TAG,
"insertContentValues: List<KeyValue> is empty or ContentValues is empty!");
}
}
/**
* 更新数据 (主键ID必须不能为空)
*
* @param entity
*/
public void update(Object entity) {
checkTableExist(entity.getClass());
exeSqlInfo(SqlBuilder.getUpdateSqlAsSqlInfo(entity));
}
/**
* 根据条件更新数据
*
* @param entity
* @param strWhere
* 条件为空的时候,将会更新所有的数据
*/
public void update(Object entity, String strWhere) {
checkTableExist(entity.getClass());
exeSqlInfo(SqlBuilder.getUpdateSqlAsSqlInfo(entity, strWhere));
}
/**
* 删除数据
*
* @param entity
* entity的主键不能为空
*/
public void delete(Object entity) {
checkTableExist(entity.getClass());
exeSqlInfo(SqlBuilder.buildDeleteSql(entity));
}
/**
* 根据主键删除数据
*
* @param clazz
* 要删除的实体类
* @param id
* 主键值
*/
public void deleteById(Class<?> clazz, Object id) {
checkTableExist(clazz);
exeSqlInfo(SqlBuilder.buildDeleteSql(clazz, id));
}
/**
* 根据条件删除数据
*
* @param clazz
* @param strWhere
* 条件为空的时候 将会删除所有的数据
*/
public void deleteByWhere(Class<?> clazz, String strWhere) {
checkTableExist(clazz);
String sql = SqlBuilder.buildDeleteSql(clazz, strWhere);
debugSql(sql);
db.execSQL(sql);
}
/**
* 删除表的所有数据
*
* @param clazz
*/
public void deleteAll(Class<?> clazz) {
checkTableExist(clazz);
String sql = SqlBuilder.buildDeleteSql(clazz, null);
debugSql(sql);
db.execSQL(sql);
}
/**
* 删除指定的表
*
* @param clazz
*/
public void dropTable(Class<?> clazz) {
checkTableExist(clazz);
TableInfo table = TableInfo.get(clazz);
String sql = "DROP TABLE " + table.getTableName();
debugSql(sql);
db.execSQL(sql);
}
/**
* 删除所有数据表
*/
public void dropDb() {
Cursor cursor = db.rawQuery(
"SELECT name FROM sqlite_master WHERE type ='table' AND name != 'sqlite_sequence'", null);
if (cursor != null) {
while (cursor.moveToNext()) {
db.execSQL("DROP TABLE " + cursor.getString(0));
}
}
if (cursor != null) {
cursor.close();
cursor = null;
}
}
private void exeSqlInfo(SqlInfo sqlInfo) {
if (sqlInfo != null) {
debugSql(sqlInfo.getSql());
db.execSQL(sqlInfo.getSql(), sqlInfo.getBindArgsAsArray());
} else {
Log.e(TAG, "sava error:sqlInfo is null");
}
}
/**
* 根据主键查找数据(默认不查询多对一或者一对多的关联数据)
*
* @param id
* @param clazz
*/
public <T> T findById(Object id, Class<T> clazz) {
checkTableExist(clazz);
SqlInfo sqlInfo = SqlBuilder.getSelectSqlAsSqlInfo(clazz, id);
if (sqlInfo != null) {
debugSql(sqlInfo.getSql());
Cursor cursor = db.rawQuery(sqlInfo.getSql(),
sqlInfo.getBindArgsAsStringArray());
try {
if (cursor.moveToNext()) {
return CursorUtils.getEntity(cursor, clazz, this);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
}
}
return null;
}
/**
* 根据主键查找,同时查找“多对一”的数据(如果有多个“多对一”属性,则查找所有的“多对一”属性)
*
* @param id
* @param clazz
*/
public <T> T findWithManyToOneById(Object id, Class<T> clazz) {
checkTableExist(clazz);
String sql = SqlBuilder.getSelectSQL(clazz, id);
debugSql(sql);
DbModel dbModel = findDbModelBySQL(sql);
if (dbModel != null) {
T entity = CursorUtils.dbModel2Entity(dbModel, clazz);
return loadManyToOne(dbModel, entity, clazz);
}
return null;
}
/**
* 根据条件查找,同时查找“多对一”的数据(只查找findClass中的类的数据)
*
* @param id
* @param clazz
* @param findClass
* 要查找的类
*/
public <T> T findWithManyToOneById(Object id, Class<T> clazz,
Class<?>... findClass) {
checkTableExist(clazz);
String sql = SqlBuilder.getSelectSQL(clazz, id);
debugSql(sql);
DbModel dbModel = findDbModelBySQL(sql);
if (dbModel != null) {
T entity = CursorUtils.dbModel2Entity(dbModel, clazz);
return loadManyToOne(dbModel, entity, clazz, findClass);
}
return null;
}
/**
* 将entity中的“多对一”的数据填充满 如果是懒加载填充,则dbModel参数可为null
*
* @param clazz
* @param entity
* @param <T>
* @return
*/
public <T> T loadManyToOne(DbModel dbModel, T entity, Class<T> clazz,
Class<?>... findClass) {
if (entity != null) {
try {
Collection<ManyToOne> manys = TableInfo.get(clazz).manyToOneMap
.values();
for (ManyToOne many : manys) {
Object id = null;
if (dbModel != null) {
id = dbModel.get(many.getColumn());
} else if (many.getValue(entity).getClass() == ManyToOneLazyLoader.class
&& many.getValue(entity) != null) {
id = ((ManyToOneLazyLoader) many.getValue(entity))
.getFieldValue();
}
if (id != null) {
boolean isFind = false;
if (findClass == null || findClass.length == 0) {
isFind = true;
}
for (Class<?> mClass : findClass) {
if (many.getManyClass() == mClass) {
isFind = true;
break;
}
}
if (isFind) {
@SuppressWarnings("unchecked")
T manyEntity = (T) findById(
Integer.valueOf(id.toString()),
many.getManyClass());
if (manyEntity != null) {
if (many.getValue(entity).getClass() == ManyToOneLazyLoader.class) {
if (many.getValue(entity) == null) {
many.setValue(
entity,
new ManyToOneLazyLoader(entity,
clazz,
many.getManyClass(),
this));
}
((ManyToOneLazyLoader) many
.getValue(entity)).set(manyEntity);
} else {
many.setValue(entity, manyEntity);
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return entity;
}
/**
* 根据主键查找,同时查找“一对多”的数据(如果有多个“一对多”属性,则查找所有的一对多”属性)
*
* @param id
* @param clazz
*/
public <T> T findWithOneToManyById(Object id, Class<T> clazz) {
checkTableExist(clazz);
String sql = SqlBuilder.getSelectSQL(clazz, id);
debugSql(sql);
DbModel dbModel = findDbModelBySQL(sql);
if (dbModel != null) {
T entity = CursorUtils.dbModel2Entity(dbModel, clazz);
return loadOneToMany(entity, clazz);
}
return null;
}
/**
* 根据主键查找,同时查找“一对多”的数据(只查找findClass中的“一对多”)
*
* @param id
* @param clazz
* @param findClass
*/
public <T> T findWithOneToManyById(Object id, Class<T> clazz,
Class<?>... findClass) {
checkTableExist(clazz);
String sql = SqlBuilder.getSelectSQL(clazz, id);
debugSql(sql);
DbModel dbModel = findDbModelBySQL(sql);
if (dbModel != null) {
T entity = CursorUtils.dbModel2Entity(dbModel, clazz);
return loadOneToMany(entity, clazz, findClass);
}
return null;
}
/**
* 将entity中的“一对多”的数据填充满
*
* @param entity
* @param clazz
* @param <T>
* @return
*/
public <T> T loadOneToMany(T entity, Class<T> clazz, Class<?>... findClass) {
if (entity != null) {
try {
Collection<OneToMany> ones = TableInfo.get(clazz).oneToManyMap
.values();
Object id = TableInfo.get(clazz).getId().getValue(entity);
for (OneToMany one : ones) {
boolean isFind = false;
if (findClass == null || findClass.length == 0) {
isFind = true;
}
for (Class<?> mClass : findClass) {
if (one.getOneClass() == mClass) {
isFind = true;
break;
}
}
if (isFind) {
List<?> list = findAllByWhere(one.getOneClass(),
one.getColumn() + "=" + id);
if (list != null) {
/* 如果是OneToManyLazyLoader泛型,则执行灌入懒加载数据 */
if (one.getDataType() == OneToManyLazyLoader.class) {
OneToManyLazyLoader oneToManyLazyLoader = one
.getValue(entity);
oneToManyLazyLoader.setList(list);
} else {
one.setValue(entity, list);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
return entity;
}
/**
* 查找所有的数据
*
* @param clazz
*/
public <T> List<T> findAll(Class<T> clazz) {
checkTableExist(clazz);
return findAllBySql(clazz, SqlBuilder.getSelectSQL(clazz));
}
/**
* 查找所有数据
*
* @param clazz
* @param orderBy
* 排序的字段
*/
public <T> List<T> findAll(Class<T> clazz, String orderBy) {
checkTableExist(clazz);
return findAllBySql(clazz, SqlBuilder.getSelectSQL(clazz)
+ " ORDER BY " + orderBy);
}
/**
* 根据条件查找所有数据
*
* @param clazz
* @param strWhere
* 条件为空的时候查找所有数据
*/
public <T> List<T> findAllByWhere(Class<T> clazz, String strWhere) {
checkTableExist(clazz);
return findAllBySql(clazz,
SqlBuilder.getSelectSQLByWhere(clazz, strWhere));
}
/**
* 根据条件查找所有数据
*
* @param clazz
* @param strWhere
* 条件为空的时候查找所有数据
* @param orderBy
* 排序字段
*/
public <T> List<T> findAllByWhere(Class<T> clazz, String strWhere,
String orderBy) {
checkTableExist(clazz);
return findAllBySql(clazz,
SqlBuilder.getSelectSQLByWhere(clazz, strWhere) + " ORDER BY "
+ orderBy);
}
/**
* 根据条件查找所有数据
*
* @param clazz
* @param strSQL
*/
private <T> List<T> findAllBySql(Class<T> clazz, String strSQL) {
checkTableExist(clazz);
debugSql(strSQL);
Cursor cursor = db.rawQuery(strSQL, null);
try {
List<T> list = new ArrayList<T>();
while (cursor.moveToNext()) {
T t = CursorUtils.getEntity(cursor, clazz, this);
list.add(t);
}
return list;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
cursor = null;
}
return null;
}
/**
* 根据sql语句查找数据,这个一般用于数据统计
*
* @param strSQL
*/
public DbModel findDbModelBySQL(String strSQL) {
debugSql(strSQL);
Cursor cursor = db.rawQuery(strSQL, null);
try {
if (cursor.moveToNext()) {
return CursorUtils.getDbModel(cursor);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
}
return null;
}
public List<DbModel> findDbModelListBySQL(String strSQL) {
debugSql(strSQL);
Cursor cursor = db.rawQuery(strSQL, null);
List<DbModel> dbModelList = new ArrayList<DbModel>();
try {
while (cursor.moveToNext()) {
dbModelList.add(CursorUtils.getDbModel(cursor));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
cursor.close();
}
return dbModelList;
}
private void checkTableExist(Class<?> clazz) {
if (!tableIsExist(TableInfo.get(clazz))) {
String sql = SqlBuilder.getCreatTableSQL(clazz);
debugSql(sql);
db.execSQL(sql);
}
}
private boolean tableIsExist(TableInfo table) {
if (table.isCheckDatabese())
return true;
Cursor cursor = null;
try {
String sql = "SELECT COUNT(*) AS c FROM sqlite_master WHERE type ='table' AND name ='"
+ table.getTableName() + "' ";
debugSql(sql);
cursor = db.rawQuery(sql, null);
if (cursor != null && cursor.moveToNext()) {
int count = cursor.getInt(0);
if (count > 0) {
table.setCheckDatabese(true);
return true;
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
cursor = null;
}
return false;
}
private void debugSql(String sql) {
if (config != null && config.isDebug())
android.util.Log.d("Debug SQL", ">>>>>> " + sql);
}
public static class DaoConfig {
private Context mContext = null; // android上下文
private String mDbName = "afinal.db"; // 数据库名字
private int dbVersion = 1; // 数据库版本
private boolean debug = true; // 是否是调试模式(调试模式 增删改查的时候显示SQL语句)
private DbUpdateListener dbUpdateListener;
// private boolean saveOnSDCard = false;//是否保存到SD卡
private String targetDirectory;// 数据库文件在sd卡中的目录
public Context getContext() {
return mContext;
}
public void setContext(Context context) {
this.mContext = context;
}
public String getDbName() {
return mDbName;
}
public void setDbName(String dbName) {
this.mDbName = dbName;
}
public int getDbVersion() {
return dbVersion;
}
public void setDbVersion(int dbVersion) {
this.dbVersion = dbVersion;
}
public boolean isDebug() {
return debug;
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public DbUpdateListener getDbUpdateListener() {
return dbUpdateListener;
}
public void setDbUpdateListener(DbUpdateListener dbUpdateListener) {
this.dbUpdateListener = dbUpdateListener;
}
// public boolean isSaveOnSDCard() {
// return saveOnSDCard;
// }
//
// public void setSaveOnSDCard(boolean saveOnSDCard) {
// this.saveOnSDCard = saveOnSDCard;
// }
public String getTargetDirectory() {
return targetDirectory;
}
public void setTargetDirectory(String targetDirectory) {
this.targetDirectory = targetDirectory;
}
}
/**
* 在SD卡的指定目录上创建文件
*
* @param sdcardPath
* @param dbfilename
* @return
*/
private SQLiteDatabase createDbFileOnSDCard(String sdcardPath,
String dbfilename) {
File dbf = new File(sdcardPath, dbfilename);
if (!dbf.exists()) {
try {
if (dbf.createNewFile()) {
return SQLiteDatabase.openOrCreateDatabase(dbf, null);
}
} catch (IOException ioex) {
throw new DbException("数据库文件创建失败", ioex);
}
} else {
return SQLiteDatabase.openOrCreateDatabase(dbf, null);
}
return null;
}
class SqliteDbHelper extends SQLiteOpenHelper {
private DbUpdateListener mDbUpdateListener;
public SqliteDbHelper(Context context, String name, int version,
DbUpdateListener dbUpdateListener) {
super(context, name, null, version);
this.mDbUpdateListener = dbUpdateListener;
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (mDbUpdateListener != null) {
mDbUpdateListener.onUpgrade(db, oldVersion, newVersion);
} else { // 清空所有的数据信息
dropDb();
}
}
}
public interface DbUpdateListener {
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
}
}