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

《Android 移动应用基础教程(Android Studio)(第2版)》【课本客观题】+【学习通2023春】【参考答案】 阅读详情

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
































Android Studio-手把手教你基于SQLLITE实现登录注册功能 继启动界面之后,现在开始做登录界面和注册界面。需要看启动界面教学的,请戳我的博客: Android Studio之启动界面教学 Android Studio之启动界面教学. 其实安卓的登录注册,和JAVA是一样的,甚至于是极简版的JAVA登录注册,安卓太智能了,数据保存在sqllite中,完全不需要导包,而且还伴有可视化界面,简直无敌,安卓可以说是目前我们学过的最简单的语言,只是大家没有好好学罢了... 阅读详情

相关推荐

安卓微信本地数据库解密

安卓微信数据库解密

强强强子的博客 1万+

五一加班也要看的Android数据库开发基础入门【附完整案例】

目录 一、初识Android数据库 二、表中数据的添加操作 三、数据表中数据的删除 1、删除数据表中的全部数据 2、单条数据的删除 四、数据表中单条数据指定值的修改 五、数据表中数据的查找 1、查找全部数据 2、查询单条数据 六、完整项目案例源码 Hello,你好呀,我是灰小猿!一个超会写bug的程序猿! 最近和大家分享了很多关于Python开发的相关文章,今天就换个口味,在这里和大家记录一下在Android开发中连接数据库并进行基本操作的相关内容。 Android中的数据库使用和

累计发表博客60余万字,优快云 博客专家、Java 领域优质创作者、华为云享专家、阿里云专家博主。专注分享 Java 优质文章,毕设实战项目开发,全网粉丝10W+ 4106

安卓Room框架初使用

安卓Room框架初使用Room框架介绍Database(数据库)Entity:表示数据库中的表。表的复杂写法:DAO:包含用于访问数据库的方法。具体使用(附Demo)添加依赖数据库的建立数据库的升级表结构的修改Entity代码Dao代码表的增删改查操作执行效果总结 Room框架介绍 Room在·SQLite上提供了抽象层,以便于更加流畅的访问数据库,对数据库进行操作。主要由Database,Entity,Dao这三部分组成: Database(数据库) 包含数据库持有者,并作为应用已保留的持久关系型数据的底

qq_42462966的博客 2208

Android数据存储之SQLite数据库存储,数据库的CRUD操作(增、查、改、删)(创建BookStore.db数据库,创建Book表,并了解adb shell来查看数据库和表的创建情况)

GitHub项目地址: https://github.com/Skymqq/DatabaseSave.git SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源也很少,通常只需要几百KB的内存就足够了,因而特别适合在移动设备上使用。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务,所以只要你以前使用过其他的关系型数据库,就可以很快的上手SQLite。而SQ...

Android_小牧的博客 2905

Android数据库:SQLite除了.db文件,还多出.db-shm,.db-wal文件

Android数据库:SQLite除了.db文件,还多出.db-shm,.db-wal文件 使用安卓数据库保存文件时,在Android Studio 的 Device File Explorer里数据库文件目录 /data/data/com.urovo.datatopc/databases/DATA.db 发现其目录下不仅有.db文件,还有两个其他文件 .db-shm和.db-wal文件 而且数据库存储文件DATA.db(这是我的数据库名字,你们的可能不一样)保存至本地,通过SQLite Develope

m0_53581523的博客 4940

Android Studio 使用插件Database Navigation 连接 sqlite数据库

Database Navigation 是一款在 Android Studio 开发环境中非常实用的插件,它主要致力于帮助开发者更便捷、高效地管理和操作数据库,尤其是在 Android 项目涉及到数据库相关开发工作时,能够提升开发体验与效率。

梁辰兴的博客 2556

Android开发者必须收藏的8个开源库,Android最牛教材

面经分享 我是那边的朋友帮忙内推的,简历通过后电话约的面试。 当时约的是 10:30 的面试时间,我提前了二十机分钟左右到达公司楼下。公司楼层有点复杂,花了十分钟左右到目的地(约的食堂)。 第一轮是两个面试官轮流问问题。第一面大约面了一个小时到了 11:40差不多到饭点了。面完我就准备走了,两个面试官要我等会,然后就直接去叫二面的面试官了,我就在食堂看大家吃饭。 大约等了十几分钟第二面的面试官把我叫到了 31 楼。 也是两个面试官,轮流问了大概一个小时。二面后就差不多到了下午一点,面试官带我去食堂吃了个饭,

