Android SQLite的ORM接口实现(一)---findAll和find的实现
最近在看Android的ORM数据库框架LitePal,就想到可以利用原生的SQLite来实现和LitePal类似的ORM接口实现。
LitePal有一个接口是这样的:
List<Status> statuses = DataSupport.findAll(Status.class);
指定什么类型,就能获取到该类型的数据集合。
这样是很方便,于是想着自己不看它们的实现,自己搞一个出来。
首先想到的就是利用反射和泛型。
我想到了利用反射来调用set方法完成赋值。
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
Log.e("DatabaseStore", field.getName());
}
Java的Class API有getFields和getDeclaredFields两个方法,前者是用来获取public字段的,后者是用来获取所有声明的字段的,显然必须使用后者,而且注意的是,因为获取到的字段是所有声明的字段,所以绝对有可能获取到不需要的字段。
cursor.getString(cursor.getColumnIndex("name"));
幸运的是,是可以获取到的:
for (Field field : fields) {
Type type = field.getGenericType();
Log.e("DatabaseStore", type.toString());
}
但如何知道哪些属性是要被赋值的呢?
List<Method> setMethods = new ArrayList<Method>();
for (Method method : allMethods) {
String name = method.getName();
if (name.contains("set") && !name.equals("offset")) {
setMethods.add(method);
continue;
}
}
这就要求我们所有的属性的setter前面都必须带有set关键字,这同样也是种代码约束。
Cursor cursor = Connector.getDatabase().query(clazz.getSimpleName(), null, null, null, null, null, null);//查询并获得游标
List<T> list = new ArrayList<T>();
Constructor<?> constructor = findBestSuitConstructor(clazz);
while (cursor.moveToNext()) {
T data = null;
try {
data = (T) constructor
.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
for (Method method : setMethods) {
String name = method.getName();
String valueName = name.substring(3).substring(0, 1).toLowerCase() + name.substring(4);
String type = null;
int index = 0;
if (fieldNames.contains(valueName)) {
index = fieldNames.indexOf(valueName);
type = fields[index].getGenericType().toString();
}
Object value = new Object();
if (type != null) {
if (type.contains("String")) {
value = cursor.getString(cursor.getColumnIndex(valueName.toLowerCase()));
} else if (type.equals("int")) {
value = cursor.getInt(cursor.getColumnIndex(valueName.toLowerCase()));
} else if (type.equals("double")) {
value = cursor.getDouble(cursor.getColumnIndex(valueName.toLowerCase()));
} else if (type.equals("float")) {
value = cursor.getFloat(cursor.getColumnIndex(valueName.toLowerCase()));
} else if (type.equals("boolean")) {
value = cursor.getInt(cursor.getColumnIndex(valueName.toLowerCase())) == 1 ? true : false;
} else if (type.equals("long")) {
value = cursor.getLong(cursor.getColumnIndex(valueName.toLowerCase()));
} else if (type.equals("short")) {
value = cursor.getShort(cursor.getColumnIndex(valueName.toLowerCase()));
}
try {
fields[index].setAccessible(true);
fields[index].set(data, value);
} catch (IllegalAccessException e) {
Log.e("data", e.toString());
}
}
}
list.add(data);
}
cursor.close();
为了保证通用性,使用了泛型,但这里有个小小的问题需要解决,就是如何new一个T?
protected Constructor<?> findBestSuitConstructor(Class<?> modelClass) {
Constructor<?> finalConstructor = null;
Constructor<?>[] constructors = modelClass.getConstructors();
for (Constructor<?> constructor : constructors) {
if (finalConstructor == null) {
finalConstructor = constructor;
} else {
int finalParamLength = finalConstructor.getParameterTypes().length;
int newParamLength = constructor.getParameterTypes().length;
if (newParamLength < finalParamLength) {
finalConstructor = constructor;
}
}
}
finalConstructor.setAccessible(true);
return finalConstructor;
}
谁的参数最少,谁就是最佳构造器,0当然是最少的。
到了这里,我们基本上就实现了一个拥有和LitePal的API一样但内在实现却是原生方法的数据库接口方法了:
List<Status> newData = DatabaseStore.getInstance().findAll(Status.class);
LitePal当然会提供条件查询的接口,也就是所谓的模糊查询。
SELECT 字段 FROM 表 WHERE 某字段 Like 条件
其中,条件有四种匹配模式。
SELECT * FROM [user] WHERE u_name LIKE '%三%'
会把u_name中有“三”的记录找出来。
SELECT * FROM [user] WHERE u_name LIKE '%三%' AND u_name LIKE '%猫%'
这样能够找出u_name中的“三脚猫”的记录,但无法找到“张猫三”的记录。
SELECT * FROM [user] WHERE u_name LIKE '_三_'
这样只能找出“张三猫”这样中间是“三”的记录。
SELECT * FROM [user] WHERE u_name LIKE '三__';
这样是找到“三脚猫”这样“三”放在开头的三个单词的记录。
SELECT * FROM [user] WHERE u_name LIKE '[张李王]三'
这样是找到“张三”,“李三”或者“王三”的记录。
SELECT * FROM [user] WHERE u_name LIKE '老[1-9]'
这将找出”老1“,”老2“。。。等记录。
SELECT * FROM [user] WHERE u_name LIKE '[^张李王]三'
这样找到的记录就是排除”张三“,”李三“或者”王三“的其他记录。
List<Status> myStatus = DataSupport.where("text=?", "我好").find(Status.class);
这样的接口比较简单,并且允许链式调用,形式上更加简洁。
private String conditionStr;
public DatabaseStore where(String key, String value) {
conditionStr = " where " + key + " like '%" + value + "%'";
return store;
}
为了实现链式调用,返回DatabaseStore是必须的。
public <T> List<T> find(Class<T> clazz) {
String sql = "SELECT * FROM " + clazz.getSimpleName().toLowerCase() + conditionStr;
Cursor cursor = Connector.getDatabase().rawQuery(sql, null);
Field[] fields = clazz.getDeclaredFields();
List<String> fieldNames = new ArrayList<String>();
for (Field field : fields) {
fieldNames.add(field.getName());
}
List<Method> setMethods = getSetMethods(clazz);
List<T> list = getList(clazz, cursor, setMethods, fieldNames, fields);
cursor.close();
conditionStr = "";
return list;
}
getSetMethods方法就是上面获取setter的代码的封装,而getList方法就是上面生成指定类型对象的List的代码的封装。
List<Status> data = DatabaseStore.getInstance().where("text", "我好").find(Status.class);
无论是LitePal还是我们自己的实现,where都必须放在find前面。
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<!-- 数据库名称 -->
<dbname value="xxx.db"></dbname>
<!-- 数据库版本 -->
<version value="1"></version>
<!-- 数据库表 -->
<list>
<mapping class="com.example.pc.model.Status"></mapping>
</list>
</litepal>
但表名具体到底是啥呢?
Cursor cursor = Connector.getDatabase().rawQuery("select name from sqlite_master where type='table' order by name", null);
while (cursor.moveToNext()) {
//遍历出表名
String name = cursor.getString(0);
Log.e("DatabaseStore", name);
}
每一个SQLite的数据库中都有一个sqlite_master的表,这个表的结构如下:
CREATE TABLE sqlite_master (
type TEXT,
name TEXT,
tbl_name TEXT,
rootpage INTEGER,
sql TEXT
);
SELECT name FROM
(SELECT * FROM sqlite_master UNION ALL
SELECT * FROM sqlite_temp_master)
WHERE type=’table’
ORDER BY name
LitePal还可以对结果进行排序:
List<Status> myStatus = DataSupport.where("text=?", "我好").order("updatetime").find(Status.class);
这个也是很简单就能实现的,类似where方法一样的处理:
public DatabaseStore order(String key) {
conditionStr += " order by " + key;
return store;
}
默认是升序。
API被人乱用的概率相当大,这时就需要有一些错误提示帮助用户定位问题了,最简单的例子就是在没有任何条件的情况下调用find方法,这时就应该提示没有任何条件:
if (conditionStr.equals("")) {
throw new Throwable("There are not any conditions before find method invoked");
}
还有一种情况并不算是被乱用,但按照上面的实现是会出错的:
statuses = DatabaseStore.getInstance().order("updatetime").where("text", "我好").find(Status.class);
绝对会报错,因为最后的SQL语句是这样的:select * from status order by updatetime where text like '%我好%'。
private String whereStr = "";
private String orderStr = "";
public DatabaseStore where(String key, String value) {
whereStr += " where " + key + " like '%" + value + "%'";
return store;
}
public DatabaseStore order(String key) {
orderStr += " order by " + key;
return store;
}
接着就是在find方法中进行判断:
if (whereStr.equals("") && orderStr.equals("")) {
throw new Throwable("There are not any conditions before find method invoked");
}
String sql = "select * from " + clazz.getSimpleName().toLowerCase() + (whereStr.equals("") ? "" : whereStr) + (orderStr.equals("") ? "" : orderStr);
暂时就简单实现了类似LitePal的ORM接口调用形式。
Android SQLite的ORM接口实现(一)---findAll和find的实现的更多相关文章
- C# Android 开发中使用 Sqlite.NET ORM
开发环境:VS2015 Xamarin Sqlite.NET ORM 不就相当于 Entiry Framework For Xamarin 吗? 相当于用 C# 开发安卓程序访问 Sqlite 可以使 ...
- Android SQLite 数据库详细介绍
Android SQLite 数据库详细介绍 我们在编写数据库应用软件时,需要考虑这样的问题:因为我们开发的软件可能会安装在很多用户的手机上,如果应用使用到了SQLite数据库,我们必须在用户初次使用 ...
- Android SQLite与ListView的简单使用
2017-04-25 初写博客有很多地方都有不足,希望各位大神给点建议. 回归主题,这次简单的给大家介绍一下Android SQLite与ListView的简单使用sqlite在上节中有介绍,所以在这 ...
- Storm——Android SQLite数据库管理类库
Storm是一个Android SQLite数据库管理类库,可以通过注解创建表和迁移数据库.它不是ORM框架. 特性: 1.通过@Annotations创建表: 2.通过@Annotations迁 ...
- 再读Android sqlite
再读Android sqlite Android原生支持sqlite数据库操作,sqlite时轻量级关系型数据库,支持标准sql语句.Android对sqlite进行良好的接口封装来避免sql注入等安 ...
- Android SQLite 通配符查询找不到参数问题
使用Android SQLite中SQLiteDatabase类的query方法查询时,如果where中包含通配符,则参数会无法设置,如类似下面的方法查询时 SQLiteDatabase db = d ...
- Android+Sqlite 实现古诗阅读应用(三)
往期传送门: Android+Sqlite 实现古诗阅读应用(一) Android+Sqlite 实现古诗阅读应用(二) 加入截图分享的功能. 很多应用都有分享的功能,我也想在我的古诗App里加入这个 ...
- Android+Sqlite 实现古诗阅读应用(二)
传送门:Android+Sqlite 实现古诗阅读应用(一) Hi,又回来了,最近接到很多热情洋溢的小伙伴们的来信,吼开心哈,我会继续努力的=-=! 上回的东西我们做到了有个textview能随机选择 ...
- Android Sqlite 数据库版本更新
Android Sqlite 数据库版本更新 http://87426628.blog.163.com/blog/static/6069361820131069485844/ 1.自己写一个类继承 ...
随机推荐
- Android平台免Root无侵入AOP框架Dexposed使用详解
Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...
- ELK——为调试 Logstash Grok 表达式,安装 GrokDebuger 环境
内容 安装 RVM 安装 Ruby 和 Gems 安装 Rails 安装 jls-grok Ruby grok 解析 调试 grok 注意:不要用 root 执行以下操作. 用 logstash ...
- liunx CentOS 升级Python版本
CentOS python版本是V2.6.6,升级3.4.3. 1.下载 安装包:wget http://www.python.org/ftp/python/3.4.3/Python-3.4.3.tg ...
- LESS详解之函数(四)
之前已经为大家介绍了一些LESS函数,大家应该对之前介绍的有所了解了.下面依旧为大家介绍LESS的函数,附加着一些小例子.希望这些有关LESS的函数能在大家编写LESS的时候有所帮助. saturat ...
- Android软件安全开发实践(下)
Android开发是当前最火的话题之一,但很少有人讨论这个领域的安全问题.本系列将分两期,探讨Android开发中常见的安全隐患和解决方案.第一期将从数据存储.网络通信.密码和认证策略这三个角度,带你 ...
- Strategy策略设计模式
1.策略模式和状态模式的区别和联系(本部分转载自:http://letscoding.cn/java%E4%B8%AD%EF%BC%8C%E7%8A%B6%E6%80%81%E6%A8%A1%E5%B ...
- C#集合--ICollection接口和IList接口
虽然列举接口提供了一个协议,用于向前的方式遍历集合,但它们没有提供一种机制来确定集合的大小,通过索引访问集合的成员,搜索集合,或修改集合.为了实现这些功能,.NET Framework定义了IColl ...
- mysql 显示行号,以及分组排序
建表: CREATE TABLE `my_tb` ( `id` ) NOT NULL AUTO_INCREMENT, `parent_code` ) DEFAULT NULL, `code` ) DE ...
- 提高Java代码质量:使用枚举定义常量(转)
一.分析 常量的声明是每一个项目中不可或缺的,在Java1.5之前,我们只有两种方式的声明:类常量和接口常量.不过,在1.5版之后有了改进,即新增了一种常量声明方式,枚举常量.代码如下: enum ...
- 安卓开发笔记——重识Activity
Activity并不是什么新鲜的东西,老生常谈,这里只是随笔记录一些笔记. 每当说起Activity,感觉最关注的还是它的生命周期,因为要使我们的应用程序更加健壮,客户体验更加良好,如果对生命周期不熟 ...