android 手把手教你写数据库框架(第二篇)

接着上一篇讲,今天把数据库的增删改查操作写完,在讲这前把第一篇出现的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
































  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值