Android对SQLite数据库进行封装使用反射来进行表的增删改查

一、介绍
1.首先该操作类由三部分组成:DBOperHelper、AppDao、DBProvider

DBOperHelper:用来管理表的创建和版本升级 

AppDao:数据库的操作类,该方法进行数据库的增、删、改、查

DBProvider:数据库内容变化监听器

2、使用
比如说我们现在需要建个Student表
(1).创建Student的bean对象

package lc.com.car.bean;

import java.io.Serializable;

/**
 * @author lingfeng 2017/12/22 11:39
 */

public class StudentBean implements Serializable {
    private String name;
    private String id;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

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

(2).在DBOperHelper下的onCreate()中创建Student 表

    @Override
    public void onCreate(SQLiteDatabase db) {

        // Student表
        db.execSQL("CREATE TABLE "
                +"StudentBeanTable "
                +"(_ID INTEGER primary key autoincrement,name varchar(40),id varchar(40),age varchar(10))");

    }

注意:这里表名必须遵守一定规范 例如:类名为StudentBean时 StudentBean在数据库中的表名必须是 StudentBeanTable(包括大小写)

(3)调用


/**
    插入数据 
*/

//StudentBean表下插入两条数据
List<StudentBean> list = new ArrayList<>();
list.add(new StudentBean("张三","123","18"));
list.add(new StudentBean("李四","456","19"));
AppDao.getInstance().insertAll(list);

/**

    查询

*/
//查询StudentBean表下的所有数据
 List<StudentBean> list = AppDao.getInstance().findAll(StudentBean.class);
 //查询StudentBean表下年龄为18的所有数据
 List<StudentBean> list = AppDao.getInstance().findAll(StudentBean.class,"age=?",new String);

/**
    修改数据
*/

//将StudentBean表下的age = 18改为20
ContentValues value = new ContentValues();
value.put("age", "20");
AppDao.getInstance().update(StudentBean.class,"age=?",new String[]{"20"},value);

/**
    删除
*/
//删除StudentBean表下的age = 19的的数据
AppDao.getInstance().delete(StudentBean.class,"age=?",new String[]{"19"});

//删除StudentBean表下的所有数据
AppDao.getInstance().deleteAll(StudentBean.class);

三、重点来啦啊啊

AppDao:

package lc.com.car.base.db;

import android.content.ContentValues;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import lc.com.car.MyApplication;
import lc.com.car.utils.LogData;


/**
 * 使用反射来进行表的增删改查操作
 * <p>
 * <p>
 * 使用方法:
 * <p>
 * 1.在创建表时:表名需要遵守一定规范 例如:类名为User时 User在数据库中的表名必须是 UserTable(包括大小写)
 * ,这样便于查找到相对应的表进行操作 2.在创建表时:字段名需要遵守一定规范
 * 例如:在类中字段名为userName,那么这个字段的get方法名应该是getUserName(),那么表中的字段名,必须是userName,(包括大小写)
 *
 * @author lingfeng
 */
public final class AppDao {
    private DBOperHelper operHelper;

    /**
     * 在类名后追加的字符串,以此就知道这个类相对应的表名
     */
    private final static String endTableName = "Table";

    private AppDao() {

        // 在这里初始化数据库版本,其版本号与应用的versionCode一致,这样在升级的时候就不用考虑这些细节问题
        int version = 1;
        try {
            version = MyApplication.getInstance().getPackageManager().getPackageInfo(MyApplication.getInstance().getPackageName(), 1).versionCode;
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }

        operHelper = new DBOperHelper(MyApplication.getInstance(), version);
    }

    /**
     * 确保只有一个DBOperHelper的实例,
     * 从DBOperHelper获取到的readableDatabase或writableDatabase是同步的, 所以就不需要考虑线程安全问题
     *
     * @param
     * @return
     */
    public synchronized static AppDao getInstance() {
        return new AppDao();
    }

    /**
     * 查询出某个表中所有的数据
     *
     * @param claz
     */
    public <T> List<T> findAll(Class<T> claz) {
        return find(claz, null, null, null, null, null, null);
    }

    /**
     * 有查询条件的查询某个表
     *
     * @param claz
     * @return
     */
    public <T> List<T> findAll(Class<T> claz, String selection, String[] selectionArgs) {
        return find(claz, selection, selectionArgs, null, null, null, null);
    }

