Android学习笔记之封装数据库工具类

Android学习笔记之封装数据库工具类

在android开发中,难免会对数据库进行操作,如果说每次要自己去写一些具体的数据库操作语句来操作数据库,我个人感觉非常的麻烦,所以今天就写了一个操作数据库的封装类,是需要调用一些基本的增、删、改、查等,而且创建表格,插入数据都不需要我们去写一些操作语句,只需要将你需要插入的表格,写成一个实体类,然后将要插入的数据作为该类的属性,然后保存数据到该实体类中,就能简单的实现我们的数据库基本操作了,多说不意,下面直接看代码吧,所有的解释都在代码中注释了。

IOpenHelper.java(所有要操作的功能,全部写在这个接口中,功能可以任意添加,任意删除,当然了,要有实际用处)

public interface IOpenHelper {
    void save(Object obj); //保存数据
    void saveAll(Collection collection); //保存所有数据
    <T> List<T> queryAll(Class<T> table); //根据类名(表名)查找所有的数据
    <T> List<T> queryAll(Class<T> table, String order); //通过排序的方式查找所有的数据
    <T> List<T> queryAll(Class<T> table, String order, int limit); //通过排序和一夜显示多少条数据来查询所有的数据
    <T> T queryById(Class<T> table, Object id);//通过id查找对应的数据
    void clear(Class table); //清空对应表名中的所有数据
    void delete(Object obj); //删除对应的数据;
    void deleteAll(Collection collection); // 删除集合中所有的数据

}

MyOpenHelper.java(具体实现类)

public class MyOpenHelper extends SQLiteOpenHelper implements IOpenHelper {
    public MyOpenHelper(Context context, String name) {
        super(context, name, null, 1);
    }

    /**
     * 初始化数据库,一般我们都在这里写语句,现在我们自己封装了方法,就不需要在这里写
     *
     * @param db
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    /**
     * 保存对应的对象到数据库,该类的类名就是插入到数据库的表名
     * @param obj 插入到数据库的对象
     */
    @Override
    public void save(Object obj) {
        //获取类类型
        Class<?> table = obj.getClass();
        //创建对应的表
        createTableIfNotExists(table);
        //具体实现保存数据方法
        save(obj, table, getWritableDatabase());
    }

