前言

相信不少开发人员跟我一样,每次都非常烦恼自己写数据库,并且那些数据库语句也经常记不住。当然网上也有非常多非常好的数据库框架,你能够直接拿来用,可是 非常多时候我们的项目。特别是一个小型的Andrond应用原本用到的数据库结构比較简单,不是必需去用那些有点臃肿的框架。当然,即使你用那些框架。当你遇到问题时,你是否也得去改动它?你要改动别人的框架必须的读懂他人的设计代码。所以无论从那个角度出发,你都得掌握简单的数据库操作。那么这篇博客就从简单的数据库操作来学习Android数据库相关知识点。然后一步一步去搭建自己的简单型数据库框架,以后就再也不用操心害怕去写数据库了,直接拿自己的数据库框架用就好了。

框架功能

  1. public long insert(Object obj);插入数据
  2. public List findAll(Class clazz);查询全部数据
  3. public List findByArgs(Class clazz, String select, String[] selectArgs) 。依据指定条件查询满足条件数据
  4. public T findById(Class clazz, int id);依据id查询一条记录
  5. public void deleteById(Class

创建数据库

Android系统中已经集成了Sqlite数据库,我们直接使用它就好了,同一时候Android系统提供了一个数据库帮助类SQLiteOpenHelper,该类是一个抽象类。所以得写一个类来继承它实现里面的方法。代码例如以下:

MySQLiteHelper类

public class MySQLiteHelper extends SQLiteOpenHelper {

    public MySQLiteHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
} @Override
public void onCreate(SQLiteDatabase db) {
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
} }

当数据库创建时系统会调用当中的 onCreate方法,那么我们就能够来实现 onCreate 方法来创建数据库表。假设我们要创建一张 Person表,表中有 id,name,age,flag字段。那么代码例如以下:

public class MySQLiteHelper extends SQLiteOpenHelper {

    public static final String CREATE_TABLE = "create table Person ("
+ "id integer primary key autoincrement, "
+ "name text, "
+ "age integer, "
+ "flag boolean)"; public MySQLiteHelper(Context context, String name, CursorFactory factory,
int version) {
super(context, name, factory, version);
} @Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE);
}
...
}

由此我们的数据库帮助类就完毕了,接下来是这么使用的:

    private static final String DB_NAME = "demo.db";
private static final int DB_VERSION = 1; public void oepnDB(){
MySQLiteHelper helper = new MySQLiteHelper(context, DB_NAME, null, DB_VERSION);
SQLiteDatabase db = helper.getWritableDatabase();
}

有以上代码就已经完毕了一个数据库创建以及一张表的创建。是不不是感觉不是非常难呢?这么看起来的确不是非常难,可是我的也不得不每次去继承SQLiteOpenHelper类来实现里面的方法。关键是每次都要去写创建表语句

public static final String CREATE_TABLE = "create table Person ("
+ "id integer primary key autoincrement, "
+ "name text, "
+ "age integer, "
+ "flag boolean)";

这里表的字段仅仅有4个,假设有一天你遇到表里的字段有10列怎么办?还继续依照上面的方法写创建表语句么?你就不嫌繁琐么?并且easy粗错。那么有没有超级简单的方法一步完毕表语句的创建呢?你细想:存放在数据库中表的这些字段无非就是一个Person类中的全部成员变量,这么一来能否够仅仅通过Person类型直接创建表语句呢?答案是肯定的。

我们通过java 的反射机制来一步一劳永逸的实现建表操作。