    /**
     * 多条件查询
     * columns:要查询的列名,可以是多个,可以为null,表示查询所有列
     * @param claz     需要返回的数据类型字节码对象,通过字节码对象才能通过反射进行下一步操作,不可为null
     * @param selection   查询条件,比如id=? and name=? 可以为null
     * @param selectionArgs  对查询条件赋值,一个问号对应一个值,按顺序 可以为null
     * @param groupBy       与合计函数一起使用
     * @param having        语法have,与合计函数一起使用,可以为null
     * @param orderBy           语法,按xx排序,可以为null
     * @param limit         返回rows数量
     */
    public <T> List<T> find(Class<T> claz, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit) {

        List<T> list = null;

        // 获取到类名
        String className = claz.getName();

        SQLiteDatabase readableDatabase = operHelper.getReadableDatabase();

        // 表名必须为是类名+Table
        String tableName = className.substring(className.lastIndexOf(".") + 1) + endTableName;

        // 查询数据
        Cursor query = readableDatabase.query(tableName, null, selection, selectionArgs, groupBy, having, orderBy, limit);

        if (query != null) {
            list = new ArrayList<T>();

            try {
                // 得到字节码对象
                @SuppressWarnings("unchecked")
                Class<T> clz = (Class<T>) Class.forName(className);
                // 根据字节码对象获取到所有set开头的公共方法集合
                List<Method> methodList = parseSetMethodList(clz, "set");

                // 如果得到的set方法集合大于1
                if (methodList != null && methodList.size() > 0) {
                    query.moveToFirst();
                    for (int i = 0; i < query.getCount(); i++) {
                        // 将获取到得数据组装为list集合
                        list.add(initObject(clz, query, methodList));
                        query.moveToNext();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (query != null) {
                    query.close();
                }
                if (readableDatabase != null) {
                    readableDatabase.close();
                }
            }
        } else {
            if (readableDatabase != null) {
                readableDatabase.close();
            }
        }
        Log.e("appDao", tableName + "表获取到数据" + list == null ? "0" : list.size() + "条");
        return list;
    }

    /**
     * 根据从数据库获取到得数据封装对象并返回
     *
     * @param clz        字节码对象
     * @param query      数据库游标
     * @param methodList set开头的方法名称集合
     * @return
     */
    private <T> T initObject(Class<T> clz, Cursor query, List<Method> methodList) {
        T obj = null;

        try {
            obj = clz.newInstance();
            // 循环插入clz实例的每一个字段值
            for (Method method : methodList) {
                // 根据方法名获取到参数的名字
                String fieldName = method.getName().substring(4);
                // 截取第三个字符转换为小写,并拼接成字段
                fieldName = (method.getName().charAt(3) + "").toLowerCase(Locale.getDefault()) + fieldName;
                // 得到方法的参数类型
                Class<?>[] parameterTypes = method.getParameterTypes();

                // 如果参数类型数组不为null且大于0时才进行插入数据
                if (parameterTypes != null && !(parameterTypes.length < 1)) {

                    int columnIndex = query.getColumnIndex(fieldName);
                    if (parameterTypes[0] == String.class) {
                        method.invoke(obj, query.getString(columnIndex));
                    } else if (parameterTypes[0] == Integer.class) {
                        method.invoke(obj, query.getInt(columnIndex));
                    } else if (parameterTypes[0] == Long.class) {
                        method.invoke(obj, query.getLong(columnIndex));
                    } else if (parameterTypes[0] == Float.class) {
                        method.invoke(obj, query.getFloat(columnIndex));
                    } else if (parameterTypes[0] == Boolean.class) {
                        method.invoke(obj, query.getBlob(columnIndex));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return obj;
    }

    /**
     * 插入数据
     *
     * @param list
     * @return insert 返回插入操作所影响的行数
     */
    public <T> Long insertAll(List<T> list) {
        Long insert = 0L;

        if (list == null || list.size() < 1) {
            return 0L;
        }

        String className = list.get(0).getClass().getName();
        String tableName = className.substring(className.lastIndexOf(".") + 1) + endTableName;
        SQLiteDatabase writableDatabase = operHelper.getWritableDatabase();
        Class<?> c = null;
        try {
            // 根据类名来获取字节码对象
            c = Class.forName(className);

            if (c != null) {
                // 查找出所有以get为开头的方法
                List<Method> methodList = parseSetMethodList(c, "get");

                // 根据查找出来的get方法集合来执行插入数据的动作
                for (T obj : list) {
                    // 返回插入数据的id
                    long number = insert(tableName, obj, methodList, writableDatabase);

                    // 说明插入一条数据成功
                    if (number != -1) {
                        insert++;
                    }
                }
            } else {
                Log.e(this.getClass().getName(), "=============== not found className class =================");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writableDatabase != null) {
                writableDatabase.close();
            }
        }
        LogData.i("appDao", tableName + "表插入数据" + insert + "条");

        // 通知内容变化
        if (insert > 0) {
            DBProvider.getInstance().onChange(tableName);
        }

        return insert;
    }

    /**
     * 执行具体的插入数据的动作
     *
     * @param tableName  表名
     * @param obj        数据源
     * @param methodList 数据源中的对象类型中以get为开头的方法的集合
     * @return
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     * @throws InvocationTargetException
     */
    private <T> Long insert(String tableName, T obj, List<Method> methodList, SQLiteDatabase writableDatabase) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Long insert = 0L;

        ContentValues values = new ContentValues();

        for (Method method : methodList) {
            String name = method.getName();

            // 封装好ContentValues 数据
            if (name.startsWith("get") && !name.startsWith("getClass")) {

                Object methodValue = method.invoke(obj, new Object[]{});
                String filedName = method.getName().substring(4);

                filedName = (method.getName().charAt(3) + "").toLowerCase(Locale.getDefault()) + filedName;

                values.put(filedName, methodValue + "");
            }
        }
        // 插入数据
        insert = writableDatabase.insert(tableName, null, values);
        return insert;
    }

    /**
     * 删除一条数据
     *
     * @param clz       字节码对象
     * @param select
     * @param selectArg
     * @return 操作所影响的行数
     */
    public int delete(Class<?> clz, String select, String[] selectArg) {
        int delete = 0;

        SQLiteDatabase writableDatabase = operHelper.getWritableDatabase();
        String tableName = "";
        try {
            tableName = clz.getName().substring(clz.getName().lastIndexOf(".") + 1) + endTableName;

            delete += writableDatabase.delete(tableName, select, selectArg);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writableDatabase != null) {
                writableDatabase.close();
            }
        }
        LogData.i("AppDao", tableName + "表删除" + delete + "条数据");

        // 通知内容变化
        if (delete > 0) {
            DBProvider.getInstance().onChange(tableName);
        }

        return delete;
    }

    /**
     * 修改数据
     *
     * @param clz       所要操作对象字节码
     * @param select    选择条目的条件
     * @param selectArg 选择条目的值
     * @param values    所要修改的键值对
     * @return 修改所影响的行数
     */
    public int update(Class<?> clz, String select, String[] selectArg, ContentValues values) {
        int update = 0;

        SQLiteDatabase writableDatabase = operHelper.getWritableDatabase();
        String tableName = "";
        try {
            tableName = clz.getName().substring(clz.getName().lastIndexOf(".") + 1) + endTableName;

            update += writableDatabase.update(tableName, values, select, selectArg);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (writableDatabase != null) {
                writableDatabase.close();
            }
        }
        LogData.i("AppDao", tableName + "表修改所影响了" + update + "条数据");

        // 通知内容变化
        if (update > 0) {
            DBProvider.getInstance().onChange(tableName);
        }

        return update;
    }

    /**
     * 删除所有数据
     *
     * @param clz
     * @return 操作所影响的行数
     */
    public int deleteAll(Class<?> clz) {
        return delete(clz, null, null);
    }

    /**
     * 根据class的字节码对象来反射获取到以set或get开头的公共方法集合
     *
     * @param clz
     * @param methodStartsWith 寻找以什么开头的方法(在这里也就是setXxx()或者getXxx()之类的方法)
     * @return
     */
    private List<Method> parseSetMethodList(Class<?> clz, String methodStartsWith) {
        Method[] methods = clz.getMethods();
        List<Method> list = new ArrayList<Method>();

        for (Method m : methods) {
            if (m.getName().startsWith(methodStartsWith)) {
                // 根据方法名获取到参数的名字
                list.add(m);
            }
        }
        return list;
    }
}

DBOperHelper:

package lc.com.car.base.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * 数据库操作类
 * 
 * @author lingfeng
 * 
 */
public class DBOperHelper extends SQLiteOpenHelper {

    public DBOperHelper(Context context, int version) {
        super(context, "hb.db", null, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
      // Student表  使用时删掉
        db.execSQL("CREATE TABLE "
                +"StudentBeanTable "
                +"(_ID INTEGER primary key autoincrement,name varchar(40),id varchar(40),age varchar(10))");
    }

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

    }
}

DBProvider:

package lc.com.car.base.db;


import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import lc.com.car.base.callback.OnRespanceListener;


/**
 * 数据库内容变化监听器
 * @author lingfeng
 *
 */
public class DBProvider {

    private static DBProvider dbProvider;

    public static DBProvider getInstance(){
        if(dbProvider == null){
            dbProvider = new DBProvider();
        }
        return dbProvider;
    }

    //数据库状态变化监听
    private HashMap<OnRespanceListener<String>,String> hashMap = new HashMap<OnRespanceListener<String>, String>();

    /**
     * 当tableName表中的数据发生改变时被appDao调用
     * @param tableName
     */
    public void onChange(String tableName){
        Set<OnRespanceListener<String>> keySet = hashMap.keySet();
        Iterator<OnRespanceListener<String>> iterator = keySet.iterator();

        while(iterator.hasNext()){
            OnRespanceListener<String> next = iterator.next();
            String value = hashMap.get(next);
            if(tableName.equals(value)){
                next.onRespance(tableName);
            }
        }
    }

    /**
     * 注册监听
     * @param tableName
     * @param onRespanceListener
     */
    public void addRegisterContentObserver(String tableName, OnRespanceListener<String> onRespanceListener){
        hashMap.put(onRespanceListener, tableName);
    }

    /**
     * 取消注册监听
     * @param
     * @param onRespanceListener
     */
    public void unRegisterContentObserver(OnRespanceListener<String> onRespanceListener){
        hashMap.remove(onRespanceListener);
    }
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一个简单的基于AndroidSqlite数据库的操作封装,它有如下的好处:便捷地创建和增添字段灵活的数据类型处理通过操作对象来insert或者update记录支持多种查询方式,支持多自定义的复杂查询,支持分页查询支持事务快速开始:    1. 设计:@Table(name="t_user") public class UserModel {     @Table.Column(name="user_id",type=Column.TYPE_INTEGER,isPrimaryKey=true)     public Integer userId;     @Table.Column(name="user_name",type=Column.TYPE_STRING,isNull=false)     public String userName;     @Table.Column(name="born_date",type=Column.TYPE_TIMESTAMP)     public Date bornDate;     @Table.Column(name="pictrue",type=Column.TYPE_BLOB)     public byte[] pictrue;     @Table.Column(name="is_login",type=Column.TYPE_BOOLEAN)     public Boolean isLogin;     @Table.Column(name="weight",type=Column.TYPE_DOUBLE)     public Double weight; }2. 初始化对象:SQLiteDatabase db = context.openOrCreateDatabase("test.db", Context.MODE_PRIVATE, null); DbSqlite dbSqlite = new DbSqlite(db); IBaseDao userDAO = DaoFactory.createGenericDao(dbSqlite, UserModel.class);3. 创建:userDAO.createTable(); 4. Insert 记录:UserModel user = new UserModel(); user.userName = "darcy"; user.isLogin = true; user.weight = 60.5; user.bornDate = new Date(); byte[] picture = {0x1,0x2,0x3,0x4}; user.pictrue = picture; userDAO.insert(user);5. Update 记录:UserModel user = new UserModel(); user.weight = 88.0; userDAO.update(user, "user_name=?", "darcy");6. 查询://单条结果查询 UserModel user = userDAO.queryFirstRecord("user_name=?", "darcy"); //一般查询 List userList = userDAO.query("user_name=? and weight > ?", "darcy" , "60"); //分页查询 PagingList pagingList = userDAO.pagingQuery(null, null, 1, 3);7. 事务支持:DBTransaction.transact(mDb, new DBTransaction.DBTransactionInterface() {         @Override         public void onTransact() {             // to do                 } };8. 更新(目前只支持添加字段)@Table(name="t_user" , version=2) //修改版本 public class UserModel {     //members above...     //new columns     @Table.Column(name="new_column_1",type=Column.TYPE_INTEGER)     public Integer newColumn;     @Table.Column(name="new_column_2",type=Column.TYPE_INTEGER)     public Integer newColumn2; } userDAO.updateTable();缺点和不足:还没支持多对一或者一多的关系没支持联合主键没支持的外键设计其他...实例:SqliteLookup(Android内查看Sqlite数据库利器): https://github.com/YeDaxia/SqliteLookup 标签:SQLiteUtils
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值