正常来说使用sql来操作数据库的话没啥问题,但是对于一些不太擅长写sql的同学来说,通过面向对象的方式来操作数据库,可以极大的避免写sql时不小心犯错误的情况,接下来让我们直接进入主题
首先是需要先定义接口规范,即数据库常用的增删改查操作,这用来让我们知道有哪些功能,当然还可以自己再继续扩展
public interface IBaseDao<T> {
/**
* 插入
*/
long insert(T entity);
/**
* 更新
*/
long update(T entity, T where);
/**
* 删除
*/
int delete(T where);
/**
* 查询
*/
List<T> query(T where);
/**
* 限制查询
*/
List<T> query(T where, String orderBy, Integer startIndex, Integer limit);
}
使用注解的方式可以让我们来更好的定义表名以及列名,因为有时候对象既要对应后台下发的字段,又想适应数据库名的规范
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbTable {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DbField {
String value();
}
接着就是建库的操作,用单例方式来进行建立数据库操作
public class BaseDaoFactory {
private static BaseDaoFactory sInstance;
private SQLiteDatabase sqLiteDatabase;
/**
* 设计一个数据库的连接池
*/
protected Map<String, BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());
/**
* 定义建数据库的路径
*/
private String sqliteDatabasePath;
public static BaseDaoFactory getInstance() {
if (sInstance == null) {
synchronized (BaseDaoFactory.class) {
if (sInstance == null) {
sInstance = new BaseDaoFactory();
}
}
}
return sInstance;
}
public BaseDaoFactory() {
// Environment.getExternalStorageDirectory() + File.separator;
sqliteDatabasePath = "data/data/com.tgp.dbapp/test.db";
//新建数据库
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
}
}
这里数据库的路径可以也写到sd卡中,这样即使App被卸载了,下次安装的时候还可以直接获取数据继续操作,当然这个具体根据需求情况来存储位置就可以
接着创建BaseDao去实现定义的接口,同时让我们在构造方法中对其进行一些初始化操作,
public void init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
this.sqLiteDatabase = sqLiteDatabase;
this.entityClass = entityClass;
//*************自动建表*************
//根据传入的entityClass来建表,只需要建一次
if (!isInit) {
//取得表名
if (entityClass.getAnnotation(DbTable.class) == null) {
//用类名做表名
this.tableName = entityClass.getSimpleName();
} else {
//取得注解上的名字
this.tableName = entityClass.getAnnotation(DbTable.class).value();
}
//执行建表操作
String createSql = getCreateTableSql();
Log.i(TAG, createSql);
this.sqLiteDatabase.execSQL(createSql);
cacheMap = new HashMap<>();
initCacheMap();
isInit = true;
}
}
用面向对象的方式来操作数据库,本质上来说还是使用sql,只不过对外展现的样子是面向对象的方式
所以执行建表操作的时候就需要我们根据对象去生成相应的sql语句,平时我们写的建表语句为
create table if not exists tb_user(id integer, name TEXT)
这种样子,上面的表名我们已经可以通过注解或者获取类名的方式获取到了,现在需要的是获取到有哪些列,即对象中是什么字段,这时候就用到了反射的操作
private String getCreateTableSql() {
//create table if not exists tb_user(id integer, name TEXT)
StringBuilder sb = new StringBuilder();
sb.append("create table if not exists ");
sb.append(tableName).append("(");
//反射得到所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();
for (Field field : declaredFields) {
//拿到成员类型
Class<?> type = field.getType();
if (field.getAnnotation(DbField.class) != null) {
if (type == String.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" TEXT,");
} else if (type == Integer.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" Integer,");
} else if (type == Long.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" BIGINT,");
} else if (type == Double.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" DOUBLE,");
} else if (type == byte[].class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" BLOB,");
} else {
//不支持的类型
continue;
}
} else {
if (type == String.class) {
sb.append(field.getName()).append(" TEXT,");
} else if (type == Integer.class) {
sb.append(field.getName()).append(" Integer,");
} else if (type == Long.class) {
sb.append(field.getName()).append(" BIGINT,");
} else if (type == Double.class) {
sb.append(field.getName()).append(" DOUBLE,");
} else if (type == byte[].class) {
sb.append(field.getName()).append(" BLOB,");
} else {
//不支持的类型
continue;
}
}
}
//去掉最后的逗号
if (',' == (sb.charAt(sb.length() - 1))) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(")");
return sb.toString();
}
上述代码中,同样的有注解的用注解来做列名,没有的话使用字段名来做列名,并根据数据库的列的字段类型来匹配
this.sqLiteDatabase.execSQL(createSql);获取到创建表的语句后我们就可以执行建表操作了
我们这里需要缓存下列名和字段的map,以此来方便后面的增删改查等操作
private void initCacheMap() {
//取得所有字段名
// 查询空表
String sql = "select * from " + tableName + " limit 1, 0";
Cursor cursor = sqLiteDatabase.rawQuery(sql, null);
//获取列名
String[] columnNames = cursor.getColumnNames();
//取所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
}
//字段跟对象的成员变量一一对应
for (String columName : columnNames) {
Field columnField = null;
for (Field field : declaredFields) {
String fieldName = "";
if (field.getAnnotation(DbField.class) != null) {
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if (columName.equals(fieldName)) {
columnField = field;
break;
}
}
if (columnField != null) {
cacheMap.put(columName, columnField);
}
}
}
到这里我们就可以进行增删改查的操作了,首先来看下增的操作
增
正常的sql语句写法为 sqLiteDatabase.insert(tableName, null, contentValues);
表名我们已经获取到了,接着就是去组装数据的操作,首先让我们来获取一个Map,key是列名,value就是字段对应的值,上面我们缓存的对象的 列名-字段,这里我们拿到传进来的对象,通过反射的操作就可以拿到对应的值
private Map<String, String> getValueMap(T entity) {
HashMap<String, String> map = new HashMap<>();
//返回的是所有的成员变量
for (Field next : cacheMap.values()) {
next.setAccessible(true);
try {
//获取对象的属性值
Object o = next.get(entity);
if (o == null) {
continue;
}
//张三, 3
String value = o.toString();
String key = "";
if (next.getAnnotation(DbField.class) != null) {
key = next.getAnnotation(DbField.class).value();
} else {
key = next.getName();
}
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
map.put(key, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
拿到这里列-字段内容的map后,我们就可以去组装contentValues了
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues = new ContentValues();
for (String key : map.keySet()) {
String value = map.get(key);
if (value != null) {
contentValues.put(key, value);
}
}
return contentValues;
}
相应的增加操作就完成了
@Override
public long insert(T entity) {
//准备好ContentValues中需要的数据
Map<String, String> map = getValueMap(entity);
//把数据转移到ContentValues中
ContentValues contentValues = getContentValues(map);
//数据库操作
return sqLiteDatabase.insert(tableName, null, contentValues);
}
update:
sqLiteDatabase.update(tableName, contentvalue, "name = ?", new String[]{"tgp"});
更改的sql语句是这样写的,表名已经有了,然后就是要变更成什么以及变更的条件是什么的组合
@Override
public long update(T entity, T where) {
// sqLiteDatabase.update(tableName, contentvalue, "name = ?", new String[]{"tgp"});
int result = -1;
Map<String, String> values = getValueMap(entity);
ContentValues contentValues = getContentValues(values);
//条件map
Map<String, String> values1 = getValueMap(where);
Condition condition = new Condition(values1);
result = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
return result;
}
private class Condition {
String whereClause;
String[] whereArgs;
Condition(Map<String, String> values) {
ArrayList<String> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
sb.append("1=1");
//取得所有成员名
Set<String> keys = values.keySet();
for (String key : keys) {
String value = values.get(key);
if (value != null) {
sb.append(" and ").append(key).append("=?");
list.add(value);
}
}
this.whereClause = sb.toString();
whereArgs = list.toArray(new String[list.size()]);
}
}
update有两个参数,第一个参数是要变成什么,第二个对象是用来组成判断条件,Condition只是用来做了一个封装
后面的查询和删除本质上差不多,这里把BaseDao的整个代码贴出来
package com.tgp.dbapp.db;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import android.util.Log;
import com.tgp.dbapp.annotation.DbField;
import com.tgp.dbapp.annotation.DbTable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 建表
*/
public class BaseDao<T> implements IBaseDao<T> {
private static final String TAG = "BaseDao";
/**
* 持有数据库操作的引用
*/
private SQLiteDatabase sqLiteDatabase;
/**
* 表名
*/
private String tableName;
/**
* 持有操作数据库所对应的java类型
*/
private Class<T> entityClass;
/**
* 标记用来表示是否做过初始化操作
*/
private boolean isInit = false;
/**
* 定义一个缓存空间(key 字段名 value 成员变量)
*/
private HashMap<String, Field> cacheMap;
public void init(SQLiteDatabase sqLiteDatabase, Class<T> entityClass) {
this.sqLiteDatabase = sqLiteDatabase;
this.entityClass = entityClass;
//*************自动建表*************
//根据传入的entityClass来建表,只需要建一次
if (!isInit) {
//取得表名
if (entityClass.getAnnotation(DbTable.class) == null) {
//用类名做表名
this.tableName = entityClass.getSimpleName();
} else {
//取得注解上的名字
this.tableName = entityClass.getAnnotation(DbTable.class).value();
}
//执行建表操作
String createSql = getCreateTableSql();
Log.i(TAG, createSql);
this.sqLiteDatabase.execSQL(createSql);
cacheMap = new HashMap<>();
initCacheMap();
isInit = true;
}
}
private void initCacheMap() {
//取得所有字段名
// 查询空表
String sql = "select * from " + tableName + " limit 1, 0";
Cursor cursor = sqLiteDatabase.rawQuery(sql, null);
//获取列名
String[] columnNames = cursor.getColumnNames();
//取所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();
for (Field field : declaredFields) {
field.setAccessible(true);
}
//字段跟对象的成员变量一一对应
for (String columName : columnNames) {
Field columnField = null;
for (Field field : declaredFields) {
String fieldName = "";
if (field.getAnnotation(DbField.class) != null) {
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if (columName.equals(fieldName)) {
columnField = field;
break;
}
}
if (columnField != null) {
cacheMap.put(columName, columnField);
}
}
}
/**
* 拼接SQL
*/
private String getCreateTableSql() {
//create table if not exists tb_user(id integer, name TEXT)
StringBuilder sb = new StringBuilder();
sb.append("create table if not exists ");
sb.append(tableName).append("(");
//反射得到所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();
for (Field field : declaredFields) {
//拿到成员类型
Class<?> type = field.getType();
if (field.getAnnotation(DbField.class) != null) {
if (type == String.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" TEXT,");
} else if (type == Integer.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" Integer,");
} else if (type == Long.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" BIGINT,");
} else if (type == Double.class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" DOUBLE,");
} else if (type == byte[].class) {
sb.append(field.getAnnotation(DbField.class).value()).append(" BLOB,");
} else {
//不支持的类型
continue;
}
} else {
if (type == String.class) {
sb.append(field.getName()).append(" TEXT,");
} else if (type == Integer.class) {
sb.append(field.getName()).append(" Integer,");
} else if (type == Long.class) {
sb.append(field.getName()).append(" BIGINT,");
} else if (type == Double.class) {
sb.append(field.getName()).append(" DOUBLE,");
} else if (type == byte[].class) {
sb.append(field.getName()).append(" BLOB,");
} else {
//不支持的类型
continue;
}
}
}
//去掉最后的逗号
if (',' == (sb.charAt(sb.length() - 1))) {
sb.deleteCharAt(sb.length() - 1);
}
sb.append(")");
return sb.toString();
}
private ContentValues getContentValues(Map<String, String> map) {
ContentValues contentValues = new ContentValues();
for (String key : map.keySet()) {
String value = map.get(key);
if (value != null) {
contentValues.put(key, value);
}
}
return contentValues;
}
/**
* key(字段) value(成员变量)
*/
private Map<String, String> getValueMap(T entity) {
HashMap<String, String> map = new HashMap<>();
//返回的是所有的成员变量
for (Field next : cacheMap.values()) {
next.setAccessible(true);
try {
//获取对象的属性值
Object o = next.get(entity);
if (o == null) {
continue;
}
//张三, 3
String value = o.toString();
String key = "";
if (next.getAnnotation(DbField.class) != null) {
key = next.getAnnotation(DbField.class).value();
} else {
key = next.getName();
}
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
map.put(key, value);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
@Override
public long insert(T entity) {
// ContentValues contentValues = new ContentValues();
// contentValues.put
// sqLiteDatabase.insert()
//准备好ContentValues中需要的数据
Map<String, String> map = getValueMap(entity);
//把数据转移到ContentValues中
ContentValues contentValues = getContentValues(map);
//数据库操作
return sqLiteDatabase.insert(tableName, null, contentValues);
}
@Override
public long update(T entity, T where) {
// sqLiteDatabase.update(tableName, contentvalue, "name = ?", new String[]{"tgp"});
int result = -1;
Map<String, String> values = getValueMap(entity);
ContentValues contentValues = getContentValues(values);
//条件map
Map<String, String> values1 = getValueMap(where);
Condition condition = new Condition(values1);
result = sqLiteDatabase.update(tableName, contentValues, condition.whereClause, condition.whereArgs);
return result;
}
private class Condition {
String whereClause;
String[] whereArgs;
Condition(Map<String, String> values) {
ArrayList<String> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
sb.append("1=1");
//取得所有成员名
Set<String> keys = values.keySet();
for (String key : keys) {
String value = values.get(key);
if (value != null) {
sb.append(" and ").append(key).append("=?");
list.add(value);
}
}
this.whereClause = sb.toString();
whereArgs = list.toArray(new String[list.size()]);
}
}
@Override
public int delete(T where) {
// sqLiteDatabase.delete(tableName, "name = ?", new String[]{"tgp"});
Map<String, String> values = getValueMap(where);
Condition condition = new Condition(values);
int delete = sqLiteDatabase.delete(tableName, condition.whereClause, condition.whereArgs);
return delete;
}
@Override
public List<T> query(T where) {
return query(where, null, null, null);
}
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
// sqLiteDatabase.query(tableName, null, "od = ?", new String[]{}, null, null, "1,5");
Map<String, String> values = getValueMap(where);
String limitString = "";
if (startIndex != null && limit != null) {
limitString = startIndex + " , " + limit;
}
Condition condition = new Condition(values);
Cursor query = sqLiteDatabase.query(tableName, null, condition.whereClause, condition.whereArgs, null, orderBy, limitString);
List<T> objects = getResult(query, where);
return objects;
}
private List<T> getResult(Cursor query, T where) {
ArrayList objects = new ArrayList<>();
Object item = null;
while (query.moveToNext()) {
try {
item = where.getClass().newInstance();
for (Map.Entry<String, Field> stringFieldEntry : cacheMap.entrySet()) {
//获取列名
String columnName = stringFieldEntry.getKey();
//用列名来拿到列名在游标中的位置
int columnIndex = query.getColumnIndex(columnName);
Field value = stringFieldEntry.getValue();
Class<?> type = value.getType();
if (columnIndex != -1) {
if (type == String.class) {
value.set(item, query.getString(columnIndex));
} else if (type == Double.class) {
value.set(item, query.getDouble(columnIndex));
} else if (type == Integer.class) {
value.set(item, query.getInt(columnIndex));
} else if (type == Long.class) {
value.set(item, query.getLong(columnIndex));
} else if (type == byte[].class) {
value.set(item, query.getBlob(columnIndex));
} else {
continue;
}
}
}
objects.add(item);
} catch (Exception e) {
e.printStackTrace();
}
}
query.close();
return objects;
}
}
这里说一下,查询结束后返回的集合,就是根据查询的结果又使用反射的方式重新赋值了下
当然,获取basedao的地方还是在basedao工厂中,这里是BaseDaoFactory的完整代码
package com.tgp.dbapp.db;
import android.database.sqlite.SQLiteDatabase;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* 工厂
*/
public class BaseDaoFactory {
private static BaseDaoFactory sInstance;
private SQLiteDatabase sqLiteDatabase;
/**
* 设计一个数据库的连接池
*/
protected Map<String, BaseDao> map = Collections.synchronizedMap(new HashMap<String, BaseDao>());
/**
* 定义建数据库的路径
*/
private String sqliteDatabasePath;
public static BaseDaoFactory getInstance() {
if (sInstance == null) {
synchronized (BaseDaoFactory.class) {
if (sInstance == null) {
sInstance = new BaseDaoFactory();
}
}
}
return sInstance;
}
public BaseDaoFactory() {
// Environment.getExternalStorageDirectory() + File.separator;
sqliteDatabasePath = "data/data/com.tgp.dbapp/test.db";
//新建数据库
sqLiteDatabase = SQLiteDatabase.openOrCreateDatabase(sqliteDatabasePath, null);
}
public <T> BaseDao<T> getBaseDao(Class<T> entity) {
BaseDao baseDao = null;
try {
baseDao = BaseDao.class.newInstance();
baseDao.init(sqLiteDatabase, entity);
} catch (Exception e) {
e.printStackTrace();
}
return baseDao;
}
public <T extends BaseDao<M>, M> T getBaseDao(Class<T> daoClass, Class<M> entity){
BaseDao baseDao = null;
try {
baseDao = daoClass.newInstance();
baseDao.init(sqLiteDatabase, entity);
} catch (Exception e) {
e.printStackTrace();
}
return (T)baseDao;
}
}
使用方式也很简单,我们建一个对应的model
@DbTable("tb_person")
public class Person {
@DbField("_id")
private Integer id;
private String name;
private String password;
public Person() {
}
public Person(Integer id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
是否使用注解,根据自己的需要来定就可以
使用的时候,
BaseDao<Person> person = BaseDaoFactory.getInstance().getBaseDao(Person.class);
person.insert(new Person(1, "tgpperson", "123person"));
通过工厂获取到对象,直接进行增删改查的操作即可
假设我们想扩展一下,因为有的数据库操作比较复杂,牵扯到多表联动之类的,我们可以自行扩展
public class BaseDaoImpl<T> extends BaseDao<T> {
public List<T> query(String sql) {
return null;
}
}
我们通过继续BaseDao,来增加方法即可
这里我们工厂中也提供了对应的方法,直接获取到对象
BaseDaoNewImpl baseDao = BaseDaoFactory.getInstance().getBaseDao(BaseDaoNewImpl.class, Person.class);
Person person = new Person();
person.setName("tgp update");
Person where = new Person();
where.setId(1);
long update = baseDao.update(person, where);
用面向对象的方式来操作数据库本质上来说只是进行了一层封装,其实还是进行的sql操作,只是对写代码来说更友好些
至于像数据库升级,字段扩展,我们可以通过xml的方式来进行操作,通过从网络获取到最新的xml并进行解析,和当前的版本做比较,因为有可能用户长时间不用这时候你的数据库版本可能已经从1-2-3-4这样,所以xml中对此都需要写出来该怎么进行操作
好了,以上文章就是对数据库的封装来达到面向对象方式的使用