代码例如以下:

 /**
* 得到建表语句
*
* @param clazz 指定类
* @return sql语句
*/
private String getCreateTableSql(Class<?> clazz) {
StringBuilder sb = new StringBuilder();
//将类名作为表名
String tabName = Utils.getTableName(clazz);
sb.append("create table ").append(tabName).append(" (id INTEGER PRIMARY KEY AUTOINCREMENT, ");
//得到类中全部属性对象数组
Field[] fields = clazz.getDeclaredFields();
for (Field fd : fields) {
String fieldName = fd.getName();
String fieldType = fd.getType().getName();
if (fieldName.equalsIgnoreCase("_id") || fieldName.equalsIgnoreCase("id")) {
continue;
} else {
sb.append(fieldName).append(Utils.getColumnType(fieldType)).append(", ");
}
}
int len = sb.length();
sb.replace(len - 2, len, ")");
Log.d(TAG, "the result is " + sb.toString());
return sb.toString();
}

工具类代码例如以下:

package com.xjp.databasedemo;

import android.text.TextUtils;

import java.util.Locale;

/**
* Created by xjp on 2016/1/23.
*/
public class DBUtils {
//得到每一列字段的数据类型
public static String getColumnType(String type) {
String value = null;
if (type.contains("String")) {
value = " text ";
} else if (type.contains("int")) {
value = " integer ";
} else if (type.contains("boolean")) {
value = " boolean ";
} else if (type.contains("float")) {
value = " float ";
} else if (type.contains("double")) {
value = " double ";
} else if (type.contains("char")) {
value = " varchar ";
} else if (type.contains("long")) {
value = " long ";
}
return value;
} //得到表名
public static String getTableName(Class<? > clazz){
return clazz.getSimpleName();
} public static String capitalize(String string) {
if (!TextUtils.isEmpty(string)) {
return string.substring(0, 1).toUpperCase(Locale.US) + string.substring(1);
}
return string == null ? null : "";
}
}

如此一来。用户创建数据库表就变的非常easy了,传入Person类的类型(Person.class)作为參数,那么代码就帮你创建出了一张名字为Person的表。使用代码例如以下:

class MySqLiteHelper extends SQLiteOpenHelper {

        ..................

        @Override
public void onCreate(SQLiteDatabase db) {
createTable(db);
}
/**
* 依据制定类名创建表
*/
private void createTable(SQLiteDatabase db) {
db.execSQL(getCreateTableSql(Person.class)); ..............
}

是不是非常easy!

。。领导再也不用操心我不会创建数据库了。

数据库操作–插入

android提供的数据库插入操作

对数据库插入操作 SQLite提供了例如以下方法

public long insert(String table, String nullColumnHack, ContentValues values)

能够看到。第一个參数是table 表示表名,第二个參数通经常使用不到,传入null就可以,第三个參数将数据以 ContentValues键值对的形式存储。比方我们在数据库中插入一条人Person的信息代码例如以下:

public void insert(Person person){
ContentValues values = new ContentValues();
values.put("name",person.getName());
values.put("age",person.getAge());
values.put("flag",person.getFlag());
db.insert("Person",null,values);
}

当中ContentValues是以键值对的形式存储数据,上面代码中的key 分别相应数据库中的每一列的字段。vaule分别相应着该列的值。你是否发现Person类中有几个属性就得写多少行values.put(key。value);增加它有10个字段须要保存到数据库中。你是否认为这样非常麻烦呢?认为麻烦就对了,接下来我们利用反射来一步完毕以上数据库插入操作。

数据库插入框架

数据库插入操作框架能够减轻你写代码量,让你一步完毕数据库插入操作而无须关注其内部繁琐的操作。相同利用java反射来实现以上效果。

代码例如以下:

