在上一篇目中,讲解了如何自动建表,代码.
本期来实现增删改查,这里现实最简单的增删改查,主要讲解实现思想,复杂的会在后续文章中讲解。
准备工作
1.首先在BaseDao init 方法,在建表时,缓存一个map , 存储 (key - 字段名 ) ( value - 成员变量 ) private Map<String, Field> cacheMap;
//建表
if (!isInit) {
//判断数据库是否打开
if (!sqLiteDatabase.isOpen()) {
return false;
}
//数据库已打开
//建表的sql语句
sqLiteDatabase.execSQL(getCreateSql());
cacheMap = new HashMap<>();
initCacheMap();
isInit = true;
}
initCacheMap(); 方法实现的很简单,就是将字段名和成员变量缓存到map中去,这样的好处就是在建表时就缓存好了,我们进行增删改查的操作就可以用缓存的map来进行操作,从map中拿到 字段名 和成员变量 根据成员变量可以获得成员变量的值。
下面来看map的实现
/**
* 初始化缓存值
*/
private void initCacheMap() {
// 1 获取数据库的所有 列名
String sql = "select * from " + tbName + " limit 1,0";//空表
// 拿到所有的列名
Cursor cursor = sqLiteDatabase.rawQuery(sql, null);//获取游标
String[] columnNames = cursor.getColumnNames();
//获取所有的成员变量
Field[] declaredFields = entityClass.getDeclaredFields();
for (String columnName : columnNames) {
Field columnField = null;
for (Field field : declaredFields) {
String fieldName = null;
if (field.getAnnotation(DbField.class) != null) {
fieldName = field.getAnnotation(DbField.class).value();
} else {
fieldName = field.getName();
}
if (fieldName.equals(columnName)) {
columnField = field;
break;
}
}
if (columnField != null) {
cacheMap.put(columnName, columnField);
}
}
}
缓存好之后,根据cacheMap准备好ContentValues 所需要的数据 ,返回一个map,这个map存储的就是存取 key - 字段名,value - 插入的数据
/**
* 获取 map 存取 key - 字段名,value - 插入的数据
*
* @param entity
* @return 准备好ContentValues 所需要的数据
*/
private Map<String, String> getValues(T entity) {
Map<String, String> map = new HashMap<>();
// 获取成员变量
Iterator<Field> iterator = cacheMap.values().iterator();
while (iterator.hasNext()) {
Field next = iterator.next();
next.setAccessible(true);
//获取成员变量的值
try {
Object o = next.get(entity);
if (o == null) {
continue;
}
String value = o.toString();
//获取列名
String key = null;
if (next.getAnnotation(DbField.class) != null) {
key = next.getAnnotation(DbField.class).value();
} else {
key = next.getName();
}
map.put(key, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return map;
}
根据Map<String, String> getValues得到的map 把数据转移到 ContentValues
private ContentValues getContentValues(Map<String, String> values) {
ContentValues contentValues = new ContentValues();
Set<String> keys = values.keySet();
Iterator<String> iterator = keys.iterator();
while (iterator.hasNext()) {
String key = iterator.next();
String value = values.get(key);
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
contentValues.put(key, value);
}
}
return contentValues;
}
准备工作完毕,我们进行插入操作
很简单,看一下代码
@Override
public long insert(T entity) {
Map<String, String> values = getValues(entity);
//把数据转移到 ContentValues
ContentValues contentValues = getContentValues(values);
long result = sqLiteDatabase.insert(tbName, null, contentValues);
return result;
}
调用方式
User user = new User();
user.id = 1;
user.name = "sss";
user.pass = "123";
BaseDaoFactory.getInstance().getBaseDao(User.class).insert(user);
https://blog.csdn.net/linchunhua/article/details/7184439 sqlite3 命令
来看一下,是否将这条数据插入到了数据库中了,
image.png
OK,这条数据确实插入进去了。
更新操作
我们将 IBaseDao 添加一下代码
public interface IBaseDao<T> {
long insert(T entry);
long update(T entry, T where);
int delete(T where);
List<T> query(T where);
/**
* @param where 查询的条件
* @param orderBy 排序
* @param startIndex 开始的位置
* @param limit 查多少个
* @return
*/
List<T> query(T where, String orderBy, Integer startIndex, Integer limit);
List<T> query(String sql);
}
先看一下,原始的更新操作
sqLiteDatabase.update("表名","ContentValues","name = ?",new String[]{}) 原始操作
分析一下,首先表名和ContentValues,我们都可以拿到,在上面准备工作中,写了一个方法getContentValues,这样我们就可以很方便的拿到ContentValues
Map<String, String> values = getValues(entry);// 拿到
ContentValues contentValues = getContentValues(values);
那么对于 "name = ?",new String[]{} 如何更灵活的封装呢?
在BaseDao里面写一个 内部类,来专门处理条件。
/**
* 条件 拼接处理类
*/
private class Codition {
private String whereCause;//name = ?
private String[] whereArgs;//new String[]{}
Codition(Map<String, String> whereCasue) {
this(whereCasue, true);
}
Codition(Map<String, String> whereCasue, boolean isAnd) {
ArrayList<String> valueList = new ArrayList<>();
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append("1==1");
Set<String> keySet = whereCasue.keySet();
for (String key : keySet) {
String value = whereCasue.get(key);
if (value != null) {
// or
if (isAnd) {
keyBuilder.append(" and ").append(key).append("=?");
} else {
keyBuilder.append(" or ").append(key).append("=?");
}
valueList.add(value);
}
}
this.whereCause = keyBuilder.toString();
this.whereArgs = valueList.toArray(new String[valueList.size()]);
}
}
以上代码比较简单,主要是通过 ArrayList<String> valueList = new ArrayList<>(); 来装载条件的值。StringBuilder keyBuilder = new StringBuilder(); 来拼接where 条件。
那么更新操作就可以写成这样:
@Override
public long update(T entry, T where) {
// sqLiteDatabase.update("表名","ContentValues","name = ?",new String[]{}) 原始操作
int result = -1;
Map<String, String> values = getValues(entry);// 拿到
ContentValues contentValues = getContentValues(values);
Map<String, String> whereCause = getValues(where);
Codition codition = new Codition(whereCause);
result = sqLiteDatabase.update(tbName, contentValues, codition.whereCause, codition.whereArgs);
return result;
}
调用更新操作,通过对象的方式来进行封装,这样就不用再去写复杂的SQL语句了:
public void update(View view) {
User user = new User();
user.name = "bbbbb";
User whereUser = new User();
whereUser.id = 2;
BaseDaoFactory.getInstance().getBaseDao(User.class).update(user, whereUser);
}
image.png
删除操作
删除操作就更叫简单了,一样的道理。
@Override
public int delete(T where) {
// 原始的写法
// sqLiteDatabase.delete("table","where","whereArgs");
int result;
Codition codition = new Codition(getValues(where));
result = sqLiteDatabase.delete(tbName, codition.whereCause, codition.whereArgs);
return result;
}
public void delete(View view) {
User user = new User();
user.id = 2;
BaseDaoFactory.getInstance().getBaseDao(User.class).delete(user);
}
image.png
查询操作
查询操作比较复杂,查询方法有多个,我们这里只讲一个,剩下的都是同样的道理
原始的查询方法:sqLiteDatabase.query(tbName,null,"id = ?",new String[],null,null,orderBy,"1,5")
方法传递的值都很简单,这里就不在复述了。
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {}
tbName "id = ?", new String[] 这几个都可以通过 Codition codition = new Codition(getValues(where)); 拿到。
limit 就是:
String strLimit = null;
if (startIndex != null && limit != null) {
strLimit = startIndex + " , " + limit;
}
那么这样就可以写成;
String strLimit = null;
if (startIndex != null && limit != null) {
strLimit = startIndex + " , " + limit;
}
Codition codition = new Codition(getValues(where));
Cursor query = sqLiteDatabase.query(tbName, null,
codition.whereCause, codition.whereArgs, null, null, orderBy, strLimit);
最重要的就是通过Cursor 转成List<T> 这里涉及到反射的知识,写一遍就懂了,代码都有注释。
// obj 是用来表示user的结构的
private List<T> getListResult(Cursor cursor, T obj) {
List list = new ArrayList<>();// 存储 user对象
Object item = null;// Object 的对象
while (cursor.moveToNext()) {
try {
item = obj.getClass().newInstance();// new User 但是不知道Object 中的成员变量
Iterator<Map.Entry<String, Field>> iterator = cacheMap.entrySet().iterator();//cacheMap 中存的是列名和成员变量
while (iterator.hasNext()) {// 迭代 map 将成员变量和值 放到item中去 循环完成list 添加一个
Map.Entry<String, Field> entry = iterator.next();
//取列名
String colnumName = entry.getKey();
//然后以列名拿到列名在游标的位置
Integer columnIndex = cursor.getColumnIndex(colnumName);
Field field = entry.getValue();// 拿到列名对应的成员变量
Class<?> type = field.getType();//成员变量的类型
if (columnIndex != -1) {// 查询成功的
if (type == String.class) {
//将成员变量还有值 设置到item中去
field.set(item, cursor.getString(columnIndex));
} else if (type == Integer.class) {
field.set(item, cursor.getInt(columnIndex));
} else if (type == Long.class) {
field.set(item, cursor.getLong(columnIndex));
} else if (type == Double.class) {
field.set(item, cursor.getDouble(columnIndex));
} else if (type == byte[].class) {
field.set(item, cursor.getBlob(columnIndex));
} else if (type == int.class) {
field.set(item, cursor.getInt(columnIndex));
} else {
continue;
}
}
}
list.add(item);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
cursor.close();//关闭游标
return list;
}
那么我们的查询操作就是:
@Override
public List<T> query(T where) {
// sqLiteDatabase.query()
return query(where, null, null, null);
}
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
// sqLiteDatabase.query(tbName,null,"id = ?",new String[],null,null,orderBy,"1,5")
String strLimit = null;
if (startIndex != null && limit != null) {
strLimit = startIndex + " , " + limit;
}
Codition codition = new Codition(getValues(where));
Cursor query = sqLiteDatabase.query(tbName, null,
codition.whereCause, codition.whereArgs, null, null, orderBy, strLimit);
//定义一个用来解析游标的方法
return getListResult(query, where);
}
调用:
public void query(View view) {
User user = new User();
user.id = 1;
List<User> query = BaseDaoFactory.getInstance().getBaseDao(BaseDaoNewImpl.class, User.class).query(user);
for (int i = 0; i < query.size(); i++) {
Log.e(TAG, "query: " + query.get(i).toString());
}
}
OK,查询这里稍微麻烦一些,明白思想就可以了,具体的细节可以自己来写,来更深入的理解。如何来实现分组查询。
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit, String groupBy) {
return null;
}
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit, String groupBy, String having) {
return null;
}
最后BaseDaoFactory的更改
/**
* 生产basedao 对象
*
* @param entityClass
* @param <T> User 对象
* @param <M> BaseDao {@link BaseDao}
* @return
*/
public synchronized <M extends BaseDao<T>, T> M getBaseDao(Class<M> daoClass, Class<T> entityClass) {
BaseDao baseDao = null;
try {
baseDao = daoClass.newInstance();
baseDao.init(sqLiteDatabase, entityClass);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return (M) baseDao;
}
这样的好处,遵循里氏替换原则。进行新的实现在BaseDao的基础上实现一个新的方法。
网友评论