    /**
     *  保存数据的主要操作
     * @param obj 数据库对象
     * @param table 对象类类型
     * @param db 操作数据库
     */
    private void save(Object obj, Class<?> table, SQLiteDatabase db) {
        //将一个对象中的所有字段添加到该数据集中
        ContentValues contentValues = new ContentValues();
        //通过反射获取一个类中的所有属性
        Field[] declaredFields = table.getDeclaredFields();
        //遍历所有的属性
        for (Field field : declaredFields) {
            //获取对应的修饰类型
            int modifiers = field.getModifiers();
            //如果不是静态的就插入到数据库
            if (!Modifier.isStatic(modifiers)) {
                //设置一下数据访问权限为最高级别,也就是public
                field.setAccessible(true);
                try {
                    //将每一个字段的信息保存到数据集中
                    contentValues.put(field.getName(), field.get(obj) + "");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        //对于一般的数据操作,我们采用通常是insert来插入数据,但是为了防止同一个对象的数据进行刷新,所以采用直接替换掉
        db.replace(table.getName().replaceAll("\\.", "_"), null, contentValues);
    }

    /**
     * 这里是保存统一对象的多个数据,通过获取集合中的对象,来保存所有的数据
     * @param collection
     */
    @Override
    public void saveAll(Collection collection) {
        //如果集合为空直接不需要操作
        if (collection.isEmpty()) {
            return;
        }
        SQLiteDatabase db = getWritableDatabase();
        //获取该集合的一个对象即可,因为一个集合中保存的都是同一个对象
        Object next = collection.iterator().next();
        //然后创建该对象所对应的表
        createTableIfNotExists(next.getClass());
        //这里为了提高效率采用了事务处理方式,对于事务这里不做过多的讲解
        db.beginTransaction();
        for (Object o : collection) {
            save(o);
        }
        //设置事务为成功状态
        db.setTransactionSuccessful();
        //当事务结束,才会一次性执行上面for中的所有save方法,如果该事务没有结束,则for中的save方法一个都不会执行
        db.endTransaction();
    }

    /**
     * 通过表名,查询所有的数据,表名对应于类名
     * @param table 类类型
     * @param <T> 泛型参数,任意类型
     * @return
     */
    @Override
    public <T> List<T> queryAll(Class<T> table) {
        //如果该表不存在数据库中,则不需要进行操作
        if (!isTableExists(table)) {
            return null;
        }

        SQLiteDatabase db = getReadableDatabase();
        //获取表名,因为表名是采用完全包名的形式存储,按照表名规则,不允许有 "." 的存在,所以采用"_"进行替换
        String tableName = table.getName().replaceAll("\\.", "_");
        //通过表名查询所有的数据
        Cursor cursor = db.query(tableName, null, null, null, null, null, null);
        //通过initList拿到对应的数据
        List<T> result = initList(table, cursor);
        //关闭游标
        cursor.close();
        //返回结果
        return result;
    }

    /**
     * 通过指定的顺序返回所有查询的结果
     * @param table 类类型
     * @param orderBy 指定顺序
     * @param <T> 泛型参数
     * @return
     */
    @Override
    public <T> List<T> queryAll(Class<T> table, String orderBy) {
        //这里所有的操作和上面类似,就不依依介绍了
        if (!isTableExists(table)) {
            return null;
        }
        SQLiteDatabase db = getReadableDatabase();
        Cursor cursor = db.query(table.getName().replaceAll("\\.", "_"), null, null, null, null, null, orderBy);
        List<T> result = initList(table, cursor);
        return result;
    }

    /**
     * 通过指定的顺序和查询多少页来查询所有的数据
     * @param table 类类型
     * @param orderBy 指定顺序
     * @param limit 指定的页数
     * @param <T>
     * @return
     */
    @Override
    public <T> List<T> queryAll(Class<T> table, String orderBy, int limit) {
        //如上雷同,不做介绍
        if (!isTableExists(table)) {
            return null;
        }
        SQLiteDatabase db = getReadableDatabase();
        Cursor cursor = db.query(table.getName().replaceAll("\\.", "_"), null, null, null, null, null, orderBy, String.valueOf(limit));
        List<T> result = initList(table, cursor);
        return result;
    }

    /**
     * 通过id来查询对应的数据
     * @param table
     * @param id
     * @param <T>
     * @return
     */
    @Override
    public <T> T queryById(Class<T> table, Object id) {
        Field idField = null;
        //获取属性id
        idField = getFieldId(table);
        SQLiteDatabase db = getReadableDatabase();
        Cursor cursor = db.query(table.getName().replaceAll("\\.", "_"),
                null,
                (idField == null ? "_id" : idField.getName()) + " = ?", //判断,如果对应的类中存在id,则通过该类中的id查找数据,如果不存在id就采用使用默认的_id来查询数据
                new String[]{String.valueOf(id)}, null, null, null);
        List<T> list = initList(table, cursor);

        if (list.isEmpty()) {
            return null;
        } else {
            return list.get(0);//这里是通过id查询数据,因为id唯一,所以查到的数据最多也就一个,直接返回list.get(0)
        }
    }

    /**
     * 通过表名清空所有的数据
     * @param table 类类型
     */
    @Override
    public void clear(Class table) {
        SQLiteDatabase db = getWritableDatabase();
        db.delete(table.getName().replaceAll("\\.","_"), null, null);
    }

    /**
     * 删除数据
     * @param obj 指定对象(表)中的数据
     */
    @Override
    public void delete(Object obj) {
        SQLiteDatabase db = getWritableDatabase();
        delete(obj, db);
    }

    /**
     * 主要删除操作,主要是通过id来删除,因为删除一条操作必须有一个唯一列项
     * @param obj 指定对象(表)中的数据
     * @param db
     */
    private void delete(Object obj, SQLiteDatabase db){
        //首先获取该类中的id,如果有就会获取到
        Field idField = getFieldId(obj.getClass());
        //如果不存在属性id,就不需要删除
        if (idField != null) {
            idField.setAccessible(true);
            try {
                db.delete(obj.getClass().getName().replaceAll("\\.", "_"),
                        idField.getName() + " = ?",
                        new String[]{idField.get(obj).toString()});
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 删除集合中所有的对象数据
     * @param collection
     */
    @Override
    public void deleteAll(Collection collection) {
        SQLiteDatabase db = getWritableDatabase();
        db.beginTransaction();
        for (Object o : collection) {
            delete(o, db);
        }
        db.setTransactionSuccessful();
        db.endTransaction();
    }

    /**
     * 这个方法的主要功能是将数据中查询到的数据放到集合中。
     * 类似于我们查询到对应的数据重新封装到一个对象中,然后把这个对象
     * 放入集合中。这样就能拿到我们的数据集了
     * @param table
     * @param cursor
     * @param <T>
     * @return
     */
    private <T> List<T> initList(Class<T> table, Cursor cursor) {
        List<T> result = new ArrayList<>();
        //这里可能大家不了解,这是Gson为我们提供的一个通过JDK内部API 来创建对象实例,这里不做过多讲解
        UnsafeAllocator allocator = UnsafeAllocator.create();
        while (cursor.moveToNext()) {
            try {
                //创建具体的实例
                T t = allocator.newInstance(table);
                boolean flag = true;
                //遍历所有的游标数据
                for (int i = 0; i < cursor.getColumnCount(); i++) {

                    //每次都去查找该类中有没有自带的id,如果没有,就不应该执行下面的语句
                    //因为下面获取属性名时,有一个异常抛出,要是找不到属性就会结束这个for循环
                    //后面的所有数据就拿不到了,只要检测到没有id,就不需要再检测了。
                    if(flag){
                        Field fieldId = getFieldId(table);
                        if(fieldId == null){
                            flag = !flag;
                            continue;
                        }
                    }
                    //通过列名获取对象中对应的属性名
                    Field field = table.getDeclaredField(cursor.getColumnName(i));
                    //获取属性的类型
                    Class<?> type = field.getType();
                    //设置属性的访问权限为最高权限,因为要设置对应的数据
                    field.setAccessible(true);
                    //获取到数据库中的值,由于sqlite是采用若语法,都可以使用getString来获取
                    String value = cursor.getString(i);
                    //通过判断类型,保存到指定类型的属性中,这里判断了我们常用的数据类型。
                    if (type.equals(Byte.class) || type.equals(Byte.TYPE)) {
                        field.set(t, Byte.parseByte(value));
                    } else if (type.equals(Short.class) || type.equals(Short.TYPE)) {
                        field.set(t, Short.parseShort(value));
                    } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
                        field.set(t, Integer.parseInt(value));
                    } else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
                        field.set(t, Long.parseLong(value));
                    } else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
                        field.set(t, Float.parseFloat(value));
                    } else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
                        field.set(t, Double.parseDouble(value));
                    } else if (type.equals(Character.class) || type.equals(Character.TYPE)) {
                        field.set(t, value.charAt(0));
                    } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
                        field.set(t, Boolean.parseBoolean(value));
                    } else if (type.equals(String.class)) {
                        field.set(t, value);
                    }
                }
                result.add(t);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * 判断表格是否存在
     * @param table
     * @return
     */
    private boolean isTableExists(Class table) {
        SQLiteDatabase db = getReadableDatabase();
        //查询表是否存在
        Cursor cursor = db.query("sqlite_master", null, "type = 'table' and name = ?", new String[]{table.getName().replaceAll("\\.", "_")}, null, null, null);
        boolean isExists = cursor.getCount() > 0;
        cursor.close();
        return isExists;
    }

    /**
     * 如果表格不存在就创建该表。如果存在就不创建
     * @param table
     */
    private void createTableIfNotExists(Class table) {
        if (!isTableExists(table)) {
            SQLiteDatabase db = getWritableDatabase();
            StringBuilder builder = new StringBuilder();
            builder.append("CREATE TABLE IF NOT EXISTS ");
            builder.append(table.getName().replaceAll("\\.", "_"));
            builder.append(" (");
            Field id = getFieldId(table);
            if (id == null) {
                builder.append("_id Integer PRIMARY KEY AUTOINCREMENT,");
            } else {
                builder.append(id.getName()).append("  PRIMARY KEY, ");
            }
            for (Field field : table.getDeclaredFields()) {
                int modifiers = field.getModifiers();
                if (!field.equals(id) && !Modifier.isStatic(modifiers)) {
                    builder.append(field.getName()).append(",");
                }
            }
            builder.deleteCharAt(builder.length() - 1);
            builder.append(")");
            db.execSQL(builder.toString());
        }
    }

    /**
     * 获取对象属性中的id字段,如果有就获取,没有就不获取
     * @param table
     * @return
     */
    private Field getFieldId(Class table) {
        Field fieldId = null;
        try {
            fieldId = table.getDeclaredField("id");
            if (fieldId == null) {
                table.getDeclaredField("_id");
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        return fieldId;
    }
}

DatabaseUtils.java(最终,我们只需要通过这个类来操作数据库即可)

public class DatabaseUtils {
    private static  MyOpenHelper mHelper;

    private DatabaseUtils(){
    }

    /**
    * 一般来说这里的initHelper放到application中去初始化
    * 当然也可以在项目运行阶段初始化
    */
    public static void initHelper(Context context,String name){ 
        if(mHelper == null){
            mHelper = new MyOpenHelper(context,name);
        }
    }
    public static MyOpenHelper getHelper(){
        if(mHelper == null){
            new RuntimeException("MyOpenHelper is null,No init it");
        }
        return mHelper;
    }
}

下面来通过几个列子来测试下:

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //必须先初始化
        DatabaseUtils.initHelper(this,"user.db");

        //创建学生类
        Student student1 = new Student("张三","1001",12);

        //将学生类保存到数据库
        DatabaseUtils.getHelper().save(student1);

    }
}

Student.java

public class Student {
    //姓名
    private String name;

    //学号
    private String nubmer;

    //年龄
    private int age;

    public Student() {
    }

    public Student(String name, String nubmer, int age) {
        this.name = name;
        this.nubmer = nubmer;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getNubmer() {
        return nubmer;
    }

    public void setNubmer(String nubmer) {
        this.nubmer = nubmer;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

然后,我们看看结果吧:

这里写图片描述

通过adb shell命令我们可以看到,我们对应的数据已经插入到数据库了,是不是很简单,我们只需要调用DatabaseUtils.getHelper().save(student1);就可以保存对应的数据到数据库了,下面我们测试一下插入多条操作。

添加如下语句

 List<Student> list = new ArrayList<>();
 list.add(new Student("李四","1002",13));
 list.add(new Student("王五","1003",23));
 list.add(new Student("赵六","1004",21));
 list.add(new Student("钱七","1005",20));
 DatabaseUtils.getHelper().saveAll(list);

结果如下:

这里写图片描述

这样多条数据也就插入了,非常方便吧。由于Student类中本身没有自带id,所以会使用系统默认的_id来进行自动增长,才会导致出现两条相同的张三数据,下面再来测试一下查询操作:

List<Student> list = DatabaseUtils.getHelper().queryAll(Student.class);
Log.d("TAG", "onCreate: " + list.size());
 for (Student student : list) {
     Log.d("TAG", "onCreate: " + student);
 }

打印结果如下:

09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: 6
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='张三', nubmer='1001', age=12}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='张三', nubmer='1001', age=12}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='李四', nubmer='1002', age=13}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='王五', nubmer='1003', age=23}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='赵六', nubmer='1004', age=21}
09-29 16:53:11.852 1120-1120/cn.itrealman.databaseutils D/TAG: onCreate: Student{name='钱七', nubmer='1005', age=20}

然后其他的操作就不做测试了,每天搞到这么晚,写个一篇博客也不容易,希望读过的朋友在下面点个赞吧。

源码下载

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值