 /**
* 插入一条数据
*
* @param obj
* @return 返回-1代表插入数据库失败。否则成功
* @throws IllegalAccessException
*/
public long insert(Object obj) {
Class<? > modeClass = obj.getClass();
Field[] fields = modeClass.getDeclaredFields();
ContentValues values = new ContentValues(); for (Field fd : fields) {
fd.setAccessible(true);
String fieldName = fd.getName();
//剔除主键id值得保存,由于框架默认设置id为主键自己主动增长
if (fieldName.equalsIgnoreCase("id") || fieldName.equalsIgnoreCase("_id")) {
continue;
}
putValues(values, fd, obj);
}
return db.insert(DBUtils.getTableName(modeClass), null, values);
} ............ /**
* put value to ContentValues for Database
*
* @param values ContentValues object
* @param fd the Field
* @param obj the value
*/
private void putValues(ContentValues values, Field fd, Object obj) {
Class<? > clazz = values.getClass();
try {
Object[] parameters = new Object[]{fd.getName(), fd.get(obj)};
Class<? >[] parameterTypes = getParameterTypes(fd, fd.get(obj), parameters);
Method method = clazz.getDeclaredMethod("put", parameterTypes);
method.setAccessible(true);
method.invoke(values, parameters);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

有以上框架之后。我们如今来向数据库插入一条数据代码例如以下:

Person person = new Person("Tom",18,false);
DBManager.insert(person);

哇。如此简单。一行代码解决繁琐的插入操作。我们仅仅须要传入Person对象的实例作为參数就可以完毕数据库插入操作。再也不用去构建什么ContentVaules键值对了。

数据库操作–查询

android提供的数据库查询

android 的sqlite数据库提供的查询语句有rawQuery()方法。该方法的定义例如以下:

public Cursor rawQuery(String sql, String[] selectionArgs)

当中第一个參数是sql字符串,第二个參数是用于替换SQL语句中占位符(?

)的字符串数组。返回结果存放在Cursor对象当中,我们仅仅要循环一一取出数据就可以。当然我们平时不怎么用这种方法,由于须要记住非常多数据库查询语句的规则等。Android给开发人员封装了另外一个数据库查询方法。即SQLiteDatabase中的query()方法。

该方法的定义例如以下:

public Cursor query(String table, String[] columns, String selection,
String[] selectionArgs, String groupBy, String having,
String orderBy)

当中,

第一个參数是须要查询数据的表名称。

第二个參数指查询表中的那几列字段,假设不指定则默认查询全部列。

第三个參数是sql语句,表示查询条件。

第四个參数是用于替换第三个參数sql语句中的占位符(?)数组,假设第三,四个參数不指定则默认查询全部行;

第五个參数用于指定须要去group by的列,不指定则表示不正确查询结果进行group by操作。

第六个參数用于对group by之后的数据进行进一步的过滤。不指定则表示不进行过滤。

第七个參数用于指定查询结果的排序方式,不指定则表示使用默认的排序方式。

query()方法的參数是不是非常多,一般人都非常难记住这些參数的意思,在用的时候就非常不方便,比方你要查询数据库中 age=18的人。你的代码得这么写:

 Cursor cursor = db.query("Person", null, "age = ?

", new String[]{"18"}, null, null, null);

第三个參数是查询条件,去约束查询结果 age = 18。所以 第三个參数是“age= ?”。第四个參数用于替换第三个參数的占位符(?)。因此是String的数组。

查询的结果保存在Cursor中,为了拿到查询结果,我们不得不去变量里Cursor一一取出当中的数据并保存。

代码例如以下:

List<Person> list = new ArrayList<>();
if (cursor != null && cursor.moveToFirst()) {
do {
Person person = new Person();
int id = cursor.getInt(cursor.getColumnIndex("id"));
String name = cursor.getString(cursor.getColumnIndex("name"));
String age = cursor.getString(cursor.getColumnIndex("age"));
boolean flag = cursor.getInt(cursor.getColumnIndex("flag")) == 1 ? true : false;
person.setId(id);
person.setName(name);
person.setAge(age);
person.setFlag(flag);
list.add(person);
} while (cursor.moveToNext()); }

为了取得Cursor中的查询结果。我们写了如此多的繁琐的代码。假设此时有一个新的Student类,那么你是否又要去改动这个查询方法呢?如此看来该查询方法和取得结果是不是没有通用性。非常不方便使用。

对于讨厌敲反复代码的程序猿来说这样非常麻烦。用的不爽,那么有没有一种方法直接将查询结果转换成我须要的类的集合呢?这里我们又要用到自己写的查询框架了,利用该框架一行代码就可以搞定全部。

数据库查询框架

1.查询数据库中全部数据

  /**
* 查询数据库中全部的数据
*
* @param clazz
* @param <T> 以 List的形式返回数据库中全部数据
* @return 返回list集合
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
* @throws InvocationTargetException
*/
public <T> List<T> findAll(Class<T> clazz) {
Cursor cursor = db.query(clazz.getSimpleName(), null, null, null, null, null, null);
return getEntity(cursor, clazz);
} ..................... /**
* 从数据库得到实体类
*
* @param cursor
* @param clazz
* @param <T>
* @return
*/
private <T> List<T> getEntity(Cursor cursor, Class<T> clazz) {
List<T> list = new ArrayList<>();
try {
if (cursor != null && cursor.moveToFirst()) {
do {
Field[] fields = clazz.getDeclaredFields();
T modeClass = clazz.newInstance();
for (Field field : fields) {
Class<? > cursorClass = cursor.getClass();
String columnMethodName = getColumnMethodName(field.getType());
Method cursorMethod = cursorClass.getMethod(columnMethodName, int.class); Object value = cursorMethod.invoke(cursor, cursor.getColumnIndex(field.getName())); if (field.getType() == boolean.class || field.getType() == Boolean.class) {
if ("0".equals(String.valueOf(value))) {
value = false;
} else if ("1".equals(String.valueOf(value))) {
value = true;
}
} else if (field.getType() == char.class || field.getType() == Character.class) {
value = ((String) value).charAt(0);
} else if (field.getType() == Date.class) {
long date = (Long) value;
if (date <= 0) {
value = null;
} else {
value = new Date(date);
}
}
String methodName = makeSetterMethodName(field);
Method method = clazz.getDeclaredMethod(methodName, field.getType());
method.invoke(modeClass, value);
}
list.add(modeClass);
} while (cursor.moveToNext());
} } catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null) {
cursor.close();
}
}
return list;
}

查询全部数据并且自己主动保存在List中返回,无须用户去将Cursor解析成对象封装。简单易用。自须要一个方法一个參数就可以。调用代码例如以下:

List<Person> list = dbManager.findAll(Person.class);

超级简单啊!

2.查询指定条件的数据

 /**
* 依据指定条件返回满足条件的记录
*
* @param clazz 类
* @param select 条件语句 :("id>?")
* @param selectArgs 条件(new String[]{"0"}) 查询id=0的记录
* @param <T> 类型
* @return 返回满足条件的list集合
*/
public <T> List<T> findByArgs(Class<T> clazz, String select, String[] selectArgs) {
Cursor cursor = db.query(clazz.getSimpleName(), null, select, selectArgs, null, null, null);
return getEntity(cursor, clazz);
}

3.依据指定id查询一条数据

/**
* 通过id查找制定数据
*
* @param clazz 指定类
* @param id 条件id
* @param <T> 类型
* @return 返回满足条件的对象
*/
public <T> T findById(Class<T> clazz, int id) {
Cursor cursor = db.query(clazz.getSimpleName(), null, "id=" + id, null, null, null, null);
List<T> list = getEntity(cursor, clazz);
return list.get(0);
}

用户代码调用例如以下:

 Person p = dbManager.findById(Person.class, 1);

查询id=1的数据,第一个參数为Person类型,第二个參数为id值,查询结果直接保存在Person对象p里。

以上就是自己封装的数据库查询操作。简单易用,无须记住quary()方法中的那么多參数。也无须自己去一个个解析Cursor数据并保存。该方法一步到位,直接返回Person类型的list集合。

凝视:当中用到的一些方法我临时没有贴出来,文章最后我会把样例和代码都贴出来。

数据库删除操作

android提供的删除

android系统提供了sqlite数据库删除方法 delete(),其定义例如以下:

public int delete(String table, String whereClause, String[] whereArgs)

当中,第一个參数表示表名,第二个參数是条件SQL语句,第三个參数是替换第二个參数中的占位符(?)。

假如我要删除Person表中的age=18的数据,则代码调用例如以下:

db.delete("Person","age = ?",new String[]{"18"});

数据库删除框架

删除这一块比較简单,我直接贴出代码来

 /**
* 删除记录一条记录
*
* @param clazz 须要删除的类名
* @param id 须要删除的 id索引
*/
public void deleteById(Class<?> clazz, long id) {
db.delete(DBUtils.getTableName(clazz), "id=" + id, null);
}

用户调用例如以下:

dbManager.deleteById(Person.class, 1);

第一个 參数是Person类的类型,第二个參数是被删除数据的id。是不是非常easy呢?它的实现例如以下:

   /**
* 删除记录一条记录
*
* @param clazz 须要删除的类名
* @param id 须要删除的 id索引
*/
public void deleteById(Class<? > clazz, long id) {
db.delete(DBUtils.getTableName(clazz), "id=" + id, null);
}

数据库更新操作

android提供的更新操作

在android的sqlite中提供了update()方法来更新数据操作,其定义例如以下:

public int update(String table, ContentValues values, String whereClause, String[] whereArgs)

update()方法接收四个參数。第一个參数是表名。第二个參数是一个封装了待改动数据的ContentValues对象,第三和第四个參数用于指定改动哪些行,相应了SQL语句中的where部分。

比方我要改动id=1的Person人的年龄age改成20。那么代码实现例如以下:

 ContentValues values = new ContentValues();
values.put("age",20);
db.update("Person",values,"id = ?",new String[]{"1"});

该方法也算比較简单,那么我们来看看自己写的数据库框架是怎么实现的呢?

数据库框架更新操作

 ContentValues values = new ContentValues();
values.put("age", 34);
dbManager.updateById(Person.class, values, 1);

第一个參数为Person类的类型。第二个參数为须要更新的vaules,第三个參数是条件,更新id为1的数据。使用方法非常easy,它的实现例如以下:

 /**
* 更新一条记录
*
* @param clazz 类
* @param values 更新对象
* @param id 更新id索引
*/
public void updateById(Class<? > clazz, ContentValues values, long id) {
db.update(clazz.getSimpleName(), values, "id=" + id, null);
}

总结

自此。数据库的基本操作都罗列出来了,也说明了Android提供的sqlite数据库在平时开发中的一些繁琐的地方。所以自己总结提取了一个简单型的数据库操作框架,仅仅是比較简单的操作。假设你有数据量大的操作,请出门左转利用其它多功能成熟稳定的数据库开源框架。

该框架仅仅适合数据量小。不存在表与表之间的相应关系。能够将查询结果直接转换成对象的轻量级框架。

源代码以及演示样例地址:DataBaseDemo

自己动手写Android数据库框架的更多相关文章

  1. 自己动手写Android框架-数据库框架

    大家在工作中基本上都有使用到数据库框架 关系型:ORMLite,GreenDao 对象型:DB4O,Perst 这些数据库用起来都非常的简单,对于我们Android上来说这些数据库足够我们使用了,但是 ...

  2. 自己动手写Android插件化框架

    自己动手写Android插件化框架 转 http://www.imooc.com/article/details/id/252238   最近在工作中接触到了Android插件内的开发,发现自己这种技 ...

  3. Android 数据库框架OrmLite的使用(一)

    在这里记录下最基本的用法,官网上可了解相关的介绍. 1.下载OrmLite jar 在下载android的:ormlite-android-4.48.jar和ormlite-core-4.48.jar ...

  4. 自己动手写PHP MVC框架

    自己动手写PHP MVC框架 来自:yuansir-web.com / yuansir@live.cn 代码下载: https://github.com/yuansir/tiny-php-framew ...

  5. Android数据库框架——GreenDao轻量级的对象关系映射框架,永久告别sqlite

    Android数据库框架--GreenDao轻量级的对象关系映射框架,永久告别sqlite 前不久,我在写了ORMLite这个框架的博文 Android数据库框架--ORMLite轻量级的对象关系映射 ...

  6. Android数据库框架——ORMLite轻量级的对象关系映射(ORM)Java包

    Android数据库框架--ORMLite轻量级的对象关系映射(ORM)Java包 事实上,我想写数据库的念头已经很久了,在之前写了一个答题系统的小项目那只是初步的带了一下数据库,数据库是比较强大的, ...

  7. Android 数据库框架ormlite

    Android 数据库框架ormlite 使用精要 前言 本篇博客记录一下笔者在实际开发中使用到的一个数据库框架,这个可以让我们快速实现数据库操作,避免频繁手写sql,提高我们的开发效率,减少出错的机 ...

  8. Android 数据库框架总结(转)

    转自 http://blog.csdn.net/da_caoyuan/article/details/61414626 一:OrmLite 简述: 优点: 1.轻量级:2.使用简单,易上手:3.封装完 ...

  9. Android数据库框架-----ORMLite关联表的使用

    上一篇已经对ORMLite框架做了简单的介绍:Android数据库框架-----ORMLite 的基本用法~~本篇将介绍项目可能会使用到的一些用法,也为我们的使用ORMLite框架总结出一个较合理的用 ...

随机推荐

  1. 【java_设计模式】建造者模式

    应用场景对生成对象进行链式配置好处:按需配置对象,灵活修改默认配置,比构造方法创建对象更直观. public static void init() {if (okHttpClient == null) ...

  2. ALTER LANGUAGE - 修改一个过程语言的定义

    SYNOPSIS ALTER LANGUAGE name RENAME TO newname DESCRIPTION 描述 ALTER LANGUAGE 修改一门语言的定义. 目前唯一的功能就是重命名 ...

  3. 解决vue项目eslint校验 Do not use 'new' for side effects 的两种方法

    import Vue from 'vue' import App from './App.vue' import router from './router' new Vue({ el: '#app' ...

  4. Oracle使用plsql连不上本地数据库,cmd中使用sqlplus连的上的可能解决方案

    1.无监听程序 原因: 最有可能是oracle监听的服务没有启动起来. 2.ORA-12514 TNS 监听程序当前无法识别连接描述符中请求服务 原因: 1.服务没有配置127.0.0.1或监听程序没 ...

  5. vue中的组件传值

    组件关系可以分为父子组件通信.兄弟组件通信.跨级组件通信. 父传子 - props 子传父 - $emit 跨级可以用bus 父子双向 v-model 父链(this.$parent this.$ch ...

  6. 查找BUG的方法

    1)测试环境 1)代码调试 2)问题重现 3)思考问题所在 2)生产环境 1)思考 2)测试本地环境是否存在问题 3)打开日志查看 4)思考是否是数据原因 5)拷贝数据到本地进行重现 3)未知错误 1 ...

  7. 服务器端编程心得(二)—— Reactor模式

    最近一直在看游双的<高性能linux服务器编程>一书,下载链接: http://download.csdn.net/detail/analogous_love/9673008 书上是这么介 ...

  8. 笔试算法题(16):二叉树深度计算 & 字符串全排列

    出题:要求判断二元树的深度(最长根节点到叶节点的路径): 分析:二元递归不容易使用循环实现 解题: struct Node { int value; Node *left; Node *right; ...

  9. 自动生成 serialVersionUID 的设置

    1 把鼠标放在类名上,会出现小灯泡的形状 点击 Add ‘serialVersionUID’ field... 即可生成 如果鼠标放在类名上没有出现 Add ‘serialVersionUID’ fi ...

  10. shit IE & no table `border-collapse: collapse;`

    shit IE no table border-collapse: collapse; /* IE & shit table & border-collapse: collapse; ...