293

关于unity安卓平台对数据库文件的读取

这是针对关于安卓平台对项目数据库文件的访问 刚来公司实习不久,开始了一个小项目demo,本来是用csv文件读取一些数据,然而老板开始要求用数据库来读取,因此我就简单的直接读取本机的数据库,在PC上运行没有问题。老板开始要求在安卓上运行,此时问题就出现了,安卓上是无法通过ip去访问数据库。其实可以通过访问网站来获取数据库的数据,但是对于我这种刚毕业的新手来说是无法实现的,只能妥协通过访问数据库导出来...

日尧的博客 2352

Android 数据库(SQLite)【简介、创建、使用(增删改查、事务、实战演练)、数据显示控件(ListView、Adapter、实战演练-绿豆通讯录)】

(壹)SQLite数据库简介 (贰)数据库的创建 (叁)数据库的使用 3.1、SQlite的基本操作 3.1.1、添加数据 3.1.2、修改数据 3.1.3、查询数据 3.1.4、删除数据 3.2、SQLite中的事务 3.3、实战演练——绿豆通讯录 前情提要(绿豆通讯录+ListView) 实现步骤 and 技术要点 activity_main.xml MyHelper.java MainActivity.java 运行截图 (肆)数据显示控件 ListView控件 常用数据适配器(Adapter)

upward 8302

Android项目实战 —— 手把手教你实现一款本地音乐播放器Dora Music

