接着上一篇讲,今天把数据库的增删改查操作写完,在讲这前把第一篇出现的bug修复下,bug出现在自动见表那块,现在看下第一篇自动见表的代码:
@Override
protected String createTable(User user) {
String tb_name = User.class.getAnnotation(AnnTableName.class).value();
Field[] fields = User.class.getDeclaredFields();
String fieldName = "";
String type = "";
int len = 0;
Map<String,Field> columsMap = new HashMap<>();
for(Field field:fields){
columsMap.put(field.getName(),field);
}
StringBuffer sb = new StringBuffer(Constant.create_table_prix);
sb.append(" ");
sb.append(tb_name);
boolean isFirst = false;
int i=0;
for (Map.Entry<String, Field> entry: columsMap.entrySet()) {
Field filed = entry.getValue();
if(!isFirst){
isFirst = true;
sb.append("(");
}
if(filed.getAnnotation(AnnFiled.class)!=null){
fieldName=filed.getAnnotation(AnnFiled.class).columnName();
type = filed.getAnnotation(AnnFiled.class).type();
len = filed.getAnnotation(AnnFiled.class).len();
sb.append(fieldName).append(" ").append(type).append(len>0?"("+len+")":"").append((i==columsMap.size()-1)? ")":",");
i++;
}
}
LogUtils.e(sb.toString());
return sb.toString();
}
出现bug的地方在:
sb.append(fieldName).append(" ").append(type).append(len>0?"("+len+")":"").append((i==columsMap.size()-1)? ")":",");
是因为我们在反射时获取的字段多了2个,一个是$change,一个是$seriallizable,但是这二个属性是没有注解的,这就是我们map集合的size=5,但是我做了这个判断:
if(filed.getAnnotation(AnnFiled.class)!=null)
然后进行拼接出现了问题,我的解决思路是先判断这个属性上的注解是否等于null,不等于null,就临时用一个map缓存起来,然后再去遍历这map,这个bug解决起来也不是很难,我直接把我现在的代码贴下:
package com.sqlite.dao;
import android.text.TextUtils;
import com.sqlite.annotation.AnnFiled;
import com.sqlite.annotation.AnnTableName;
import com.sqlite.bean.User;
import com.sqlite.constant.Constant;
import com.sqlite.dao.base.BaseDao;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* Created by admin on 2017/2/6.
*/
public class UserDao extends BaseDao<User> {
@Override
protected String createTable(User user) {
String tb_name = User.class.getAnnotation(AnnTableName.class).value();
Field[] fields = User.class.getDeclaredFields();
Map<String,Field> columsMap = new HashMap<>();
if(fields!=null&&fields.length>0){
for(Field field:fields){
columsMap.put(field.getName(),field);
}
}
Map<String,Field> cacheColumsMap = checkAnnotationValue(columsMap);
return dynamicCreateTable(tb_name,cacheColumsMap);
}
/**
* 创建表
*/
private String dynamicCreateTable(String tb_name,Map<String,Field> cacheColumsMap) {
String table = "";
if(TextUtils.isEmpty(tb_name)||cacheColumsMap==null||cacheColumsMap.isEmpty()){
return table;
}
String fieldName = "";
String type = "";
int len = 0;
StringBuffer sb = new StringBuffer(Constant.create_table_prix);
sb.append(" ");
sb.append(tb_name);
boolean isFirst = false;
int i=0;
for (Map.Entry<String, Field> entry: cacheColumsMap.entrySet()) {
Field filed = entry.getValue();
if(!isFirst){
isFirst = true;
sb.append("(");
}
if(filed.getAnnotation(AnnFiled.class)!=null){
fieldName=filed.getAnnotation(AnnFiled.class).columnName();
type = filed.getAnnotation(AnnFiled.class).type();
len = filed.getAnnotation(AnnFiled.class).len();
sb.append(fieldName).append(" ").append(type).append(len>0?"("+len+")":"").append((i==cacheColumsMap.size()-1)? ")":",");
i++;
}
}
table = sb.toString();
return table;
}
private Map<String,Field> checkAnnotationValue(Map<String, Field> columsMap) {
Map<String,Field> cacheColumsMap = new HashMap<>();
if(columsMap!=null&&!columsMap.isEmpty()){
for (Map.Entry<String, Field> entry: columsMap.entrySet()) {
Field filed = entry.getValue();
if(filed.getAnnotation(AnnFiled.class)!=null){
cacheColumsMap.put(filed.getName(),filed);
}
}
}
return cacheColumsMap;
}
}
网上还有一种解决方案,说是studio2.0后会出现反射多出这二个字段,
但是我们如果要写成框架,肯定不能用这个解决方案,只能用第一种解决方案了!
上一篇讲了一个插入单条记录的操作:
@Override
public Integer update(T entity, T Where) {
int reslut = -1;
Map values=getValues(entity);
Map<String,String> whereClause=getValues(Where);
Condition condition=new Condition(whereClause);
ContentValues contentValues=getContentValues(values);
reslut=dataBase.update(tbName,contentValues,condition.getWhereClause(),condition.getWhereArgs());
return reslut;
}
那么插入多条记录更是简单了,只要遍历下就行:
@Override
public Long insert(List<T> entity) {
Long result = 0l;
if(entity!=null&&!entity.isEmpty()){
for(T t:entity){
result = insert(t);
}
}
return result;
}
这下就把更新数据做好了,现在做删除数据的操作,删除数据可以是删除一条,多条或者把表中的数据都删除,先看下删除单条数据的上层使用:
public void deleteSingleData(){
User user=new User();
user.setName("zhouguizhi1");//这是作为删除条件的值
baseDao.delete(user);
}
这是在数据库中删除name="zhouguizhi1"这条记录,但是delete()方法我们传递进去的是一个对象,而不是一个String name,再看看SQLiteDatabase类中删除的方法:
public int delete(String table, String whereClause, String[] whereArgs)
对应上面的删除操作就是whereClause="name=?" whereArgs=new String[]{"zhouguizhi1"},哪我们就要把user对象利用反射然后封装起来存放在map集合中,这样不管你说单条件删除还是多条件删除都可以做到:然后遍历把这个map集合,如果这个字段上有值就是删除条件了,还记得第一篇讲的时候我们把User对象中的字段和属性进行了获取然后存储在一个map集合中么?哪就再看遍这个代码:
/**
* 数据库字段和实体bean中的注解对应
*/
private void initCacheMap() {
String sql = "select * from "+tbName+" limit 1,0";
Cursor cursor = null;
cursor = dataBase.rawQuery(sql,null);//为了获取表的字段名
if(cursor!=null){
String[] columnNames = cursor.getColumnNames();//获取表中所有的字段名
for(String str:columnNames){
LogUtils.e("str="+str);
}
Field[] fields = clazz.getFields();
if(fields!=null&&fields.length>0){
for(Field field:fields){
if(field!=null){
field.setAccessible(true);//防止bean实体中成员变量用private修饰
}
}
}
if(fields!=null&&fields.length>0){
for(String columnName:columnNames){ //遍历
String filedName = "";
Field tempFidle = null;
for(Field field:fields){
if(field.getAnnotation(AnnFiled.class)!=null){
filedName = field.getAnnotation(AnnFiled.class).columnName();
}else{//实体bean上没注解
filedName = field.getName();
}
if(filedName.equals(columnName)){//如果找到对应的就不再找了
tempFidle = field;
break;//跳出这个循环
}
}
if(tempFidle!=null){
cacheMap.put(columnName,tempFidle);
}
}
cursor.close();
}
}
}
最终的cacheMap集合里的数据是这样的:
cacheMap={name=public java.lang.String com.sqlite.bean.User.name, pwd=public java.lang.String com.sqlite.bean.User.password, province=public java.lang.String com.sqlite.bean.User.province, age=public java.lang.Integer com.sqlite.bean.User.age}
再看下我们的User类:
package com.sqlite.bean;
/**
* Created by admin on 2017/2/6.
*/
import com.sqlite.annotation.AnnFiled;
import com.sqlite.annotation.AnnTableName;
@AnnTableName("user")
public class User {
@AnnFiled(columnName = "age",type ="Integer")
public Integer age;
@AnnFiled(columnName = "name",type ="varchar",len = 20)
public String name;
@AnnFiled(columnName = "pwd",type ="varchar",len = 20)
public String password;
@AnnFiled(columnName = "province",type ="varchar",len = 20)
public String province;
public User(){}
public User(int age, String name, String password) {
this.age = age;
this.name = name;
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
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;
}
public void setAge(Integer age) {
this.age = age;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
}
然后看下我们生成的表结构:
这就是我们的表结构,你会发现表的字段是根据User属性中的注解构建而成的,然后再想想我们删除功能,怎么把一个User对象分解成可以获取whereClause, whereArgs,分二步走,这一步把User对象变成一个map,怎么变呢?其实很简单我们只要把cachemap集合遍历下获取所有的value值,然后判断这个属性上是否有注解,因为你的表名就是根据注解创建的,再判断这个属性是否有值,如果有值的话就是你从外面传递进来删除的条件了,这样代码就分析出来了:
private Map<String, String> getValues(T entity) {
HashMap<String,String> result=new HashMap<>();
Iterator<Field> filedsIterator=cacheMap.values().iterator();
while (filedsIterator.hasNext())
{
Field colmunToFiled=filedsIterator.next();
String cacheKey=null;
String cacheValue=null;
if(colmunToFiled.getAnnotation(AnnFiled.class)!=null)
{
cacheKey=colmunToFiled.getAnnotation(AnnFiled.class).columnName();
}else
{
cacheKey=colmunToFiled.getName();
}
LogUtils.e("cacheKey="+cacheKey);
try {
if(null==colmunToFiled.get(entity))
{
continue;
}
cacheValue=colmunToFiled.get(entity).toString();//获取值
} catch (IllegalAccessException e) {
e.printStackTrace();
}
result.put(cacheKey,cacheValue);
}
return result;
}
这个返回的Map<String,String>就是{name=zhouguizhi1}这样子,第二步就是把这个map变成whereClause=name where=new String[]{"zhougizhi1"}这样了,所以我们要自定义一个类去把这个map分解了,其实也简单我们只要遍历这个map集合获取的key就是其实whereCaluse,value就是whereArgs了,
class Condition
{
private String whereClause;
private String[] whereArgs;
public Condition(Map<String ,String> whereClause) {
ArrayList list=new ArrayList();
StringBuilder sb = new StringBuilder();
sb.append(" 1=1 ");
Set keys=whereClause.keySet();
Iterator iterator=keys.iterator();
while (iterator.hasNext())
{
String key= (String) iterator.next();
String value=whereClause.get(key);
if (value!=null)
{
sb.append(" and "+key+" =?");
list.add(value);
}
}
this.whereClause=sb.toString();
this.whereArgs= (String[]) list.toArray(new String[list.size()]);
}
public String[] getWhereArgs() {
return whereArgs;
}
public String getWhereClause() {
return whereClause;
}
}
上面的sb.append(" 1=1 ");是防止多个条件时,怎么好拼写语句,比如是根据name和age去删除某一条语句,那么whereCaluse就是 name =? and age =?了,为了能像下面这么写:
while (iterator.hasNext())
{
String key= (String) iterator.next();
String value=whereClause.get(key);
if (value!=null)
{
sb.append(" and "+key+" =?");
list.add(value);
}
}
所有必须前面加一个"1=1"常量,否则肯定会报错,这样子我们就把上面的map集合分解可以获取删除条件的二个参数了;
/**
* 删除单条记录(如果数据库中的字段值重复的话会把表中的数据全部删除)
* @param where
* @return
*/
@Override
public Integer delete(T where) {
Map<String,String> map=getValues(where);
Condition condition = new Condition(map);
int result = dataBase.delete(tbName,condition.getWhereClause(),condition.getWhereArgs());
LogUtils.e("删除单条数据的结果="+result);
return result;
}
我现在画个图,把删除的第一步和第二步描述出来,看图也许更容易理解:
这个搞懂了,下面的操作就很好做了,除了查询麻烦下,
删除多条记录:
/**
* 删除多条数据
*/
@Override
public Integer delete(List<T> where) {
int result = 0;
if(where!=null&&!where.isEmpty()){
for(T t:where){
result = delete(t);
}
}
return result;
}
删除表中所有数据:
@Override
public Integer delete() {
int result = dataBase.delete(tbName,null,null);
return result;
}
更新数据的操作:
@Override
public Integer update(T entity, T Where) {
int reslut = -1;
Map values=getValues(entity);
Map<String,String> whereClause=getValues(Where);
Condition condition=new Condition(whereClause);
ContentValues contentValues=getContentValues(values);
reslut=dataBase.update(tbName,contentValues,condition.getWhereClause(),condition.getWhereArgs());
return reslut;
}
最后对查询操作做下,这个会涉及到好几种条件查询:
先看下SQLiteDatabase类下查询的query语句:
query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy, String limit)
条件不是一般的多,是很多,现在对这些参数说明下:
table:表名
colums:列名称数组
selection:查询条件子语句,相当于where
selectionArgs:上面条件子语句是根据那些字段的数组
groupBy:用于分组
having:分组条件
orderBy:排序
limit:分页查询从哪开始查询几条数据
你会发现上面的参数是成对出现的,比如selection是查询条件,那么selectionArgs是你查询条件的值了,groupBy是分组,那么having是你分组的条件,orderBy是排行的意思,在sql一般排序是:
desc:降序从大到小
asc:升序从小到大
limit是分页,app都会用到,除非你项目中没有使用listview或者gridview,哪就不叫项目了,一般查询时colums这个字段是传为null的,比如你表中有age,name,pwd,proive这四个字段,哪你要查age和name字段,那么colums=new String[]{name,age}但是不一般不这么干,这么干在获取值的时候一不小心就会出错,所以colums=null就表示查询所有表中的字段,
selection和selectionArgs这个上面已经封装在Condition类中了,orderBy是二个值中的一个用枚举表示就行,limit就是分页了,关于分组先不管,
package com.sqlite.utils;
/**
* Created by admin on 2017/2/14.
*/
public enum SortUitls {
DESC("desc"), ASC("asc");
private String orderBy;
private SortUitls(String orderBy) {
this.orderBy = orderBy;
}
public String getName() {
return orderBy;
}
}
这是排序封装成了一个枚举,因为查询可能一个条件都不要,又可能带排序的,或者分页
@Override
public List<T> query(T where, String orderBy) {
return query(where,orderBy,null,null);
}
@Override
public List<T> query(T where) {
return query(where,null,null,null);
}
我在IBaseDao接口中定了这二个方法,一个是不带条件查询的,一个是查询后排序,但是这二个方法可能不行啊,因为query那么多参数,就算是分组条件不带,也还有几个,所以还要写一个方法了:
List<T> query(T where,String orderBy,Integer startIndex,Integer limit);
其实前面2个方法最终还行调这个方法的,根据上面分析的现在查询下:
@Override
public List<T> query(T where, String orderBy, Integer startIndex, Integer limit) {
Map map=getValues(where);
String limitString=null;
if(startIndex!=null&&limit!=null&&startIndex>0&&limit>0)
{
limitString=startIndex+" , "+limit;
}
Condition condition=new Condition(map);
Cursor cursor=dataBase.query(tbName,null,condition.getWhereClause(),condition.getWhereArgs(),null,null,orderBy,limitString);
cursor.close();
return null;
}
现在拿到Cursor,也就是说我们查询的结果都封装在Cursor类中了,现在怎么把Cursor类的数据怎么取出来存储在集合中,只差这一步,一步就好,
其实也很实现其实就是把cacheMap集合进行遍历,这个cacheMap集合key是属性中注解名,也就是表名,然后利用cursor对象去查找在那一列,如果能找到的话,然后再判断这个key对应的value是什么类型,根据反射把cursor对应的值赋值给实体对象,整个逻辑代码如下:
private List<T> getResultByCurosr(Cursor cursor, T where) {
ArrayList list=new ArrayList();
if(cursor==null){
return list;
}
Object item;
while (cursor.moveToNext())
{
try {
item=where.getClass().newInstance();
Iterator iterator=cacheMap.entrySet().iterator();
while (iterator.hasNext())
{
Map.Entry entry= (Map.Entry) iterator.next();
//获取表字段
String colomunName= (String) entry.getKey();
Integer colmunIndex=cursor.getColumnIndex(colomunName);
Field field= (Field) entry.getValue();
Class type=field.getType();
if(colmunIndex!=-1)
{
if(type==String.class)
{
field.set(item,cursor.getString(colmunIndex));
}else if(type==Double.class)
{
field.set(item,cursor.getDouble(colmunIndex));
}else if(type==Integer.class)
{
field.set(item,cursor.getInt(colmunIndex));
}else if(type==Long.class)
{
field.set(item,cursor.getLong(colmunIndex));
}else if(type==byte[].class)
{
field.set(item,cursor.getBlob(colmunIndex));
}else if(type==Float.class){
field.set(item,cursor.getFloat(colmunIndex));
}
else {
continue;
}
}
}
list.add(item);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return list;
}
现在不带条件查询所有数据:
public void query(){
User where=new User();
List<User> list=baseDao.query(where);
if(list!=null&&!list.isEmpty()){
for(User user:list){
LogUtils.e("user="+user);
}
}
}
结果:
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=18, name='zhouguizhi0', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=20, name='zhouguizhi2', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=21, name='zhouguizhi3', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=22, name='zhouguizhi4', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=1, name='niubi', password='654321', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=24, name='zhouguizhi6', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=25, name='zhouguizhi7', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=26, name='zhouguizhi8', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=27, name='zhouguizhi9', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=28, name='zhouguizhi10', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=29, name='zhouguizhi11', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=30, name='zhouguizhi12', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=31, name='zhouguizhi13', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=32, name='zhouguizhi14', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=33, name='zhouguizhi15', password='123456', province='浙江省'}
02-14 19:36:29.281 30346-30346/com.sqlite E/LogUtils: user=User{age=34, name='zhouguizhi16', password='123456', province='浙江省'}
02-14 19:36:29.282 30346-30346/com.sqlite E/LogUtils: user=User{age=35, name='zhouguizhi17', password='123456', province='浙江省'}
02-14 19:36:29.282 30346-30346/com.sqlite E/LogUtils: user=User{age=36, name='zhouguizhi18', password='123456', province='浙江省'}
02-14 19:36:29.282 30346-30346/com.sqlite E/LogUtils: user=User{age=37, name='zhouguizhi19', password='123456', province='浙江省'}
比如我现在分页查询,从第十条往后查询9条数据:
private void queryByLimit() {
User where=new User();
List<User> list=baseDao.query(where,"",10,9);
if(list!=null&&!list.isEmpty()){
for(User user:list){
LogUtils.e("user="+user);
}
}
}
结果:
对照上面查询所有的数据,就可以看的出来,这是不是从第11条开始查询9条数据,如果我想来个排序,根据年龄排序,
private void orderBy() {
User where=new User();
List<User> list=baseDao.query(where,"age "+ SortUitls.DESC.getName());
if(list!=null&&!list.isEmpty()){
for(User user:list){
LogUtils.e("user="+user);
}
}
}
结果:
ok,现在还差分组了,这个不常用,现在暂时不做,还有一个模糊查询:
代码下载地址:
http://download.csdn.net/detail/coderinchina/9754484