昨天相当无聊,就动手写了一个Orm。其实个人觉得在Android上写Orm意义不大,Android自带的ContentValues对象和SqliteDatabase.Insert(...)等方法已经够给不喜欢写sql语句的同学使用了。不过无聊就写了,权当练练手吧~~~
【原创文章,转载请声明】转自http://blog.csdn.net/allen_704/article/details/21225371
以下程序实现了根据实体对象自动建表,以及Orm的save(Object obj)方法自动插入对应的表,没有用注解,注解加上会更灵活一些,懒得搞了。
第一个功能:根据实体建表。思路是在Application中将要建表的实体注册到数据库类中,在SqliteOpenHelper的OnCreate方法中查找出所有已注册的实体类,通过反射拿到所有属性拼接建表Sql语句。上代码:
/**
* @author heg
* @version 创建时间:2014-3-13 下午4:24:58
* @description: 注册需要自动生成数据库表的实体类,会将实体所有属性自动生成表
*/
public class OrmApplication extends Application {
private String TAG = OrmApplication.class.getName();
@Override
public void onCreate() {
Log.i(TAG , "application created");
registerEntities();
super.onCreate();
}
/**
* 注册需要自动生成数据库表的实体类
*/
protected void registerEntities(){
BaseDatabase.registerEntities(User.class);
//TODO add entities here
}
}
public static class SqliteHelper extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
createTables(db);
}
}
public static void createTables(SQLiteDatabase db) {
for(Class<?> clazz : entities){
String sql = EntityUtil.entityToCreateTableSql(clazz);
if(sql!=null &&! "".equals(sql))
db.execSQL(sql);
}
}
/**
* 通过实体的基本属性创建建数据表语句<br>
*
* @param clazz
* @return
*/
public static String entityToCreateTableSql(Class<?> clazz) {
StringBuffer sb = new StringBuffer();
Field[] fields = clazz.getDeclaredFields();
if (fields.length == 0)
return null;
sb.append("CREATE TABLE IF NOT EXISTS ");
sb.append(clazz.getSimpleName().toLowerCase());
sb.append(" (");
sb.append("_id").append(" INTEGER PRIMARY KEY,");
for (Field f : fields) {
String fieldType = f.getType().getSimpleName();
String fieldName = f.getName().toLowerCase();
if ("String".equals(fieldType)) {
sb.append(fieldName).append(" TEXT,");
} else if ("Date".equals(fieldType)) {// 日期当做String来处理
sb.append(fieldName).append(" TEXT,");
} else if ("Integer".equals(fieldType) || "int".equals(fieldType)) {
sb.append(fieldName).append(" INT,");
} else if ("Long".equalsIgnoreCase(fieldType)) {
sb.append(fieldName).append(" BIGINT,");
} else if ("Double".equalsIgnoreCase(fieldType)) {
sb.append(fieldName).append(" DOUBLE,");
} else if ("Float".equalsIgnoreCase(fieldType)) {
sb.append(fieldName).append(" FLOAT,");
} else if ("Boolean".equalsIgnoreCase(fieldType)) {
sb.append(fieldName).append(" BOOLEAN,");
} else if ("Char".equalsIgnoreCase(fieldType)) {
sb.append(fieldName).append(" CHAR,");
} else if ("Byte".equalsIgnoreCase(fieldType)) {
sb.append(fieldName).append(" TINYINT,");
} else {// 后续Object等类型拓展
}
}
sb.deleteCharAt(sb.lastIndexOf(","));
sb.append(");");
Log.i(TAG, clazz.getSimpleName() + " create table sql = " + sb.toString());
return sb.toString();
}
第二个就是save方法了,也是利用反射将object所有属性值转成ContectValues对象丢给SqliteDatabase,insert方法,懒得拼接sql语句了。上代码:
package com.heg.lib.db.orm;
import com.heg.lib.db.orm.BaseDatabase.SqliteHelper;
import com.heg.lib.utils.EntityUtil;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
/**
* @author heg
* @version 创建时间:2014-3-13 下午6:07:30
* @description: TODO
*/
public class Dao {
private static final String TAG = Dao.class.getName();
private static Object mutex = new Object();
private SqliteHelper dbhelper = null;
private SQLiteDatabase db = null;
private boolean reserved = false;
public Dao(Context ctx) {
dbhelper = SqliteHelper.getInstance(ctx);
}
public boolean save(Object obj) {
synchronized (mutex) {
if (!BaseDatabase.isEntityRegisted(obj.getClass()))
return false;
try {
if (reserve()) {
long ret = db.insert(obj.getClass().getSimpleName(), null,
EntityUtil.entityToContentValues(obj));
Log.i(TAG, "save ret = " + ret);
if (ret != -1)
return true;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//此处按照我的本意是想关闭数据库的,不过报了个奇怪的错,
//unable to close due to unfinalised statements。
//网上查了都是些各种抄的没用的答案,暂时就不关闭了!
//有知道原因的同学麻烦告知一下了,谢谢~~
// release();
}
return false;
}
}
private boolean reserve() {
synchronized (mutex) {
if (!reserved) {
if ((db = dbhelper.getWritableDatabase()) != null) {
reserved = true;
return true;
}
} else {
return true;
}
}
return false;
}
private void release() {
synchronized (mutex) {
Log.i(TAG, "release========");
if (reserved) {
db.releaseReference();
reserved = false;
}
}
}
}
/**
* entity to ContentValues
*
* @param obj
* @return
*/
public static ContentValues entityToContentValues(Object obj) {
Class<?> cls = obj.getClass();
ContentValues cv = new ContentValues();
// 取出entity里的所有方法
Method[] methods = cls.getDeclaredMethods();
Field[] fields = cls.getDeclaredFields();
for (Field field : fields) {
try {
String fieldType = field.getType().getSimpleName();
String fieldGetName = parseMethodName(field.getName(), "get");
if (!haveMethod(methods, fieldGetName)) {
continue;
}
Method fieldGetMet = cls.getMethod(fieldGetName, new Class[] {});
Object fieldVal = fieldGetMet.invoke(obj, new Object[] {});
if (null == fieldVal) {
continue;
}
String value = String.valueOf(fieldVal);
if ("String".equals(fieldType)) {
cv.put(toDbColumName(field.getName()), value);
} else if ("Date".equals(fieldType)) {// 日期当做String来处理
cv.put(toDbColumName(field.getName()), value);
} else if ("Integer".equals(fieldType) || "int".equals(fieldType)) {
cv.put(toDbColumName(field.getName()), Integer.parseInt(value));
} else if ("Long".equalsIgnoreCase(fieldType)) {
cv.put(toDbColumName(field.getName()), Long.parseLong(value));
} else if ("Double".equalsIgnoreCase(fieldType)) {
cv.put(toDbColumName(field.getName()), value);
} else if ("Float".equalsIgnoreCase(fieldType)) {
cv.put(toDbColumName(field.getName()), value);
} else if ("Boolean".equalsIgnoreCase(fieldType)) {
cv.put(toDbColumName(field.getName()), Integer.parseInt(value));
} else if ("Char".equalsIgnoreCase(fieldType)) {
cv.put(toDbColumName(field.getName()), value);
} else if ("Byte".equalsIgnoreCase(fieldType)) {
cv.put(toDbColumName(field.getName()), Integer.parseInt(value));
} else {// 后续Object等类型拓展
}
} catch (Exception e) {
e.printStackTrace();
}
}
return cv;
}
差不多就这样了,工程代码路径 SimpleOrm 有需要的同学可以下来看看。
【原创文章,转载请声明】转自http://blog.csdn.net/allen_704/article/details/21225371