今天带大家实现一款基于Dora SDK的Android本地音乐播放器app,本项目也作为Dora SDK的实践项目或使用教程。使用到开源库有[https://github.com/dora4/dora] 、[https://github.com/dora4/dcache-android] 等。先声明一点,本项目主要作为框架的使用教程,界面风格不喜勿喷。我们要开发一款Android App,首先要搭建基础框架,比如使用MVP还是MVVM架构?使用什么网络库?使用什么ORM库?很显然,作为Dora SDK的使用

跟着哆啦学Android 2517

安卓db数据库查看工具——快速管理与分析SQLite数据库

SQLite 是 Android 内置的轻量级关系型数据库管理系统,为 Android 应用提供了一种高效、便捷的数据持久化方案。与传统的关系型数据库不同,SQLite 将数据库存储在文件系统中的单一文件内,从而简化了数据库的部署和管理。SQLite 支持标准的 SQL 语言,允许在没有服务器进程的情况下进行数据库操作,这使得它非常适合于资源有限的移动设备环境。在 Android 应用开发中,SQLite 数据库常用于本地数据存储、用户个性化设置记录以及应用内缓存。

weixin_32869687的博客 1941

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

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

zhouguizhi 3567

LLM - windows下的Dify离线部署:从镜像打包到无网环境部署(亲测,包含插件部署)

LLM - windows下的Dify离线部署:从镜像打包到无网环境部署(亲测,包含插件部署)

ocean00008899的博客 1285

查看安卓sqlite数据库工具:Android-Debug-Database

简要介绍 安卓开发过程中,有时需要查看sqlite数据库,核对数据.本文介绍Android-Debug-Database,非常好用. github地址:https://github.com/amitshekhariitbhu/Android-Debug-Database 功能: 支持查看手机该app的数据库和shared preference. 支持修改数据库和shared preferenc...

蓝不蓝编程 1521

手把手教你用STM32F030实现功率MOS双脉冲测试(附完整电路图)

本文详细介绍了如何使用STM32F030微控制器搭建功率MOSFET双脉冲测试平台。通过分析双脉冲测试原理,提供了完整的硬件电路设计、驱动电路选型、软件时序生成方法,并指导如何利用示波器捕获和分析开关波形,从而评估功率器件的动态特性与开关损耗。

juice的博客 711

立创EDA开源足轮机器人:基于STM32F407与ESP32的LQR自平衡与FOC驱动设计全解析

本文详细解析了基于立创EDA平台的开源足轮机器人项目。该项目采用STM32F407作为主控,ESP32负责无线通信,并应用LQR自平衡算法与FOC驱动技术,实现了机器人的稳定站立、移动转向及复杂地形适应能力。文章深入剖析了其硬件架构、软件设计及调试经验,为机器人运动控制学习提供了宝贵参考。

weixin_42608299的博客 232

Android访问外部DB文件

注:本文代码参考了网上的例子,忘了什么地址了,所以无法提供说明,原作者看到勿怪,可以提醒我加上。   有时候自己导入DB文件进行操作,和访问本地数据库有点差异。 首先在res下建立一个文件夹raw,然后把DB文件导入进去 然后写一个方法获取SQLiteDatabase private static SQLiteDatabase database; public static final

氵白的专栏 1万+

Open3D 教程学习指南 (持续整理)

Open3D 是一个开源库,旨在为 3D 数据处理提供高效且易用的工具。它由 Intel 开发和维护,支持多种 3D 数据处理任务,如点云处理、3D 重建、几何处理和可视化等。

Chase Blog 5925

安卓 mysql 框架_自己动手写Android数据库框架

自己动手写Android数据库框架相信不少开发者跟我一样,每次都很烦恼自己写数据库,而且那些数据库语句也经常记不住。当然网上也有很多很好的数据库框架,你可以直接拿来用,但是 很多时候我们的项目,特别是一个小型的Andrond应用原本用到的数据库结构比较简单,没必要去用那些有点臃肿的框架。当然,即使你用那些框架,当你遇到问题时,你是否也得去修改它?你要修改别人的框架必须的读懂他人的设计代码。所以不管...

weixin_39928102的博客 235

【GD32F407】i2c主机发送,从机中断接收

前言 现在手里负责的一个项目需要用i2c和主芯片进行通信主动发送,中断接收,这就需要支持i2c主机模式和从机模式,先查看原理图 这里我们用到了两条i2c总线进行数据的交替传输,避免在使用一条总线是发送数据和接收数据冲突 初始化i2c控制器 s32_k i2cDevInit(u8 byBusId, TI2cBus *pTI2cBus) { //PCLK = 36000000 BOOL retFlg = FALSE; int I2xSCL = pTI2cBus->byFastMode ?

Enosji的博客 6244

AI Agent 入门(一):什么是 AI Agent?——从概念到第一个 API 调用

AI Agent 入门(一):什么是 AI Agent?——从概念到第一个 API 调用

nbtang的博客 257

Android Studio | 使用Database Inspector 查看数据库 .db 文件,太方便了!

在涉及到数据库开发时,通常需要查看 SQLite 数据库文件(.db),以调试数据存储是否正确。然而,由于Android 设备的 数据隔离机制,普通应用无法直接访问目录下的数据库文件。除了将数据库文件导出然后利用三方工具操作外,Android Studio 本身也提供了 Database Inspector 工具,可以直接在 IDE 中可视化地查看和修改数据库内容,无需导出文件,非常方便!

小马快跑的博客 2143

关于 ABAP OPEN SQL 的动态 WHERE 条件和 Dynamic Selections

ABAP动态查询技术解析 本文介绍了ABAP中实现灵活数据库查询的动态选择技术,通过FREE_SELECTIONS函数组实现用户自定义查询条件。核心内容包括: 传统固定WHERE条件的局限性,无法满足业务多变需求 Dynamic Selections机制的工作原理,使用FREE_SELECTIONS_INIT初始化、FREE_SELECTIONS_DIALOG交互 完整Demo程序演示如何构建通用表数据浏览器: 用户输入表名后校验权限 调用动态选择函数生成条件 动态拼接WHERE子句执行查询 使用CL_SA

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 672

Android之 常用数据库框架整理

4.2 ORM 框架,即 Object-Relational Mapping,它的作用是在关系型数据库和对象之间作一个映射,这样,我们在具体操作数据库的时候,就不需要再去和复杂的 SQL 语句打交道,只是像平时操作对象一样操作它就可以了。在数据库方面,它是主键。DaoSession还提供了一些通用的持久性方法,如实体的插入,加载,更新,刷新和删除。3.2 SQLiteOpenHelper的使用,自定义DatabaseHelper,继承SQLiteOpenHelper,来管理数据库的创建和数据库表的创建。

hdulhb的博客 6017

笔试题-2023-紫光展锐-数字芯片设计【纯净题目版】

笔试题-2023-紫光展锐-数字芯片设计【纯净题目版】

w0shishabi的博客 2923

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

Android 数据库加密 android-database-sqlcipher 开源版本编译过程

build android-database-sqlcipher 介绍 android-database-sqlcipher 用于 Android SQLite 数据库的加密。 维护者提供了 收费 和 开源 两个版本。 sqlcipher开发维护者官网:https://www.zetetic.net/sqlcipher/ github开源项目地址:android-database-sqlciph...

103style 6618

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

上一篇: java中并发常用工具类
下一篇: java 线程池详解
zhoushenxian
博客等级 码龄12年 453粉丝 549原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值